6.1. CI/CD 流水线
CI 的目标不是「每次提交都跑一遍脚本」,而是让每个 commit 都能产出一个可验证、可追溯、可部署的制品。CD 的目标不是「自动 apply」,而是把已验证的制品安全地推进到环境里。
- 层级:
L4 · 实战
- 难度:
进阶
- 预计:
50 分钟
- 前置:
Git、Docker、Kubernetes、Helm/Kustomize
6.1.1. 流水线的六个阶段
阶段 |
产出 |
失败时 |
|---|---|---|
检出 |
commit + 依赖缓存 |
立即失败,不进入构建 |
构建 |
镜像 digest + SBOM |
阻断合并 |
测试 |
单测/集成/E2E 报告 |
阻断合并 |
扫描 |
漏洞/许可证/密钥报告 |
按阈值阻断 |
签名 |
Cosign 签名 + 证明 |
阻断推送 |
发布 |
GitOps commit / Helm values |
阻断部署 |
流水线要遵循「构建一次,处处部署」:dev/staging/prod 使用同一个镜像 digest,环境差异只通过配置注入。
6.1.2. 分支与触发
Trunk-based:短分支、频繁合并,适合持续交付;用 feature flag 隔离未完成功能。
GitFlow:发布分支多,适合版本化交付,但合并成本高。
PR 流水线:构建 + 测试 + 扫描,不推送生产镜像。
main 流水线:构建、签名、推送制品,更新 GitOps 仓库。
不要把「合并到 main」和「部署到生产」绑定成同一个动作;中间应有环境晋级与审批。
6.1.3. 一个可落地的流水线
name: build
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build
run: |
docker build -t ghcr.io/example/demoapp:${{ github.sha }} .
- name: Test
run: |
docker run --rm ghcr.io/example/demoapp:${{ github.sha }} make test
- name: Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/example/demoapp:${{ github.sha }}
exit-code: "1"
severity: CRITICAL,HIGH
- name: Push
if: github.ref == 'refs/heads/main'
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
docker push ghcr.io/example/demoapp:${{ github.sha }}
生产环境用 digest 部署:
kubectl set image deploy/demoapp demoapp=ghcr.io/example/demoapp@sha256:<digest>
6.1.4. 测试分层
单元测试:最快,覆盖业务逻辑,PR 必跑。
集成测试:数据库/消息队列/外部依赖,可用 Testcontainers 或临时命名空间。
E2E:部署到临时集群,验证关键路径;成本高,按需运行。
契约测试:服务间接口变更时,防止消费者被破坏。
策略测试:Kyverno/Gatekeeper 策略用
kyverno test/conftest验证。
测试要并行、缓存依赖、失败可复现;不要把 E2E 当唯一质量门禁。
6.1.5. 制品与缓存
镜像 tag 用 commit SHA 或语义版本,禁止 latest。
推送后记录 digest、SBOM、签名、扫描报告。
构建缓存用 registry cache 或 GitHub Actions cache,避免每次全量。
依赖锁定:
package-lock.json、go.sum、requirements.txt必须提交。
6.1.6. 密钥与权限
CI 使用短生命周期 OIDC 身份(GitHub Actions → 云 IAM),不要长期 AK/SK。
生产部署权限只给流水线的 deploy job,不给所有 PR。
密钥用 Vault/ESO/Sealed Secrets 注入,不写进仓库。
Runner 尽量 ephemeral,避免上一个任务的残留污染。
6.1.7. 实验:从 PR 到 GitOps
在 PR 中修改代码,触发构建 + 测试 + 扫描。
合并后构建镜像并推送,记录 digest。
流水线更新 GitOps 仓库中的
kustomization.yaml或values.yaml的 image digest。Argo CD 检测到 Git 变更,同步到 dev 环境。
验证通过后,提升到 staging/prod。
6.1.8. 验证清单
每个 commit 都能构建出唯一镜像 digest。
PR 流水线包含测试与扫描,失败会阻断合并。
生产部署使用 digest,不使用可变 tag。
镜像已签名,SBOM 已归档。
CI 使用 OIDC/短期凭证,无长期密钥。
发布失败能在 5 分钟内回滚。
6.1.9. 排错表
现象 |
原因 |
处理 |
|---|---|---|
构建成功但部署失败 |
镜像架构/tag/digest 错 |
检查 |
测试在 CI 失败本地通过 |
环境/依赖/时区差异 |
用容器化测试环境,锁定依赖 |
扫描阻断发布 |
基础镜像漏洞 |
升级基础镜像或加例外审批 |
流水线很慢 |
无缓存/串行任务 |
并行 job、缓存依赖、分层构建 |
生产凭证泄漏 |
密钥进仓库/日志 |
轮换、改用 OIDC、加 secret scanning |
6.1.10. 进阶挑战
给流水线加 SBOM 生成与 Cosign 签名。
用矩阵构建多架构镜像(amd64/arm64)。
把部署改成 GitOps 更新 digest,而不是直接
kubectl set image。给流水线加策略测试,阻止无 requests/limits 的清单。
6.1.11. 小结
CI 产出可追溯的制品,CD 把制品安全推进环境。
构建一次、处处部署;环境差异用配置,不用重新构建。
测试、扫描、签名是制品的一部分,不是可选步骤。
自动化不等于无人值守;生产晋级要有审批与回滚。