前置阅读:建议先阅读 Docker 构建优化完全指南 与 多平台镜像构建与 Docker Buildx、私有镜像仓库 Harbor 实战。
关键概念:CI/CD 里的 Docker 不是"在流水线里跑几个 docker 命令"那么简单——层缓存决定构建速度、扫描与签名决定交付质量、仓库策略与 promotion 决定发布流程。一条生产级流水线 = 快速构建 + 质量门禁 + 可信分发 + 可控发布。
1. 流水线中的 Docker 全景
1.1 端到端链路
提交代码 → 触发 CI → 单元测试 → 构建镜像 → 扫描/签名 → 推送到仓库
→ 部署到 staging → 验收 → promote 到生产 → 渐进发布 → 监控/回滚
关键决策点:
| 阶段 | 关注点 | 工具 |
|---|---|---|
| 构建 | 层缓存、并发、多平台 | BuildKit + Buildx |
| 质量 | 漏洞扫描、SBOM、签名 | Trivy + Syft + Cosign |
| 分发 | 仓库策略、不可变标签 | Harbor / ECR + OCI |
| 发布 | promotion、灰度、回滚 | GitOps / Argo CD / 渐进工具 |
1.2 两个基本原则
原则一:镜像不可变
用 git SHA 作标签(myapp:1a2b3c4),同一 SHA 只构建一次
不用 latest / 版本号覆盖(会破坏可追溯性)
原则二:构建前置、门禁后置
任何环境部署前,镜像必须已通过扫描与签名
生产 promotion 必须显式触发,而非自动流转
2. GitHub Actions 中的 Docker 流水线
2.1 基础构建工作流
name: build-image
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: 登录 GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 构建并推送
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
2.2 层缓存:type=gha
BuildKit 的 type=gha 缓存把层缓存放进 GitHub Actions 缓存服务,跨运行共享,PR 间的重复构建秒级命中:
- name: 带 gha 缓存的构建
uses: docker/build-push-action@v6
with:
push: false
tags: app:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max,mode=min
# 本地等价的缓存导出命令
docker buildx build --push \
--cache-from type=gha --cache-to type=gha,mode=max \
-t ghcr.io/team/app:$SHA .
2.3 扫描与签名门禁
- name: 漏洞扫描(Trivy)
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1' # 存在高危即失败(门禁)
- name: 镜像签名(Cosign,keyless)
uses: sigstore/cosign-installer@v3
- run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}:${{ github.sha }}
3. GitLab CI 中的 Docker 流水线
3.1 用 docker:dind 在 Runner 内构建
stages: [build, test, scan, deploy]
variables:
IMAGE: registry.example.com/myapp:$CI_COMMIT_SHA
DOCKER_TLS_CERTDIR: "/certs"
build:
stage: build
image: docker:27.3
services:
- docker:27.3-dind # Docker-in-Docker 服务
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build --pull \
--cache-from $CI_REGISTRY_IMAGE:cache-$CI_COMMIT_BRANCH \
-t $IMAGE .
- docker push $IMAGE
3.2 用 BuildKit 的 registry 缓存(跨 Pipeline)
# GitLab CI 中推荐的 registry cache 方式
docker buildx create --use
docker buildx build --push \
--cache-from type=registry,ref=$CI_REGISTRY_IMAGE:buildcache \
--cache-to type=registry,ref=$CI_REGISTRY_IMAGE:buildcache,mode=max \
-t $IMAGE .
3.3 质量门禁阶段
scan:
stage: scan
image: docker:27.3
services: [docker:27.3-dind]
script:
- docker pull $IMAGE
# Trivy 扫描,忽略 IaC(Dockerfile 用 --severity 单独处理)
- trivy image --exit-code 1 --severity CRITICAL,HIGH $IMAGE
# 生成 SBOM 并附加
- syft $IMAGE -o cyclonedx-json > sbom.json
- cosign attach sbom --sbom sbom.json $IMAGE
- cosign sign --yes $IMAGE
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
一句话:无论 GitHub 还是 GitLab,模板只有三件事——构建时吃缓存、交付前扫镜像、分发前签镜像,套到各自平台的语法即可。
4. 层缓存加速:原理与避坑
4.1 缓存命中的规则
BuildKit 按指令哈希判断缓存是否可复用:RUN 指令的 shell 文本变了,缓存就失效并连累后续指令。把"变更频繁"的步骤放在 Dockerfile 后段:
# 缓存友好的顺序:先依赖、后代码
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./ # 很少变 → 命中缓存
RUN go mod download # 只依赖前两行
COPY . . # 代码常变 → 其后指令重建
RUN go build -o /app .
4.2 缓存来源对比
| 缓存后端 | 适用平台 | 特性 | 注意 |
|---|---|---|---|
type=gha | GitHub Actions | 命中快、自动 GC | 超 10GB 会淘汰 |
type=registry | GitLab / 通用 | 缓存在镜像仓库,多平台可复用 | 增加仓库存储 |
type=local | 本地 / 自建 Runner | 共享磁盘缓存 | 多 Runner 需共享存储 |
| BuildKit inline | 单机 | 简单 | 只能内联少量层 |
4.3 常见缓存失效原因
□ Dockerfile 步骤顺序不当(拷贝大目录在前)
□ 依赖锁定文件(go.sum/package-lock)每次变化
□ apt/yum 源更新导致 RUN 哈希变化
□ 使用 latest 基础镜像(无锁定 digest)
□ 平台不一致(amd64 与 arm64 缓存互不通用)
# 锁定基础镜像 digest,稳定缓存
FROM golang:1.23@sha256:abcdef... AS build
5. 镜像签名与扫描门禁
5.1 门禁策略矩阵
| 级别 | 门禁 | 失败动作 | 建议阈值 |
|---|---|---|---|
| 构建 | 编译/测试通过 | 终止 | —— |
| 镜像 | Trivy 扫描 | 阻断推送 | CRITICAL=0、HIGH 需审批 |
| 镜像 | 签名校验 | 阻断拉取 | 无签名禁止部署 |
| 供应链 | SBOM 比对 | 阻断 | 未知来源组件告警 |
5.2 Cosign keyless 签名与校验
# 签名(利用 GitHub OIDC,无长期密钥)
cosign sign --yes ghcr.io/team/app:$SHA
# 部署侧校验签名后才拉取
cosign verify --certificate-identity "https://github.com/team/app/.github/workflows/*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/team/app:$SHA
5.3 扫描结果如何进流水线门禁
# Trivy 扫描并输出 JSON 供门禁判断
trivy image --format json --output trivy.json ghcr.io/team/app:$SHA
# 用 jq 自定义门禁逻辑(例如允许 P1 但禁 P0)
if [ "$(jq '.Results[].Vulnerabilities | map(select(.Severity=="CRITICAL")) | length' trivy.json | paste -sd+ | bc)" -gt 0 ]; then
echo "存在 CRITICAL 漏洞,阻断"; exit 1
fi
6. 多环境 promotion 与镜像仓库策略
6.1 promotion 的核心:同一镜像跨环境
构建一次 → 部署 dev/staging 验证 → 同一 SHA 镜像 promote 到 prod
关键:dev 与 prod 跑的是同一个镜像(immutable tag),
环境差异只来自 config/secret,不来自代码。
6.2 标签策略
| 标签风格 | 是否推荐 | 理由 |
|---|---|---|
myapp:1a2b3c4(SHA) | 是 | 不可变、可溯源 |
myapp:v1.2.3(版本) | 有条件 | 与 Git tag 绑定 |
myapp:latest | 否 | 不可溯源,生产禁用 |
myapp:cache-<branch> | 专用 | 仅作缓存参考 |
6.3 Argo CD 式 GitOps promotion
# staging 环境(application 指向 staging 分支/目录)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-staging
spec:
destination: { namespace: staging, server: https://staging.example.com }
source:
repoURL: https://github.com/team/manifests.git
path: overlays/staging
targetRevision: staging
# 生产 promotion = 合并 PR 到 production 目录 → GitOps 自动同步
git checkout -b promote-$SHA
sed -i "s#image: myapp:.*#image: registry.example.com/myapp:$SHA#g" overlays/production/deploy.yaml
git add -A && git commit -m "promote $SHA to production"
git push origin promote-$SHA # 走 PR + 审批
一句话:promotion 不是"重新构建",而是把已验证的同一镜像 + 新的配置/清单推进到下一环境——镜像与配置分离是流水线可控的基石。
7. blue-green 与渐进交付
7.1 三种发布模型对比
| 模型 | 原理 | 回滚 | 风险 | 适用 |
|---|---|---|---|---|
| Recreate | 删旧起新 | 慢 | 高 | 不可用场景 |
| Rolling | 滚动替换 | 中 | 中 | 常规发布 |
| Blue-Green | 新旧并存,切流量 | 秒级 | 低 | 核心服务 |
| Canary | 小比例灰度放量 | 秒级 | 最低 | 高风险变更 |
7.2 蓝绿发布的容器实现
services:
# 两个同名 service 指向不同镜像,通过 Router 切换
router:
image: nginx:1.27
ports: ["8080:80"]
volumes: [./nginx-blue-green.conf:/etc/nginx/nginx.conf]
blue:
image: registry.example.com/myapp:$SHA_OLD
green:
image: registry.example.com/myapp:$SHA_NEW
# 切换:改 nginx.conf 的 upstream 指向 green,reload 即可
# upstream backend { server blue:8080; } → server green:8080;
docker compose exec router nginx -s reload
7.3 渐进交付:Argo Rollouts + 指标判定
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10 # 先放 10%
- pause: {duration: 5m} # 观察 5 分钟
- setWeight: 50
- pause: {duration: 5m}
- setWeight: 100
analysis:
templates:
- templateName: error-rate # 错误率超标自动回滚
一句话:蓝绿解决"快速切换",Canary 解决"小步放量 + 自动回滚";容器不可变镜像让两者都变成"换一个 tag、切一下流量"的轻操作。
8. 镜像仓库管理:Harbor 与 ECR
8.1 Harbor 的核心能力
Harbor = 私有 Registry + 安全策略 + 复制分发
- 镜像不可变(immutable tag 防覆盖)
- 漏洞扫描(内嵌 Trivy)与 拦截策略
- RBAC / 项目隔离 / 审计日志
- 仓库间镜像复制(主备、跨地域)
- OIDC / 机器人账号(CI 专用)
# 创建不可变规则的 API(Harbor v2.8+)
curl -k -u admin:harbor12345 https://harbor.example.com/api/v2.0/projects/myapp/immutabletagrules \
-H "Content-Type: application/json" \
-d '{"scope":{"type":"repository","repositories":["myapp"]},"enabled":true,"rule":{"selector":{"kind":"regex","decoration":"matches","pattern":"v.*"},"disabled":false}}'
8.2 ECR 与 CI 集成
# ECR 登录 + 构建推送
aws ecr get-login-password --region ap-southeast-1 \
| docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com
docker buildx build --push \
-t 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/myapp:$SHA \
--platform linux/amd64,linux/arm64 .
# ECR 生命周期策略(保留最近 N 个镜像)
# aws ecr put-lifecycle-policy --repository-name myapp \
# --lifecycle-policy-text '{"rules":[{"rulePriority":1,"selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":50},"action":{"type":"expire"}}]}'
8.3 仓库管理最佳实践
□ 生产项目开启镜像不可变,禁止覆盖标签
□ CI 用机器人账号,最小权限、可审计
□ 定期清理过期/悬空镜像(生命周期策略)
□ 开启漏洞扫描并阻止高危镜像被拉取
□ 异地复制确保容灾,但副本策略要一致
9. 总结
| 流水线环节 | 关键动作 | 工具/机制 | 验收标准 |
|---|---|---|---|
| 构建 | 层缓存 + 多平台 | BuildKit / Buildx / gha | PR 镜像分钟级构建 |
| 质量门禁 | 漏洞扫描 + SBOM | Trivy / Syft | 0 CRITICAL 才放行 |
| 可信分发 | 签名 + 校验 | Cosign keyless | 无签名禁止部署 |
| 仓库 | 不可变 + 生命周期 | Harbor / ECR | 无覆盖标签、可回溯 |
| Promotion | 同镜像跨环境 | GitOps / Argo CD | 生产与 staging 同 SHA |
| 发布 | 蓝绿/金丝雀 | Argo Rollouts | 失败秒级回滚 |
把 Docker 接入 CI/CD,本质是把"构建、质量、可信、发布“四件事做成可重复、可门禁、可回溯的流水线:构建吃缓存、交付前扫描、分发前签名、发布渐进可控。当一条流水线能让同一 SHA 的镜像从 PR 一路顺畅 promote 到生产并在几秒内回滚,它就不再是"自动化脚本”,而是一套可信任的交付机制。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。