流水线在做什么

代码 → Lint/Format → Test → Build → 安全扫描 → 推工件 → 部署 → 验证

有的项目还多 / 少一两步,但这个骨架是公共的。每一步都有它存在的理由——少一步就少一道防线。

5 条经得起考验的原则

1. 阶段划分:失败即停

jobs:
  lint:
    ...
  test:
    needs: lint                # lint 过了才跑 test
  build:
    needs: test
  scan:
    needs: build
  deploy:
    needs: scan
    if: github.ref == 'refs/heads/main'

阶段间显式依赖——前面失败立刻停,节约时间和资源。

2. 缓存:能复用就复用

依赖装好的不变 → 缓存(npm / pip / cargo):

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm                # 自动缓存 node_modules

镜像层缓存:

- uses: docker/build-push-action@v6
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

缓存不命中也要跑得过——缓存是优化,不是依赖

3. 工件追溯:每次 build 唯一 tag

ghcr.io/me/webapp:1.4.2-abc1234     ← 版本号 + git SHA

用唯一 tag 而不是 :latest

  • 部署到 prod 的镜像可以精确回到当时的代码
  • 滚回 = 重新 deploy 旧 tag,不需要重 build
  • 故障排查时能问"这个 SHA 包含了哪些 commit"

CI 里通常打多个 tag

docker tag webapp ghcr.io/me/webapp:${GIT_SHA}
docker tag webapp ghcr.io/me/webapp:${VERSION}
docker tag webapp ghcr.io/me/webapp:latest        # 仅 main 分支

4. 拒绝在 CI 里做这些

  • 跑生产数据迁移:CI 跑迁移 = 部署没做之前数据已经改了,回滚极难
  • 把 secret 写日志:所有 CI 都自动 mask 已声明的 secret,但 echo $TOKEN 还是危险
  • 跑外部不可重复的副作用curl prod-api/cleanup 这种,CI 失败会留半个状态
  • 把 main 分支的部署放在没保护的 job 里:要门控

5. 加保护门

deploy-prod:
  environment:
    name: production         # GitHub Environment:可加 required reviewers
  needs: deploy-staging
  if: github.ref == 'refs/heads/main'

GitHub / GitLab 的 Environment 功能:可加审批人、限制时段、记录部署历史。

几个常见反模式

反模式 该改成
build 在 push 时跑、test 在 PR 时跑 都在 PR 跑,main 上只 build + deploy
CI 配置 200 行单文件 拆 reusable workflow / template
测试 5 分钟、整条流水线 30 分钟 profile 一遍,瓶颈通常在装依赖
main 部署需要 1 个人手点 5 次 自动到 staging,prod 可加单次审批
缓存把 lockfile 都缓存了 缓存 key 一定要含 lockfile 哈希

DORA 视角的衡量

DORA 用 4 个指标看 CI/CD 健康度(不要把数字当 KPI 折磨自己,当方向用):

  • 部署频率(Deployment Frequency)
  • 变更前置时间(Lead Time for Changes:commit → 上线)
  • 变更失败率(Change Failure Rate)
  • 服务恢复时间(Time to Restore Service)

高表现团队这 4 项都好。只看部署频率会被骗——没测试没监控的高频部署就是定时炸弹。

推荐阅读

下一篇:具体工具怎么选——Actions / GitLab CI / Jenkins / Tekton。