← 中文资源

简体中文版本

在 CI 运行之间共享 Docker 构建缓存

使用 BuildKit 的 GitHub Actions 缓存后端复用镜像层,避免每次流水线都重新安装依赖,同时不把缓存层推成可拉取的镜像标签。

阅读英文原文

概述

把缓存层推成 :cache 标签虽然可行,但会把构建内部产物放进用户可拉取的命名空间,也让镜像清理变得含糊。type=gha 后端把缓存存入 GitHub Actions 缓存服务,并按仓库隔离:BuildKit 通过 cache-from 导入、通过 cache-to 导出,镜像仓库只接收最终镜像。只需在构建步骤启用一次,后续运行即可受益,不需要额外基础设施。

步骤

  1. 构建时同时启用缓存导入与导出:
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ghcr.io/acme/api:${{ github.sha }}
    cache-from: type=gha,scope=${{ github.ref_name }}
    cache-to: type=gha,mode=max,scope=${{ github.ref_name }}
  1. 按分支隔离缓存作用域,避免特性分支覆盖主分支缓存:
cache-from: type=gha,scope=${{ github.ref_name }}
cache-to: type=gha,mode=max,scope=${{ github.ref_name }}
  1. 调整 Dockerfile 顺序,先复制锁文件再复制源码:
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
  1. 用 .dockerignore 排除密钥,确保任何缓存层都不会包含敏感文件:
.env
.env.*
*.pem

验证

第二次运行应出现大量 CACHED 标记,依赖安装层被复用,总时长明显下降。修改业务源码后,只有最后几层失效,安装层仍然命中。若缓存始终不命中,检查基础镜像是否使用了可变 tag,以及 apt-get update 是否与安装分离;这两者都会让第一层每次变化。

注意事项

  • Actions 缓存按仓库隔离,并会在长期不活跃后被清理,只能当作加速手段,不能当作持久存储。
  • mode=max 会缓存中间阶段,容易耗尽仓库缓存配额,大镜像建议改用 mode=min。
  • 基础镜像应使用 digest 固定,tag 变化会导致缓存静默失效。
  • 缓存命中不代表产物安全,最终镜像仍需经过漏洞扫描。
  • 不要把 node_modules 之类的大目录写入构建上下文,它会破坏缓存复用并拖慢上传。