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
安全门政策
| 查找严重性 | 公关行为 | 生产行为 |
|---|---|---|
| 关键 | 块合并 | 区块部署 |
| 高 | 块合并 | 区块部署 |
| 中等 | 警告,允许合并 | 警告,允许部署 |
| 低 | 仅供参考 | 仅供参考 |
分支保护和合并策略
所需的状态检查
将这些配置为主分支上所需的状态检查:
- Lint 和类型检查必须通过 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 发布——帮助企业充满信心地部署软件。
作者
ECOSIRE Research and Development Team
在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。