Skip to content

CI/CD 流水线

把「能编译」变成「可随时发布的制品、可控的发布过程」。运维视角重点关注:流水线怎么设计、密钥怎么管、制品版本怎么不可变、发布怎么可回滚。示例以 GitHub Actions 与 GitLab CI 为主,需结合真实仓库验证。

先分清 CI 与 CD

  • CI(持续集成):每次提交都跑 检查 → 测试 → 构建,产出可部署的制品并归档;
  • CD(持续交付/部署):把制品发布到环境。持续交付停在「可发布」人工确认,持续部署全自动到生产。

原则:CI 决定「能不能上」、CD 决定「敢不敢上」。CI 不绿不产出制品,CD 不带门禁不放行。

流水线的七个常见阶段

text
触发 → lint/格式 → 单元测试 → 构建制品 → 制品扫描/签名 → 部署预发 → 灰度/生产(门禁)
  • 触发要窄:push / pull request 分开,PR 只跑检查不发布,主干才出制品;
  • 测试分层:单测快跑在 PR,集成/E2E 在合并后或定时跑,别让慢测试拖垮每次提交;
  • 制品不可变:构建一次、处处部署——同一个 sha 的镜像既用于预发也用于生产,绝不在 CD 里重新构建(否则「测的」和「跑的」不是同一份);
  • 缓存讲策略:依赖缓存加速 PR(node_modules / Maven 仓库),但每次 git clean 保证不受污染;版本锁定文件(package-lock.json / go.sum)必须提交;
  • 环境是牛马,不是牛仔:每个环境用独立 secret 与独立凭证,预发接近生产配置(尤其是数据库 schema 迁移要能在预发真实演练)。

GitHub Actions:概念对照

yaml
name: ci
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [20, 22]          # 多版本矩阵:一份配置跑多组
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: npm
      - run: npm ci
      - run: npm test

  build-and-push:
    if: github.event_name == 'push'
    needs: test                  # 依赖:test 过才跑
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: registry.example.com
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_TOKEN }}
      - name: Build & push image
        run: |
          docker build -t registry.example.com/app:${{ github.sha }} .
          docker push registry.example.com/app:${{ github.sha }}

GitLab CI 用 .gitlab-ci.yml,思路一致:stages 定义阶段(build/test/deploy),job 带 stageonly/except(或 rules)、artifactsenvironment;Runner 是执行者,services 起依赖(如数据库容器)。

制品与版本:让回滚成为可能

  • 镜像 tag 用不可变标识git-<commit sha><构建号>,同一 sha 可重复部署;latest 只在本地开发用;
  • 构建信息进制品元数据(源码 commit、构建时间、触发人),部署页/审计能追溯到「哪次提交产生了这包」;
  • 保留最近 N 个可回滚版本,超期清理镜像仓库,避免「盘满了没法回滚」。

发布策略:灰度是 CD 的灵魂

策略方式优点代价
滚动逐个替换实例零停机,简单新旧共存,兼容性要求高
蓝绿整组切换回滚极快(切回旧组)双倍资源
金丝雀小流量新版本风险最可控、真实流量验证需要流量切分与观测
  • 蓝绿/金丝雀的准入门槛是观测:灰度期间对比错误率、延迟、业务指标,异常即自动回滚——没有观测的灰度等于「让新版本裸奔」;
  • 发布节奏要配回滚手册:每条发布记录记下部署的镜像 sha 与前一个 sha,「一键回滚」= 重放旧 sha 再配流量切换;
  • 数据库变更先于代码发布并向后兼容(expand/contract),否则「先回滚代码」也救不了已执行的破坏性 DDL。

安全:流水线是供应链的第一道门

  • 密钥只进 secrets:运行参数、配置一律经 CI 平台的 secret 注入,不打印、不进日志、不进制品;
  • 第三方 actions/images 锁 tag 或 sha,审一遍来源再允许进流水线;
  • 依赖与镜像扫描进门禁:高危(已知 CVE / 可疑依赖)阻断发布;
  • 流水线权限最小化:只读源码、只推本团队的镜像仓库、deploy 凭证按环境隔离;
  • 出包签名(cosign)并在运行时校验,防止「仓库被写坏但没人知道」。

GitOps 一句话

把「部署动作」也声明化:git 仓库是唯一事实源,operator(如 ArgoCD)持续把仓库状态同步到集群,人不再手敲 kubectl apply。适合成熟团队,先保证 CI/CD 基础扎实再引入,不要为了潮流叠复杂度。

常见坑速查

现象解法
CD 里重新构建测的与跑的不是同一份构建一次,制品贯穿所有阶段
tag 用 latest部署结果不可预期不可变 tag(sha/构建号)
PR 也触发发布测试环境被 PR 污染触发分支收窄,主干才出制品
密钥写进 yaml泄露进仓库历史一律 secrets 注入
依赖缓存不锁版本构建时好时坏提交 lockfile,CI 用 npm ci
蓝绿/金丝雀无观测带病发布到 100%灰度期对比错误率/延迟,异常自动回滚
先破坏性 DDL 后回滚代码数据不可逆expand/contract,变更向后兼容
回滚手册只在脑子里出事手忙脚乱发布记录含前后 sha,回滚 = 重放旧 sha

检查清单

  • [ ] 流水线覆盖七阶段且 CI 绿才产出制品,制品带不可变 tag 与溯源信息;
  • [ ] 测试分层合理,PR 只跑快速检查,慢测试不进关键路径;
  • [ ] 密钥全部 secret 注入,三方依赖锁定版本并做来源审查;
  • [ ] 发布有门禁与观测基线(错误率/延迟),失败能自动或一键回滚;
  • [ ] 数据库变更向后兼容,破坏性操作有前置演练;
  • [ ] 回滚手册与发布记录齐全,定期演练灾难恢复路径。

基于 VitePress 构建 · 内容以知识共享方式沉淀