最后更新于:2026年7月

CI/CD 持续集成指南
CI/CD:让软件交付自动化

CI/CD 是现代软件开发的标配。 根据 2025 年 DORA 报告,实施成熟 CI/CD 的团队,部署频率提高 200 倍,变更失败率降低 7 倍,问题修复速度快 65 倍。

但很多人对 CI/CD 的理解还停留在「有个 Jenkins 服务器」。什么是持续集成?什么是持续部署?流水线怎么设计?GitHub Actions 怎么配置?蓝绿部署和金丝雀发布是什么?

本文从核心概念到生产级配置,带你全面掌握 CI/CD。


一、CI/CD 核心概念

1.1 什么是 CI/CD?

PLAINTEXT
CI(Continuous Integration)持续集成:
- 开发者频繁将代码合并到主干
- 每次合并自动触发构建和测试
- 快速发现问题,快速修复

CD(Continuous Delivery)持续交付:
- 代码随时可以部署到生产环境
- 自动化测试通过后自动部署到预发布环境
- 生产部署需要手动触发

CD(Continuous Deployment)持续部署:
- 代码自动部署到生产环境
- 全流程自动化,无需人工干预
- 要求极高的测试覆盖率和自动化水平

1.2 CI/CD 流水线

PLAINTEXT
完整 CI/CD 流水线:

代码提交 → 构建 → 测试 → 安全扫描 → 部署 → 监控
   │         │       │          │         │        │
   ▼         ▼       ▼          ▼         ▼        ▼
 Git Push  Compile  Unit Test  SAST     Deploy   Alerts
 PR Merge  Lint     Integration DAST     Release  Metrics
                   E2E Test   SCA       Rollback Logs

1.3 CI/CD 平台对比

平台托管方式配置语言特点推荐度
GitHub Actions云托管YAMLGitHub 深度集成,生态丰富⭐⭐⭐⭐⭐
GitLab CI云/自托管YAML功能全面,DevOps 一体化⭐⭐⭐⭐⭐
Jenkins自托管Groovy灵活强大,插件丰富⭐⭐⭐⭐
CircleCI云托管YAML简单易用,速度快⭐⭐⭐⭐
Bitbucket Pipelines云托管YAMLBitbucket 集成⭐⭐⭐
Drone自托管YAML轻量,容器化⭐⭐⭐⭐
Tekton云原生YAMLKubernetes 原生⭐⭐⭐

二、GitHub Actions 完全指南

2.1 基础概念

YAML
# GitHub Actions 核心概念

Workflow(工作流):
  - 一个自动化流程,定义在 .github/workflows/*.yml
  - 由一个或多个 Job 组成

Job(作业):
  - 工作流中的一组步骤,在同一个运行环境中执行
  - 可以并行执行,也可以串行执行

Step(步骤):
  - Job 中的具体任务
  - 执行 Shell 命令或调用 Action

Action(动作):
  - 可复用的单元
  - 可以是官方提供,也可以是第三方或自己编写

Runner(运行器):
  - 执行 Job 的服务器
  - GitHub 提供 ubuntu-latest, windows-latest, macos-latest
  - 也可以使用自托管 Runner

2.2 基础工作流

YAML
# .github/workflows/ci.yml

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [20, 22]
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'pnpm'
      
      - name: Install pnpm
        run: npm install -g pnpm
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Lint
        run: pnpm lint
      
      - name: Type check
        run: pnpm type-check
      
      - name: Test
        run: pnpm test -- --coverage
      
      - name: Build
        run: pnpm build
      
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

2.3 条件与矩阵

YAML
name: CI

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:  # 手动触发

jobs:
  test:
    runs-on: ubuntu-latest
    
    # 条件执行
    if: github.event_name == 'push' || github.event.pull_request.draft == false
    
    strategy:
      # 矩阵构建
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, macos-latest]
        exclude:
          - node: 18
            os: macos-latest
      fail-fast: false  # 一个失败不影响其他
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      
      - run: npm ci
      - run: npm test
    
    # 服务容器
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      
      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379

  deploy:
    needs: test  # 依赖 test job
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        run: echo "Deploying..."

2.4 Secrets 与环境变量

YAML
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    # 使用环境(可以有环境级 Secrets)
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /app
            docker-compose pull
            docker-compose up -d
      
      # 环境变量
      - name: Build
        run: npm run build
        env:
          NODE_ENV: production
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_KEY: ${{ secrets.API_KEY }}

2.5 Docker 镜像构建与推送

YAML
name: Docker Build

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

2.6 发布到 Kubernetes

YAML
name: Deploy to K8s

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up kubectl
        uses: azure/setup-kubectl@v3
      
      - name: Set up kubeconfig
        run: |
          mkdir -p ~/.kube
          echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config
      
      - name: Deploy
        run: |
          # 替换镜像版本
          sed -i "s|image: myapp:.*|image: myapp:${{ github.sha }}|" k8s/deployment.yaml
          
          # 应用配置
          kubectl apply -f k8s/
          
          # 等待滚动更新完成
          kubectl rollout status deployment/myapp -n production --timeout=300s
      
      - name: Rollback on failure
        if: failure()
        run: |
          kubectl rollout undo deployment/myapp -n production

三、GitLab CI/CD 完全指南

3.1 基础配置

YAML
# .gitlab-ci.yml

stages:
  - build
  - test
  - security
  - deploy

variables:
  NODE_VERSION: "20"
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

# 定义缓存
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .pnpm-store/

# 定义作业
build:
  stage: build
  image: node:${NODE_VERSION}-alpine
  before_script:
    - npm install -g pnpm
    - pnpm install --frozen-lockfile
  script:
    - pnpm build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour

test:
  stage: test
  image: node:${NODE_VERSION}-alpine
  before_script:
    - npm install -g pnpm
    - pnpm install --frozen-lockfile
  script:
    - pnpm lint
    - pnpm test -- --coverage
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

security_scan:
  stage: security
  image: aquasec/trivy:latest
  script:
    - trivy fs --exit-code 1 --severity HIGH,CRITICAL .

deploy_staging:
  stage: deploy
  image: alpine:latest
  environment:
    name: staging
    url: https://staging.example.com
  before_script:
    - apk add --no-cache openssh-client
  script:
    - ssh $SSH_USER@$STAGING_SERVER "cd /app && docker-compose pull && docker-compose up -d"
  only:
    - develop

deploy_production:
  stage: deploy
  image: alpine:latest
  environment:
    name: production
    url: https://example.com
  before_script:
    - apk add --no-cache openssh-client
  script:
    - ssh $SSH_USER@$PRODUCTION_SERVER "cd /app && docker-compose pull && docker-compose up -d"
  only:
    - main
  when: manual  # 手动触发

3.2 Docker 镜像构建

YAML
docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_DRIVER: overlay2
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE
  only:
    - main
    - tags

3.3 自动部署到 Kubernetes

YAML
deploy_k8s:
  stage: deploy
  image: bitnami/kubectl:latest
  script:
    - kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
    - kubectl config set-credentials admin --token="$KUBE_TOKEN"
    - kubectl config set-context default --cluster=k8s --user=admin
    - kubectl config use-context default
    
    # 替换变量
    - envsubst < k8s/deployment.yaml | kubectl apply -f -
    
    # 等待部署完成
    - kubectl rollout status deployment/myapp -n production --timeout=300s
  environment:
    name: production
  only:
    - main

四、Jenkins Pipeline

4.1 Declarative Pipeline

GROOVY
// Jenkinsfile

pipeline {
    agent any
    
    tools {
        nodejs 'NodeJS-20'
    }
    
    environment {
        DOCKER_IMAGE = 'myapp'
        DOCKER_TAG = "${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Install') {
            steps {
                sh 'npm ci'
            }
        }
        
        stage('Lint') {
            steps {
                sh 'npm run lint'
            }
        }
        
        stage('Test') {
            steps {
                sh 'npm test -- --coverage'
            }
            post {
                always {
                    junit 'coverage/junit.xml'
                    publishCoverage adapter: 'istanbul'
                }
            }
        }
        
        stage('Build') {
            steps {
                sh 'npm run build'
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    docker.withRegistry('https://registry.example.com', 'docker-credentials') {
                        def app = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        app.push()
                    }
                }
            }
        }
        
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh """
                    kubectl set image deployment/myapp \\
                        myapp=${DOCKER_IMAGE}:${DOCKER_TAG} \\
                        -n production
                """
            }
        }
    }
    
    post {
        success {
            slackSend(
                channel: '#deployments',
                color: 'good',
                message: "部署成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            )
        }
        failure {
            slackSend(
                channel: '#deployments',
                color: 'danger',
                message: "部署失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            )
        }
    }
}

4.2 Scripted Pipeline

GROOVY
// Jenkinsfile (Scripted Pipeline)

node {
    try {
        stage('Checkout') {
            checkout scm
        }
        
        stage('Build') {
            sh 'npm ci'
            sh 'npm run build'
        }
        
        stage('Test') {
            sh 'npm test'
        }
        
        stage('Deploy') {
            if (env.BRANCH_NAME == 'main') {
                sh './deploy.sh production'
            } else if (env.BRANCH_NAME == 'develop') {
                sh './deploy.sh staging'
            }
        }
        
    } catch (e) {
        currentBuild.result = 'FAILED'
        throw e
    } finally {
        // 清理
        deleteDir()
        
        // 通知
        if (currentBuild.result == 'FAILED') {
            emailext(
                subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "请检查构建日志: ${env.BUILD_URL}",
                to: 'team@example.com'
            )
        }
    }
}

五、部署策略

5.1 滚动更新(Rolling Update)

YAML
# Kubernetes 滚动更新配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 最多可以多 1 个 Pod
      maxUnavailable: 1  # 最多可以有 1 个 Pod 不可用
  template:
    spec:
      containers:
        - name: myapp
          image: myapp:v2
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
BASH
# 手动触发滚动更新
kubectl set image deployment/myapp myapp=myapp:v2 -n production

# 查看滚动更新状态
kubectl rollout status deployment/myapp -n production

# 回滚
kubectl rollout undo deployment/myapp -n production

# 查看历史版本
kubectl rollout history deployment/myapp -n production

5.2 蓝绿部署(Blue-Green Deployment)

PLAINTEXT
蓝绿部署架构:

当前版本(蓝)          新版本(绿)
    │                       │
┌───▼───┐               ┌───▼───┐
│ Pod 1 │               │ Pod 1 │
│ Pod 2 │               │ Pod 2 │
│ Pod 3 │               │ Pod 3 │
└───┬───┘               └───┬───┘
    │                       │
    └───────┬───────────────┘
            │
      ┌─────▼─────┐
      │  Service  │ ← 切换 Service 选择器
      │           │   从 blue 切换到 green
      └───────────┘
            │
      ┌─────▼─────┐
      │   用户    │
      └───────────┘
YAML
# Kubernetes 蓝绿部署示例

# 蓝色版本(当前)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: blue
  template:
    metadata:
      labels:
        app: myapp
        version: blue
    spec:
      containers:
        - name: myapp
          image: myapp:v1

---
# 绿色版本(新)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: green
  template:
    metadata:
      labels:
        app: myapp
        version: green
    spec:
      containers:
        - name: myapp
          image: myapp:v2

---
# Service(指向蓝色)
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
    version: blue  # 切换到 green 完成蓝绿部署
  ports:
    - port: 80
      targetPort: 3000
BASH
# 切换到绿色版本
kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'

# 验证
kubectl get endpoints myapp

5.3 金丝雀发布(Canary Deployment)

PLAINTEXT
金丝雀发布架构:

        用户流量
            │
            ▼
        ┌───────┐
        │Router/│
        │ Ingress│
        └───┬───┘
            │
      ┌─────┴─────┐
      │           │
   90%│           │10%
      ▼           ▼
┌──────────┐ ┌──────────┐
│ 稳定版本 │ │ 金丝雀  │
│  (v1)    │ │  (v2)   │
│ Pod x 9  │ │ Pod x 1 │
└──────────┘ └──────────┘
      │           │
      └─────┬─────┘
            ▼
        监控指标
   - 错误率
   - 响应时间
   - 业务指标
YAML
# Kubernetes 金丝雀发布
# 使用 Istio 或 Flagger 实现自动化

# Istio VirtualService 示例
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
    - myapp.example.com
  http:
    - route:
        - destination:
            host: myapp
            subset: stable
          weight: 90
        - destination:
            host: myapp
            subset: canary
          weight: 10
YAML
# Flagger 金丝雀配置
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  progressDeadlineSeconds: 600
  service:
    port: 3000
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        thresholdRange:
          max: 500
        interval: 1m

六、自动化测试集成

6.1 单元测试

YAML
# GitHub Actions 单元测试
- name: Run unit tests
  run: npm test -- --coverage --watchAll=false

- name: Upload coverage
  uses: codecov/codecov-action@v4
  with:
    token: ${{ secrets.CODECOV_TOKEN }}
    files: ./coverage/lcov.info
    fail_ci_if_error: true

6.2 端到端测试(E2E)

YAML
# Playwright E2E 测试
name: E2E Tests

on:
  push:
    branches: [main]
  pull_request:

jobs:
  e2e:
    runs-on: ubuntu-latest
    
    services:
      # 启动测试用的后端服务
      api:
        image: myapp-api:test
        ports:
          - 3000:3000
        env:
          NODE_ENV: test
          DATABASE_URL: postgresql://test:test@db:5432/test
      
      db:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: test
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      
      - name: Install Playwright
        run: |
          npm ci
          npx playwright install --with-deps
      
      - name: Run E2E tests
        run: npx playwright test
        env:
          BASE_URL: http://localhost:3000
      
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

6.3 性能测试

YAML
# k6 性能测试
name: Performance Tests

on:
  schedule:
    - cron: '0 2 * * *'  # 每天凌晨2点
  workflow_dispatch:

jobs:
  k6:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Run k6
        uses: grafana/k6-action@v0.3.0
        with:
          filename: tests/performance/script.js
        env:
          K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
      
      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: k6-results
          path: summary.json
JAVASCRIPT
// tests/performance/script.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 },   // 30秒内增加到 20 VUs
    { duration: '1m', target: 20 },    // 维持 1 分钟
    { duration: '30s', target: 0 },    // 30秒内减少到 0
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% 的请求 < 500ms
    http_req_failed: ['rate<0.01'],    // 错误率 < 1%
  },
};

export default function () {
  const res = http.get('https://example.com/api/users');
  
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  
  sleep(1);
}

七、安全扫描集成

7.1 SAST(静态应用安全测试)

YAML
# GitHub Actions - CodeQL
name: CodeQL

on:
  push:
    branches: [main]
  pull_request:
  schedule:
    - cron: '0 0 * * 1'  # 每周一

jobs:
  analyze:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: javascript, typescript
      
      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3

7.2 SCA(软件成分分析)

YAML
# Dependabot 自动依赖更新
# .github/dependabot.yml

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    open-pull-requests-limit: 10
    reviewers:
      - "team-lead"
    labels:
      - "dependencies"
      - "security"
    
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
YAML
# Trivy 漏洞扫描
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'myapp:latest'
    format: 'table'
    exit-code: '1'
    ignore-unfixed: true
    vuln-type: 'os,library'
    severity: 'CRITICAL,HIGH'

7.3 DAST(动态应用安全测试)

YAML
# OWASP ZAP 扫描
name: DAST

on:
  workflow_dispatch:

jobs:
  zap_scan:
    runs-on: ubuntu-latest
    name: ZAP Scan
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: ZAP Full Scan
        uses: zaproxy/action-full-scan@v0.7.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a'

八、通知与告警

8.1 Slack 通知

YAML
# GitHub Actions Slack 通知
- name: Notify Slack
  if: always()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    fields: repo,message,commit,author,action,eventName,ref,workflow
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

8.2 部署状态徽章

MARKDOWN
<!-- README.md -->
![CI](https://github.com/user/repo/actions/workflows/ci.yml/badge.svg)
![Deploy](https://github.com/user/repo/actions/workflows/deploy.yml/badge.svg?branch=main)
![Coverage](https://codecov.io/gh/user/repo/branch/main/graph/badge.svg)

九、最佳实践

9.1 CI/CD 流水线设计原则

PLAINTEXT
1. 快速反馈
   - 构建时间 < 10 分钟
   - 失败立即通知
   - 明确的错误信息

2. 失败安全
   - 任何步骤失败都要停止
   - 自动回滚机制
   - 保留日志和现场

3. 幂等性
   - 流水线可以多次运行
   - 不会因为重复运行出问题

4. 可追溯
   - 每次部署都有记录
   - 可以回溯到具体代码版本
   - 部署历史可查询

5. 渐进式发布
   - 先测试环境,后生产环境
   - 小流量验证,逐步放量
   - 监控指标驱动

6. 安全优先
   - Secrets 加密存储
   - 镜像签名验证
   - 安全扫描集成

9.2 流水线优化技巧

YAML
# 优化技巧

# 1. 缓存依赖
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'pnpm'  # 启用缓存

# 2. 并行执行
jobs:
  lint:
    ...
  test:
    ...
  build:
    ...
  # 以上三个 job 并行执行

# 3. 条件执行
- name: Deploy
  if: github.ref == 'refs/heads/main'

# 4. 使用矩阵减少配置
strategy:
  matrix:
    node: [18, 20, 22]

# 5. 复用工作流
# .github/workflows/deploy.yml 可被其他工作流调用
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string

9.3 Git 分支策略

PLAINTEXT
Git Flow(适合有明确发布周期的项目):

main        ────●──────────────●────────●──── (生产)
                 \              ↑
                  \            release
                   \            ↑
develop  ─────────●──●──●──●──●──●──●──── (开发)
                     \       /
feature/              ●──●──● (功能分支)

---

GitHub Flow(适合持续部署):

main     ────●──●──●──●──●──●──●──── (始终可部署)
              \  \  \  \  \  \
feature/       ●  ●  ●  ●  ●  ●── (通过 PR 合并)

---

Trunk-Based Development(适合高频部署):

main     ────●──●──●──●──●──●──●──●──── (主干,频繁集成)
             \ /    \  |  / \   /
short/        ●      ● ●    ● ●  (短生命周期分支)

十、故障排查

Q1:构建很慢怎么办?

YAML
# 优化方案:

# 1. 启用缓存
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

# 2. 使用更快的安装工具
- run: npm install -g pnpm
- run: pnpm install --frozen-lockfile

# 3. 减少不必要的步骤
# 只在 main 分支运行 lint
- name: Lint
  if: github.ref == 'refs/heads/main'
  run: pnpm lint

# 4. 拆分 Job 并行执行
jobs:
  lint: ...
  test: ...
  build: ...

# 5. 使用自托管 Runner(更快、更稳定)
runs-on: self-hosted

Q2:部署失败怎么回滚?

BASH
# Kubernetes 回滚
kubectl rollout undo deployment/myapp -n production

# 回滚到指定版本
kubectl rollout undo deployment/myapp --to-revision=2 -n production

# Docker Compose 回滚
docker-compose -f docker-compose.prev.yml up -d

Q3:如何实现零停机部署?

YAML
# 关键配置:

# 1. 健康检查
readinessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10

# 2. 优雅关闭
lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 10"]

# 3. 滚动更新策略
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 0

十一、总结

CI/CD 实施清单

学习路径

PLAINTEXT
入门(第1周):
  → CI/CD 概念理解
  → GitHub Actions 基础
  → 简单流水线配置

进阶(第2-3周):
  → 多环境部署
  → Docker 镜像构建
  → 测试集成

高级(第1-2月):
  → Kubernetes 部署
  → 部署策略
  → 安全扫描集成

专家(第3月+):
  → GitOps(ArgoCD)
  → 平台工程
  → 企业级实践


CI/CD 是现代软件交付的基石。 掌握 CI/CD,让代码从提交到生产全流程自动化,告别手工部署的混乱与风险。🚀

版权声明

作者: 易邦

链接: https://blog.e8k.net/posts/ci-cd-pipeline-guide-2026/

许可证: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。