Docker 与 CI/CD 流水线集成:从构建到交付

完整讲解 Docker 在 CI/CD 流水线中的落地:GitHub Actions 与 GitLab CI 的镜像构建与缓存加速、layer cache 复用、镜像签名(Cosign)与漏洞扫描门禁、多环境 promotion 与 blue-green/渐进交付、Harbor/ECR 等镜像仓库管理与生命周期策略。

前置阅读:建议先阅读 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=ghaGitHub Actions命中快、自动 GC超 10GB 会淘汰
type=registryGitLab / 通用缓存在镜像仓库,多平台可复用增加仓库存储
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 / ghaPR 镜像分钟级构建
质量门禁漏洞扫描 + SBOMTrivy / Syft0 CRITICAL 才放行
可信分发签名 + 校验Cosign keyless无签名禁止部署
仓库不可变 + 生命周期Harbor / ECR无覆盖标签、可回溯
Promotion同镜像跨环境GitOps / Argo CD生产与 staging 同 SHA
发布蓝绿/金丝雀Argo Rollouts失败秒级回滚

把 Docker 接入 CI/CD,本质是把"构建、质量、可信、发布“四件事做成可重复、可门禁、可回溯的流水线:构建吃缓存、交付前扫描、分发前签名、发布渐进可控。当一条流水线能让同一 SHA 的镜像从 PR 一路顺畅 promote 到生产并在几秒内回滚,它就不再是"自动化脚本”,而是一套可信任的交付机制。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「docker」更多文章

  1. Docker 容器排障与调试实战:退出码、OOM、exec 与调试工具链
  2. Docker 镜像供应链安全:SBOM、签名、扫描与 SLSA 合规
  3. Docker 容器监控与日志实践:cgroups 资源限制、Prometheus 与日志驱动