跳到主要内容

CI/CD详解

介绍

CI/CD(持续集成/持续部署)是现代软件开发中的关键实践,它通过自动化构建、测试和部署流程,提高开发效率、减少错误并加快产品交付速度。对于前端开发来说,CI/CD尤为重要,因为前端技术更新快、依赖管理复杂,自动化流程可以有效减少人为错误。本章将详细介绍前端CI/CD的核心概念、主流工具、实现方法和最佳解决方案。

核心概念与原理

CI/CD的目标

  1. 自动化:减少手动操作,降低人为错误
  2. 快速反馈:及时发现和解决问题
  3. 持续集成:频繁合并代码,避免集成地狱
  4. 持续部署:自动化部署到生产环境
  5. 可靠性:确保每次部署都是可靠的
  6. 可追溯性:跟踪代码变更和部署历史

CI/CD流水线的阶段

  1. 代码提交:开发者提交代码到版本控制系统
  2. 构建:自动化构建项目,生成可部署的 artifacts
  3. 测试:运行自动化测试,确保代码质量
  4. 部署:将构建产物部署到目标环境
  5. 监控:监控应用性能和错误

持续集成 vs 持续部署 vs 持续交付

  • 持续集成(CI):频繁合并代码到主分支,每次合并都运行构建和测试
  • 持续交付(CD):确保代码可以随时部署到生产环境,但部署决策由人工控制
  • 持续部署(CD):代码通过测试后自动部署到生产环境
特性持续集成 (CI)持续交付 (CD)持续部署 (CD)
自动化程度中等非常高
部署频率不涉及部署按需部署每次变更自动部署
人工干预代码审查部署决策最小化或无
风险级别高(需要完善的自动化测试)
适用场景所有项目大多数项目成熟的项目和团队

主流CI/CD工具对比

工具类型特点适用场景配置复杂度生态系统社区活跃度价格模型
GitHub Actions云服务与GitHub深度集成、易于使用、免费额度、市场丰富开源项目、GitHub用户、前端项目丰富非常活跃免费起步,按使用量计费
GitLab CI/CD云服务/自托管与GitLab集成、功能全面、可定制性高、内置容器注册表GitLab用户、DevOps团队丰富活跃免费起步,高级功能付费
Jenkins自托管高度可定制、插件丰富、成熟稳定、支持分布式构建企业级应用、复杂流水线、遗留系统集成非常丰富非常活跃开源免费,维护成本高
CircleCI云服务性能优秀、配置简洁、支持多种语言、并行构建各类项目、敏捷团队丰富活跃免费起步,按使用量计费
Travis CI云服务历史悠久、配置简单、对开源友好、多环境测试开源项目、简单项目中等中等开源项目免费,商业项目付费
Azure DevOps云服务微软生态、功能全面、企业级支持、项目管理集成微软技术栈用户、企业级项目丰富活跃免费起步,按用户和功能付费
Drone CI云服务/自托管基于Docker、轻量级、配置简单、可扩展容器化项目、小型团队中等中等开源免费,企业版付费
TeamCity自托管/云服务JetBrains产品、智能构建功能、历史构建分析企业级项目、.NET项目丰富中等专业版付费,小型项目免费
Bamboo自托管Atlassian产品、与Jira集成、部署项目集成Atlassian生态用户中等中等付费
Bitbucket Pipelines云服务与Bitbucket集成、简单易用、基于DockerBitbucket用户、小型团队中等中等免费起步,按使用量计费

详细使用指南

GitHub Actions

GitHub Actions是GitHub提供的CI/CD服务,与GitHub仓库深度集成,易于配置和使用。

基本配置

在仓库中创建.github/workflows目录,并添加YAML配置文件。

工作流示例 (node.js.yml)

# 工作流名称:Node.js CI
name: Node.js CI

# 触发条件配置
on:
# 当代码推送到main或develop分支时触发
push:
branches: [ main, develop ]
# 当有PR提交到main分支时触发
pull_request:
branches: [ main ]
# 定时触发 - 每天凌晨2点运行(用于定期检查)
schedule:
- cron: '0 2 * * *'

# 环境变量定义
env:
NODE_VERSION: '18.x' # 指定Node.js版本
CACHE_NAME: cache-node-modules # 依赖缓存名称

# 作业定义
jobs:
# 作业1: 代码质量检查
lint:
runs-on: ubuntu-latest # 使用最新版Ubuntu运行器
steps:
# 步骤1: 检出代码
- uses: actions/checkout@v4
# 步骤2: 设置Node.js环境
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }} # 使用环境变量中定义的Node.js版本
cache: 'npm' # 启用npm依赖缓存以加快构建速度
# 步骤3: 安装依赖(使用ci命令确保依赖版本一致性)
- run: npm ci
# 步骤4: 运行代码 lint 检查
- run: npm run lint
# 步骤5: 运行类型检查
- run: npm run type-check

# 作业2: 构建和测试
build-and-test:
runs-on: ubuntu-latest # 使用最新版Ubuntu运行器
needs: lint
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build project
run: npm run build

- name: Run tests
run: |
npm test -- --coverage
npm run test:e2e

- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella

- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files-${{ matrix.node-version }}
path: |
dist/
build/
retention-days: 30

部署示例 (deploy.yml)

# 工作流名称:部署到生产环境
name: Deploy to Production

# 触发条件配置
on:
# 当代码推送到main分支时触发
push:
branches: [ main ]
# 当创建版本标签时触发 (如v1.0.0)
tags:
- 'v*.*.*'
# 允许手动触发工作流
workflow_dispatch:
inputs:
environment:
description: '部署环境选择'
required: true
default: 'staging'
type: choice
options:
- staging
- production

env:
NODE_VERSION: '18.x' # 指定Node.js版本

jobs:
# 作业1: 构建阶段
build:
runs-on: ubuntu-latest # 使用最新版Ubuntu运行器
outputs:
version: ${{ steps.version.outputs.version }} # 输出版本号供其他作业使用
steps:
# 步骤1: 检出代码 (fetch-depth: 0 表示获取完整历史)
- uses: actions/checkout@v4
with:
fetch-depth: 0

# 步骤2: 设置Node.js环境
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }} # 使用环境变量中定义的Node.js版本
cache: 'npm' # 启用npm依赖缓存

# 步骤3: 安装依赖
- name: Install dependencies
run: npm ci # 使用ci命令确保依赖版本一致性

# 步骤4: 运行测试
- name: Run tests
run: npm test # 运行项目测试

# 步骤5: 构建项目
- name: Build project
run: |
npm run build # 构建项目
npm run build:analyze # 运行构建分析

# 步骤6: 获取版本号
- name: Get version
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

# 步骤7: 上传构建产物
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts # 构建产物名称
path: |
dist/
build/ # 要上传的文件路径
retention-days: 30 # 构建产物保留30天

# 作业2: 部署到Staging环境
deploy-staging:
runs-on: ubuntu-latest # 使用最新版Ubuntu运行器
needs: build # 依赖于build作业完成
# 触发条件: 主分支推送或手动选择staging环境
if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'staging'
environment:
name: staging # 环境名称
url: https://staging.example.com # 环境URL
steps:
# 步骤1: 下载构建产物
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts # 下载之前上传的构建产物

# 步骤2: 部署到Staging环境
- name: Deploy to Staging
run: |
echo "Deploying version ${{ needs.build.outputs.version }} to staging..."
# 部署到S3存储桶
aws s3 sync ./dist s3://${{ secrets.STAGING_S3_BUCKET }} --delete
# 清除CloudFront缓存
aws cloudfront create-invalidation --distribution-id ${{ secrets.STAGING_CLOUDFRONT_ID }} --paths "/*"
env:
# 从GitHub Secrets中获取AWS凭证
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1

# 作业3: 部署到Production环境
deploy-production:
runs-on: ubuntu-latest # 使用最新版Ubuntu运行器
needs: [build, deploy-staging] # 依赖于build和deploy-staging作业完成
# 触发条件: 版本标签推送或手动选择production环境
if: startsWith(github.ref, 'refs/tags/v') || github.event.inputs.environment == 'production'
environment:
name: production # 环境名称
url: https://example.com # 环境URL
steps:
# 步骤1: 下载构建产物
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts # 下载之前上传的构建产物

# 步骤2: 部署到Production环境
- name: Deploy to Production
run: |
echo "Deploying version ${{ needs.build.outputs.version }} to production..."
# 部署到S3存储桶
aws s3 sync ./dist s3://${{ secrets.PRODUCTION_S3_BUCKET }} --delete
# 清除CloudFront缓存
aws cloudfront create-invalidation --distribution-id ${{ secrets.PRODUCTION_CLOUDFRONT_ID }} --paths "/*"
env:
# 从GitHub Secrets中获取AWS凭证
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1

# 步骤3: 创建GitHub Release
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 从GitHub Secrets中获取token
with:
tag_name: ${{ github.ref }} # 标签名称
release_name: Release ${{ needs.build.outputs.version }} # 发布名称
body: |
## 本次发布变更
- 从GitHub Actions自动发布
- 版本: ${{ needs.build.outputs.version }}
draft: false # 不是草稿
prerelease: false # 不是预发布

# 步骤4: 通知Slack
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }} # 作业状态
channel: '#deployments' # Slack频道
webhook_url: ${{ secrets.SLACK_WEBHOOK }} # Slack Webhook URL
if: always() # 无论作业成功失败都通知

关键概念

核心概念详解:

  • Workflow(工作流):整个CI/CD流程的定义,存储在.github/workflows/目录中的YAML文件

    • 可以有多个工作流文件
    • 每个工作流可以独立触发和运行
    • 支持复杂的触发条件和调度
  • Job(作业):工作流中的一个任务,由多个步骤组成

    • 默认情况下并行运行
    • 可以通过needs关键字设置依赖关系
    • 每个作业在独立的虚拟环境中运行
  • Step(步骤):作业中的一个操作步骤

    • 可以是预定义的Action或自定义脚本
    • 按顺序执行
    • 可以共享环境变量和文件系统
  • Action(动作):可重用的步骤,由GitHub或社区提供

    • 官方Actions:actions/checkoutactions/setup-node
    • 社区Actions:数千个可用的Actions
    • 自定义Actions:可以创建自己的Actions
  • Runner(运行器):执行工作流的服务器

    • GitHub托管的Runner:免费额度,多种操作系统
    • 自托管Runner:更多控制权,可访问内部资源
  • Secret(密钥):存储敏感信息的环境变量

    • 仓库级:只能在当前仓库使用
    • 环境级:只能在特定环境使用
    • 组织级:可在组织内多个仓库使用
  • Environment(环境):部署目标环境的抽象

    • 可以设置保护规则
    • 支持审批流程
    • 可以配置环境特定的密钥和变量
  • Artifact(构件):作业生成的文件

    • 可以在作业间传递
    • 支持下载和长期存储
    • 自动压缩和解压缩

GitLab CI/CD

GitLab CI/CD是GitLab内置的CI/CD服务,与GitLab仓库深度集成,功能全面。

基本配置

在仓库根目录创建.gitlab-ci.yml文件。

配置示例 (.gitlab-ci.yml)

# 定义流水线阶段
stages:
- build
- test
- security
- deploy
- notify

# 全局变量
variables:
NODE_VERSION: '18'
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
FF_USE_FASTZIP: "true"
ARTIFACT_COMPRESSION_LEVEL: "fast"
CACHE_COMPRESSION_LEVEL: "fast"

# 全局缓存配置
default:
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
policy: pull-push

# 构建阶段
build:
stage: build
image: node:$NODE_VERSION-alpine
before_script:
- npm config set cache .npm
- npm ci --cache .npm --prefer-offline
script:
- npm run build
- npm run build:analyze
artifacts:
name: "build-$CI_COMMIT_SHORT_SHA"
paths:
- dist/
- build/
- coverage/
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 1 week
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# 测试阶段
unit-test:
stage: test
image: node:$NODE_VERSION-alpine
script:
- npm ci --cache .npm --prefer-offline
- npm run test:unit -- --coverage
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
paths:
- coverage/
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'

e2e-test:
stage: test
image: cypress/browsers:node18.12.0-chrome107
services:
- name: selenium/standalone-chrome:latest
alias: chrome
script:
- npm ci --cache .npm --prefer-offline
- npm run build
- npm run start:test &
- npm run test:e2e
artifacts:
when: always
paths:
- cypress/screenshots/
- cypress/videos/
expire_in: 1 week
allow_failure: true

# 安全扫描阶段
dependency-scanning:
stage: security
image: node:$NODE_VERSION-alpine
script:
- npm audit --audit-level high
- npm run security:check
allow_failure: true

sast:
stage: security
image: returntocorp/semgrep
script:
- semgrep --config=auto --json --output=sast-report.json .
artifacts:
reports:
sast: sast-report.json
allow_failure: true

# 部署到Staging
deploy-staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
- apk add --no-cache aws-cli
script:
- echo "Deploying to staging environment..."
- aws s3 sync dist/ s3://$STAGING_S3_BUCKET --delete
- aws cloudfront create-invalidation --distribution-id $STAGING_CLOUDFRONT_ID --paths "/*"
environment:
name: staging
url: https://staging.example.com
deployment_tier: staging
needs:
- job: build
artifacts: true
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# 部署到Production
deploy-production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
- apk add --no-cache aws-cli
script:
- echo "Deploying to production environment..."
- aws s3 sync dist/ s3://$PRODUCTION_S3_BUCKET --delete
- aws cloudfront create-invalidation --distribution-id $PRODUCTION_CLOUDFRONT_ID --paths "/*"
environment:
name: production
url: https://example.com
deployment_tier: production
needs:
- job: build
artifacts: true
- job: deploy-staging
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
when: manual

# 通知阶段
notify-success:
stage: notify
image: alpine:latest
script:
- echo "Pipeline completed successfully!"
- 'curl -X POST -H "Content-type: application/json" --data "{\"text\":\"✅ Pipeline for $CI_PROJECT_NAME completed successfully!\"}" $SLACK_WEBHOOK_URL'
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: on_success

notify-failure:
stage: notify
image: alpine:latest
script:
- echo "Pipeline failed!"
- 'curl -X POST -H "Content-type: application/json" --data "{\"text\":\"❌ Pipeline for $CI_PROJECT_NAME failed!\"}" $SLACK_WEBHOOK_URL'
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: on_failure

关键概念

核心概念详解:

  • Pipeline(流水线):一次完整的CI/CD执行过程

    • 由一个或多个Stage组成
    • 可以通过代码推送、合并请求、定时任务等方式触发
    • 支持复杂的条件执行和依赖关系
  • Stage(阶段):流水线的逻辑分组,按顺序执行

    • 常见阶段:build、test、deploy
    • 同一阶段内的Job并行执行
    • 前一阶段失败会阻止后续阶段执行
  • Job(作业):Stage中的具体任务

    • 在独立的环境中执行
    • 可以指定运行环境(Docker镜像、Runner等)
    • 支持条件执行、依赖关系、重试机制
  • Script(脚本):Job中执行的具体命令

    • before_script:主脚本执行前的准备工作
    • script:主要的执行脚本
    • after_script:清理工作,总是执行
  • Artifact(构件):Job生成的文件或目录

    • 可以在后续Job中使用
    • 支持过期时间设置
    • 可以生成测试报告、覆盖率报告等
  • Cache(缓存):在Job之间共享的文件或目录

    • 提高构建速度,减少重复下载
    • 支持不同的缓存策略(pull、push、pull-push)
    • 可以设置缓存键值,实现精确控制
  • Runner(运行器):执行Job的服务器

    • Shared Runner:GitLab.com提供的共享运行器
    • Group Runner:组级别的运行器
    • Project Runner:项目专用的运行器
    • 自托管Runner:自己部署和管理的运行器
  • Environment(环境):部署目标的抽象

    • 可以设置环境URL
    • 支持部署历史跟踪
    • 可以配置环境保护规则
  • Variables(变量):配置信息的存储

    • 预定义变量:GitLab自动提供的变量
    • 自定义变量:用户定义的变量
    • 受保护变量:只在受保护分支/标签中可用
    • 环境变量:特定环境中的变量
  • Rules(规则):控制Job执行的条件

    • 替代传统的only/except语法
    • 支持复杂的条件逻辑
    • 可以动态设置Job的行为

Jenkins

Jenkins是一个开源的CI/CD工具,高度可定制,插件丰富,适用于复杂的CI/CD场景。

安装与配置

  1. 下载安装Jenkins

    # macOS (使用Homebrew)
    brew install jenkins-lts

    # Ubuntu/Debian
    wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
    sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
    sudo apt update
    sudo apt install jenkins

    # Docker
    docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
  2. 初始化配置

    • 访问 http://localhost:8080
    • 使用初始管理员密码解锁Jenkins
    • 安装推荐插件或自定义插件选择
    • 创建管理员用户
  3. 必要插件安装

    • NodeJS Plugin
    • Git Plugin
    • Pipeline Plugin
    • Blue Ocean Plugin
    • Docker Pipeline Plugin
    • Slack Notification Plugin

Pipeline示例 (Jenkinsfile)

pipeline {
agent {
label 'nodejs'
}

environment {
NODE_VERSION = '18'
DOCKER_REGISTRY = 'your-registry.com'
APP_NAME = 'frontend-app'
SLACK_CHANNEL = '#ci-cd'
}

options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
skipStagesAfterUnstable()
parallelsAlwaysFailFast()
}

triggers {
pollSCM('H/5 * * * *')
cron('H 2 * * *')
}

parameters {
choice(
name: 'DEPLOY_ENV',
choices: ['staging', 'production'],
description: 'Select deployment environment'
)
booleanParam(
name: 'SKIP_TESTS',
defaultValue: false,
description: 'Skip test execution'
)
}

stages {
stage('Checkout') {
steps {
checkout scm
script {
env.GIT_COMMIT_SHORT = sh(
script: 'git rev-parse --short HEAD',
returnStdout: true
).trim()
env.BUILD_VERSION = "${env.BUILD_NUMBER}-${env.GIT_COMMIT_SHORT}"
}
}
}

stage('Install Dependencies') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh '''
npm config set cache .npm
npm ci --prefer-offline
'''
}
}
}

stage('Code Quality') {
parallel {
stage('Lint') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh 'npm run lint'
}
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'reports',
reportFiles: 'eslint.html',
reportName: 'ESLint Report'
])
}
}
}

stage('Type Check') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh 'npm run type-check'
}
}
}

stage('Security Scan') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh 'npm audit --audit-level high'
sh 'npx snyk test --severity-threshold=high'
}
}
}
}
}

stage('Build') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh '''
npm run build
npm run build:analyze
'''
}
stash includes: 'dist/**', name: 'build-artifacts'
archiveArtifacts artifacts: 'dist/**', fingerprint: true
}
}

stage('Test') {
when {
not { params.SKIP_TESTS }
}
parallel {
stage('Unit Tests') {
steps {
nodejs(nodeJSInstallationName: "Node.js ${NODE_VERSION}") {
sh 'npm run test:unit -- --coverage --reporter=xunit --outputFile=test-results.xml'
}
}
post {
always {
junit 'test-results.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}

stage('E2E Tests') {
agent {
docker {
image 'cypress/browsers:node18.12.0-chrome107'
args '-v /dev/shm:/dev/shm'
}
}
steps {
unstash 'build-artifacts'
sh '''
npm ci
npm run start:test &
npx wait-on http://localhost:3000
npm run test:e2e
'''
}
post {
always {
archiveArtifacts artifacts: 'cypress/screenshots/**,cypress/videos/**', allowEmptyArchive: true
}
}
}
}
}

stage('Docker Build') {
steps {
script {
def image = docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_VERSION}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
image.push()
image.push('latest')
}
}
}
}

stage('Deploy') {
when {
anyOf {
branch 'main'
expression { params.DEPLOY_ENV != null }
}
}
steps {
script {
def deployEnv = params.DEPLOY_ENV ?: 'staging'

unstash 'build-artifacts'

if (deployEnv == 'production') {
input message: 'Deploy to production?', ok: 'Deploy',
submitterParameter: 'DEPLOYER'
}

sh '''
echo "Deploying to ${deployEnv} environment..."
aws s3 sync dist/ s3://my-app-${deployEnv} --delete
aws cloudfront create-invalidation --distribution-id ${deployEnv.toUpperCase()}_CLOUDFRONT_ID --paths "/*"
'''

// 健康检查
sh '''
sleep 30
curl -f https://${deployEnv}.example.com/health || exit 1
'''
}
}
}
}

post {
always {
cleanWs()
}
success {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'good',
message: "✅ Pipeline succeeded for ${env.JOB_NAME} - ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
)
}
failure {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'danger',
message: "❌ Pipeline failed for ${env.JOB_NAME} - ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
)
}
unstable {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'warning',
message: "⚠️ Pipeline unstable for ${env.JOB_NAME} - ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
)
}
}
}

关键概念

核心概念详解:

  1. Pipeline(流水线)

    • 声明式Pipeline: 使用预定义的结构,更易读和维护
    • 脚本式Pipeline: 基于Groovy脚本,更灵活但复杂
    • 共享库: 可重用的Pipeline代码,支持跨项目共享
    • 多分支Pipeline: 自动为每个分支创建Pipeline
  2. Agent(代理节点)

    • Master节点: 负责调度、管理和提供Web界面
    • Slave节点: 执行具体的构建任务
    • Docker Agent: 在Docker容器中执行构建
    • Kubernetes Agent: 动态创建Pod执行构建
  3. Stage(阶段)

    • 将Pipeline分解为逻辑阶段
    • 支持并行执行多个阶段
    • 可以设置阶段执行条件
    • 提供可视化的执行进度
  4. Step(步骤)

    • 阶段内的具体执行单元
    • 包括shell命令、插件调用等
    • 支持条件执行和错误处理
    • 可以跨阶段传递数据
  5. Workspace(工作空间)

    • 每个构建的独立工作目录
    • 包含源代码和构建产物
    • 支持工作空间清理和归档
    • 可以在节点间传递工作空间
  6. Artifact(构建产物)

    • 构建过程中生成的文件
    • 支持长期存储和版本管理
    • 可以在不同Job间传递
    • 支持指纹识别和依赖追踪
  7. Plugin(插件)

    • 扩展Jenkins核心功能
    • 支持第三方工具集成
    • 提供丰富的生态系统
    • 支持自定义插件开发

高级特性

多环境部署

根据分支或标签自动部署到不同环境。

# GitHub Actions多环境部署示例
name: Multi-environment Deploy

on:
push:
branches:
- main
- develop
tags:
- 'v*.*.*'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: '16'
- run: npm ci
- run: npm run build
- name: Deploy to Production
if: startsWith(github.ref, 'refs/tags/v')
run: |
echo "Deploying to production..."
# 生产环境部署命令
- name: Deploy to Staging
if: github.ref == 'refs/heads/main'
run: |
echo "Deploying to staging..."
# 预发布环境部署命令
- name: Deploy to Development
if: github.ref == 'refs/heads/develop'
run: |
echo "Deploying to development..."
# 开发环境部署命令

金丝雀发布

逐步将新版本部署给部分用户,降低风险。

# GitLab CI/CD金丝雀发布示例
stages:
- build
- test
- deploy_canary
- monitor
- deploy_production

# ... 省略build和test阶段

deploy_canary:
stage: deploy_canary
script:
- echo "Deploying canary version..."
- # 部署5%的流量到新版本
only:
- main

monitor:
stage: monitor
script:
- echo "Monitoring canary deployment..."
- # 监控错误率、性能等指标
- # 如果指标正常,继续部署;否则回滚
needs:
- job: deploy_canary

# ... 省略deploy_production阶段

自动回滚

当监控发现问题时,自动回滚到上一个稳定版本。

# GitHub Actions自动回滚示例
name: Deploy with Rollback

on:
push:
branches: [ main ]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 16.x
uses: actions/setup-node@v3
with:
node-version: '16'
- run: npm ci
- run: npm run build
- name: Deploy to Production
id: deploy
run: |
echo "Deploying to production..."
# 部署命令
echo "::set-output name=deploy_id::$(uuidgen)"
- name: Monitor Application
id: monitor
run: |
echo "Monitoring application..."
# 监控命令
# 如果发现问题,设置status为failure
echo "::set-output name=status::success"
- name: Rollback if Needed
if: steps.monitor.outputs.status == 'failure'
run: |
echo "Rolling back deployment..."
# 回滚命令

最佳实践

  1. 保持流水线简洁:避免过于复杂的流水线,提高可维护性
  2. 自动化一切:尽可能自动化构建、测试、部署流程
  3. 快速反馈:确保流水线快速执行,及时反馈问题
  4. 版本控制:将CI/CD配置文件纳入版本控制
  5. 基础设施即代码:使用代码定义CI/CD流程和环境配置
  6. 安全第一:保护敏感信息,避免在配置文件中硬编码密钥
  7. 测试驱动:在部署前运行充分的测试
  8. 渐进式部署:使用金丝雀发布或蓝绿部署降低风险
  9. 监控与告警:部署后监控应用性能和错误
  10. 持续改进:定期回顾和优化CI/CD流程

实际案例分析

案例1:大型科技公司的CI/CD实践

某大型科技公司采用Jenkins构建复杂的CI/CD流水线,支持多环境部署、自动回滚和金丝雀发布。通过CI/CD,该公司的部署频率从每周一次提高到每天多次,上线时间从几小时缩短到几分钟。

案例2:开源项目的GitHub Actions实践

一个流行的开源React组件库使用GitHub Actions进行CI/CD,每次提交都运行测试和构建,合并到主分支后自动部署到npm和GitHub Pages。通过自动化流程,维护者可以专注于代码开发,而不必担心部署问题。

总结

CI/CD是现代前端开发的关键实践,通过自动化构建、测试和部署流程,可以提高开发效率、减少错误并加快产品交付速度。GitHub Actions、GitLab CI/CD和Jenkins是主流的CI/CD工具,各有其适用场景。多环境部署、金丝雀发布和自动回滚是高级CI/CD特性,可以进一步提高部署的可靠性和安全性。遵循CI/CD最佳实践,持续改进流程,是现代前端团队成功的关键。