CI/CD 管道最佳实践:自动化实现可靠部署

利用生产工作流程中的测试、暂存、部署自动化、回滚策略和安全扫描的最佳实践构建可靠的 CI/CD 管道。

E
ECOSIRE Research and Development Team
|2026年3月16日4 分钟阅读774 字数|

CI/CD 管道最佳实践:自动化实现可靠部署

拥有成熟 CI/CD 管道的团队的部署频率是没有成熟 CI/CD 管道的团队的 208 倍,同时变更失败率降低了 7 倍。 脆弱的“大部分有效”管道与经过实战考验的部署系统之间的区别归结为将业余自动化与生产级基础设施分开的一些实践。

本指南涵盖了使 CI/CD 管道大规模可靠的具体实践、配置和架构决策。

要点

  • 管道执行时间直接影响开发人员的工作效率 --- 全套套件的目标是在 10 分钟以内
  • CI 中的安全扫描可在漏洞到达生产环境之前捕获 85%
  • 自动回滚机制将平均恢复时间从几小时缩短到几分钟
  • 分支保护规则和所需的状态检查可防止损坏的代码到达主干

管道架构

五阶段模型

每个生产 CI/CD 管道应实施五个阶段:

第 1 阶段:Lint 和验证(目标:<2 分钟)

lint:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: pnpm
    - run: pnpm install --frozen-lockfile
    - run: pnpm lint
    - run: pnpm typecheck

第 2 阶段:测试(目标:<8 分钟)

test:
  runs-on: ubuntu-latest
  services:
    postgres:
      image: postgres:17
      env:
        POSTGRES_PASSWORD: test
        POSTGRES_DB: test
      ports:
        - 5432:5432
      options: >-
        --health-cmd pg_isready
        --health-interval 10s
        --health-timeout 5s
        --health-retries 5
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: pnpm
    - run: pnpm install --frozen-lockfile
    - run: pnpm test
      env:
        DATABASE_URL: postgresql://postgres:test@localhost:5432/test

第 3 阶段:构建(目标:<5 分钟)

构建 Docker 镜像、编译资产、生成生产包。积极缓存依赖项。

第 4 阶段:部署到暂存

合并到主程序时自动部署。针对暂存环境运行冒烟测试。

第 5 阶段:部署到生产环境

手动审批门或在分阶段验证通过后自动审批。


速度优化

缓慢的管道会降低开发人员的生产力。团队中每增加一分钟的 CI 等待时间都会造成数小时的上下文切换时间损失。

并行化

同时运行独立作业:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps: [...]

  test-unit:
    runs-on: ubuntu-latest
    steps: [...]

  test-integration:
    runs-on: ubuntu-latest
    steps: [...]

  test-e2e:
    runs-on: ubuntu-latest
    steps: [...]

  build:
    needs: [lint, test-unit, test-integration, test-e2e]
    runs-on: ubuntu-latest
    steps: [...]

依赖缓存

- uses: actions/cache@v4
  with:
    path: |
      ~/.pnpm-store
      node_modules
      apps/*/node_modules
      packages/*/node_modules
    key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
    restore-keys: |
      ${{ runner.os }}-pnpm-

Docker 层缓存

- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: registry.example.com/app:${{ github.sha }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

管道速度基准

优化之前之后改进
没有缓存12 分钟---基线
依赖项缓存12 分钟7 分钟42%
Docker 层缓存7 分钟4.5 分钟36%
并行测试套件4.5 分钟3 分钟33%
Turbo 远程缓存3 分钟2 分钟33%

安全扫描

依赖漏洞扫描

security:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4

    - name: Run Snyk to check for vulnerabilities
      uses: snyk/actions/node@master
      env:
        SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
      with:
        args: --severity-threshold=high

    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: fs
        scan-ref: .
        severity: CRITICAL,HIGH
        exit-code: 1

秘密扫描

    - name: Detect secrets
      uses: trufflesecurity/trufflehog@main
      with:
        extra_args: --only-verified

SAST(静态应用程序安全测试)

    - name: CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        languages: javascript-typescript

安全门政策

查找严重性公关行为生产行为
关键块合并区块部署
块合并区块部署
中等警告,允许合并警告,允许部署
仅供参考仅供参考

分支保护和合并策略

所需的状态检查

将这些配置为主分支上所需的状态检查:

  1. Lint 和类型检查必须通过 2.所有单元测试必须通过 3.所有集成测试必须通过
  2. 安全扫描必须没有严重/严重的发现
  3. 构建必须成功

合并策略

对功能分支使用压缩合并来维护干净的历史记录:

main: A --- B --- C --- D (each is a squashed feature)

需要至少一项 PR 批准。对于关键路径(身份验证、计费、数据库迁移),需要两次批准。


部署策略

蓝绿部署

维护两个相同的生产环境。将流量路由到一个,同时部署到另一个。

#!/bin/bash
# blue-green-deploy.sh

CURRENT=$(kubectl get service production -o jsonpath='{.spec.selector.version}')

if [ "$CURRENT" == "blue" ]; then
  TARGET="green"
else
  TARGET="blue"
fi

echo "Current: $CURRENT, deploying to: $TARGET"

# Deploy to inactive environment
kubectl set image deployment/$TARGET-app app=registry.example.com/app:$TAG

# Wait for rollout
kubectl rollout status deployment/$TARGET-app --timeout=300s

# Run smoke tests against target
curl -sf "http://$TARGET.internal/health" || exit 1

# Switch traffic
kubectl patch service production -p "{\"spec\":{\"selector\":{\"version\":\"$TARGET\"}}}"

echo "Traffic switched to $TARGET"

滚动部署

增量更新 Pod:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 0

maxUnavailable: 0 确保部署期间不会出现容量损失。

金丝雀部署

将一小部分流量路由到新版本:

# Using Istio for traffic splitting
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-canary
spec:
  hosts:
    - app.example.com
  http:
    - route:
        - destination:
            host: app-stable
          weight: 95
        - destination:
            host: app-canary
          weight: 5

有关更多部署策略,请参阅我们的零停机部署 专用指南。


回滚自动化

健康检查失败自动回滚

deploy-production:
  runs-on: ubuntu-latest
  steps:
    - name: Deploy
      run: |
        kubectl set image deployment/app app=${{ env.IMAGE }}
        kubectl rollout status deployment/app --timeout=300s

    - name: Smoke tests
      run: |
        sleep 30
        STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://app.example.com/health)
        if [ "$STATUS" != "200" ]; then
          echo "Health check failed with status $STATUS"
          kubectl rollout undo deployment/app
          exit 1
        fi

    - name: Monitor error rate
      run: |
        # Check error rate over 5 minutes
        ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[5m])/rate(http_requests_total[5m])" | jq '.data.result[0].value[1]' -r)
        if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
          echo "Error rate $ERROR_RATE exceeds threshold"
          kubectl rollout undo deployment/app
          exit 1
        fi

Monorepo 管道优化

对于 monorepo 项目(例如使用 Turborepo 的项目),仅运行更改的内容:

- name: Determine affected packages
  id: affected
  run: |
    AFFECTED=$(npx turbo run build --filter='...[HEAD~1]' --dry-run=json | jq -r '.packages[]')
    echo "packages=$AFFECTED" >> $GITHUB_OUTPUT

- name: Test affected packages
  if: steps.affected.outputs.packages != ''
  run: npx turbo run test --filter='...[HEAD~1]'

对于仅影响大型 monorepo 中的单个包的更改,这可以将 CI 时间缩短 60-80%。


常见问题

我们应该多久部署到生产环境?

在管道允许的情况下尽可能频繁地进行部署。高绩效团队每天部署多次。目标是易于审查、测试和回滚的小型增量更改。如果部署感觉有风险,则表明您的管道需要更多自动化测试和更好的回滚机制,而不是减少部署。

我们应该使用基于主干的开发还是功能分支?

生命周期较短(1-3 天)的功能分支最适合大多数团队。基于主干的开发需要更成熟的测试基础设施和功能标志。重要的是分支是短暂的——长期存在的功能分支会产生合并冲突并延迟反馈。

我们如何在 CI/CD 中处理数据库迁移?

在应用程序部署之前将迁移作为单独的管道步骤运行。确保迁移向后兼容(旧应用程序版本必须适用于新架构)。使用扩展和收缩模式:首先添加新列,部署写入旧列和新列的代码,迁移数据,然后在后续版本中删除旧列。

什么是 CI 的正确测试金字塔?

对于典型的 Web 应用程序:70% 单元测试(快速、隔离)、20% 集成测试(API 端点、数据库查询)、10% E2E 测试(关键用户流)。单元测试在每次提交时运行。集成测试在 PR 上运行。 E2E 测试在合并到主系统时或在生产部署之前运行。


接下来会发生什么

精心设计的 CI/CD 管道是所有其他 DevOps 实践的基础。有了可靠的自动化,您就可以自信地追求基础设施即代码生产监控负载测试

联系 ECOSIRE 了解 CI/CD 管道设计和实施,或探索我们的小型企业 DevOps 指南 了解完整的基础设施路线图。


由 ECOSIRE 发布——帮助企业充满信心地部署软件。

E

作者

ECOSIRE Research and Development Team

在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。

通过 WhatsApp 聊天