写在前面:BuildKit/Buildx 到底是什么关系?

很多人日常只用 docker build,但真正让构建“又快又稳又可审计”的关键在 BuildKit

  • BuildKit:Docker 的下一代构建引擎,提供并行构建、增量缓存、RUN --mount、远程缓存导入导出、供应链元数据(SBOM/Provenance)等能力。
  • Buildx:Docker 的构建前端(CLI 插件/子命令),用来驱动 BuildKit,并提供多平台、远程 builder、Bake 编排、导出/推送、attestation 等高级功能。

截至 2026-01(以官方发布为准),Buildx 最新稳定版本仍在 0.30.x(2025-11 发布 0.30.0/0.30.1),其中包含了对 imagetools 与 attestation 持久化等能力的增强,以及对部分旧命令的弃用提示。

适用读者与版本建议

本文面向:

  • 需要在本地/CI 中加速镜像构建的人
  • 需要多架构镜像(amd64/arm64)的人
  • 需要把缓存、SBOM、Provenance 一并纳入发布流程的人

版本建议(越新越好):

  • Docker Engine / Docker Desktop:建议使用近两年的版本
  • docker buildx:建议 0.29+,最好接近 0.30.x
  • Dockerfile frontend:建议显式声明 #syntax=docker/dockerfile:1.8(或更高)

一张图理解 BuildKit 的“新能力地图”

BuildKit 的“新特性”可以按场景分成四类:

  • 性能:并行、细粒度缓存、cache mount、减少上下文传输
  • 可复现:更严格的 Dockerfile 解析/校验、平台一致性检查
  • 多平台:一次构建产出 linux/amd64 + linux/arm64 并推送 manifest list
  • 供应链:Provenance(SLSA 语义)与 SBOM attestation,提升可审计性

下面按“你能立刻用起来”的角度展开。

新特性 1:RUN --mount 让构建更快(缓存/Secret/SSH)

1.1 缓存挂载:把“下载依赖”从每次构建中剥离出来

以 Node.js 为例,传统 RUN npm ci 每次都会重新下载依赖(或者依赖层缓存命中不稳定)。BuildKit 的 cache mount 可以把下载缓存放到一个专用缓存里,让构建在本地和 CI 都更稳。

1
2
3
4
5
6
7
8
9
10
11
12
# syntax=docker/dockerfile:1.8
FROM node:20-bookworm-slim AS build
WORKDIR /app

COPY package.json package-lock.json ./

# Cache npm download directory to speed up builds.
RUN --mount=type=cache,target=/root/.npm \
npm ci

COPY . .
RUN npm run build

要点:

  • cache mount 不会进入最终镜像层,仅用于加速构建步骤
  • 对 Go / Maven / pip 等生态同样有效(目标目录不同)

1.2 Secret 挂载:把 Token 从镜像层里彻底移除

很多团队会在构建时拉私有依赖(NPM token、pip index token、GitHub token)。正确做法是用 --secret 在构建阶段临时注入,不落盘进镜像层。

1
2
3
4
# syntax=docker/dockerfile:1.8
FROM alpine:3.20
RUN --mount=type=secret,id=my_token,target=/run/secrets/my_token \
sh -c 'test -s /run/secrets/my_token && echo "Secret is present"'

构建时传入 secret:

1
2
3
4
# Pass secret from local file.
docker buildx build \
--secret id=my_token,src=./my_token.txt \
-t demo:secret .

注意:

  • secret 只在该条 RUN 指令可见
  • 不要用 ARG/ENV 传敏感信息(容易进入层或被构建日志泄露)

1.3 SSH 挂载:安全拉取私有 Git 仓库

当你需要 git clone 私有仓库时,可以用 SSH agent 转发,而不是把私钥复制进镜像。

1
2
3
4
5
# syntax=docker/dockerfile:1.8
FROM alpine:3.20
RUN apk add --no-cache git openssh-client
RUN --mount=type=ssh \
git clone git@github.com:YOUR_ORG/YOUR_PRIVATE_REPO.git /src

构建时启用:

1
2
# Forward default SSH agent socket.
docker buildx build --ssh default -t demo:ssh .

多个 SSH Key 的常见做法

做法 A:在同一个 ssh-agent 里加载多把 key(推荐)

BuildKit 转发的是 SSH agent,因此多 key 场景最稳的方式是:在宿主机把多把 key 都 ssh-add 进同一个 agent,然后构建时仍然只需要 --ssh default

1
2
3
4
5
6
7
8
9
# Start ssh-agent in current shell.
eval "$(ssh-agent -s)"

# Add multiple keys into the same agent.
ssh-add ~/.ssh/id_ed25519_github
ssh-add ~/.ssh/id_ed25519_gitlab

# Build with forwarded agent.
docker buildx build --ssh default -t demo:ssh .

说明:

  • agent 会根据目标主机与可用 key 自动尝试匹配;如果你有 GitHub/GitLab/自建 Git 多个来源,这是最省心的方式。
  • 如果 key 带 passphrase,使用 agent 方式也更友好(避免在构建里交互输入)。
做法 B:用多个 --ssh id=... 分别转发不同 key(便于精确控制)

如果你想在 Dockerfile 的不同步骤里明确使用不同的 SSH 凭据,可以为 --mount=type=ssh 指定 id,并在构建时传入多个 --ssh

1
2
3
4
5
6
7
8
9
# syntax=docker/dockerfile:1.8
FROM alpine:3.20
RUN apk add --no-cache git openssh-client

RUN --mount=type=ssh,id=github \
git clone git@github.com:YOUR_ORG/YOUR_PRIVATE_REPO.git /src/github

RUN --mount=type=ssh,id=gitlab \
git clone git@gitlab.com:YOUR_ORG/YOUR_PRIVATE_REPO.git /src/gitlab

构建时分别指定不同 id 的来源:

1
2
3
4
5
# Forward different keys (or sockets) with different ids.
docker buildx build \
--ssh github=$HOME/.ssh/id_ed25519_github \
--ssh gitlab=$HOME/.ssh/id_ed25519_gitlab \
-t demo:ssh-multi .

提示:

  • 如果你使用的是 passphrase key,更推荐做法 A(agent),然后把 --ssh github=$SSH_AUTH_SOCK / --ssh gitlab=$SSH_AUTH_SOCK 都指向同一个 agent socket(本质上仍由 agent 管理解密与选择)。

新特性 2:远程缓存(--cache-to/--cache-from)让 CI 构建“越跑越快”

仅靠本地层缓存,CI 往往命中率很差。BuildKit 支持把缓存导出到 registry(或其他后端),再在后续构建导入。

下面是一个常见的 registry 缓存模式(适合 GitHub Actions/Jenkins/GitLab CI 等):

1
2
3
4
5
6
7
# Export cache to registry and reuse in next builds.
docker buildx build \
--platform linux/amd64,linux/arm64 \
--cache-from type=registry,ref=registry.example.com/myapp:buildcache \
--cache-to type=registry,ref=registry.example.com/myapp:buildcache,mode=max \
-t registry.example.com/myapp:$(git rev-parse --short HEAD) \
--push .

建议:

  • 只在推送镜像时写入远程缓存,否则会产生无效写入
  • mode=max 缓存更完整但占用更大;可按项目权衡

新特性 3:多平台镜像从“麻烦事”变成“默认能力”

Buildx 的典型价值之一就是多平台构建。你可以用一条命令构建并推送多架构镜像:

1
2
3
4
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.example.com/myapp:latest \
--push .

常见坑:

  • --load 只能加载单平台镜像到本地 Docker 镜像仓库(并且可能丢失部分元数据),多平台通常要用 --push
  • 某些依赖(如下载的二进制)需要按 TARGETARCH 分支处理,否则会构建出“能推送但不能运行”的镜像。

新特性 4:Dockerfile frontend(#syntax)持续进化:更强表达力 + 更严格校验

Dockerfile 顶部的 #syntax=... 让你选择 Dockerfile frontend 版本,从而获得新语义和新能力。

截至近年的更新中,值得关注的方向包括:

  • 变量展开能力增强:更接近 shell parameter expansion 的用法,写复杂镜像逻辑更可控(frontend 1.7+)。
  • 更严格的校验与检查:新增/增强检查规则,帮助在构建前发现平台不一致、ARG 扩展错误等问题(frontend 1.8+)。

实践建议:

  • 业务镜像建议显式写 # syntax=docker/dockerfile:1.8(或你团队验证过的更高版本)
  • 如果团队想把 Dockerfile 质量门禁做起来,可以研究 #check / BUILDKIT_DOCKERFILE_CHECK 相关能力,在 CI 中先做校验再做构建

新特性 5:供应链元数据(SBOM / Provenance)让镜像“可审计”

5.1 为什么需要 SBOM 与 Provenance?

当你需要回答这些问题时,SBOM/Provenance 就很关键:

  • 这个镜像里到底包含哪些依赖与组件?(SBOM)
  • 这个镜像是用什么源码、什么构建参数、什么环境构建出来的?(Provenance)

BuildKit 已经支持为镜像生成并附加这些 attestation(in-toto 语义),并且与 Docker 的构建体系集成。

5.2 开启方式(示例)

1
2
3
4
5
6
docker buildx build \
--platform linux/amd64,linux/arm64 \
--provenance=true \
--sbom=true \
-t registry.example.com/myapp:latest \
--push .

更精细的控制(示例,强调可读性):

1
2
3
4
5
6
# Generate more detailed provenance (may expose more build details).
docker buildx build \
--attest type=provenance,mode=max \
--attest type=sbom \
-t registry.example.com/myapp:latest \
--push .

重要提示:

注意:在某些导出方式下(例如 --load 或使用默认本地镜像存储),attestation 可能无法完整保留。生产发布链路建议以 --push 为主,并在你的镜像仓库侧验证元数据是否可见。

新特性 6:Buildx 的一些近期变化(截至 2025-11)

从近一年的发布说明看,Buildx 的演进重点之一是“把构建产物的元数据保留下来,并让多工具链更好协作”。

你可能会关心的点:

  • imagetools create 对 attestation manifest 与 cosign 相关签名的保留增强(适合做镜像重组/迁移时不丢元数据)。
  • 一些旧命令(如 docker buildx install/uninstall)出现弃用提示,更倾向于直接使用 docker buildx 的默认行为(以官方 release note 为准)。

一个可直接复用的 CI 模板(多平台 + 远程缓存 + SBOM/Provenance)

把下面这段“按需替换 registry/name/tag”就能跑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
REGISTRY=registry.example.com
IMAGE=$REGISTRY/myteam/myapp
TAG=$(git rev-parse --short HEAD)
CACHE_REF=$IMAGE:buildcache

docker buildx build \
--platform linux/amd64,linux/arm64 \
--cache-from type=registry,ref=$CACHE_REF \
--cache-to type=registry,ref=$CACHE_REF,mode=max \
--provenance=true \
--sbom=true \
-t $IMAGE:$TAG \
-t $IMAGE:latest \
--push .

常见问题与排查思路

1)为什么我开了 --sbom/--provenance,但仓库里看不到?

优先排查:

  • 你是否用了 --push(建议)而不是 --load
  • 你的镜像仓库/代理是否会剥离 OCI artifact/attestation(某些老旧组件会)
  • 是否用 docker buildx imagetools inspect 检查远端镜像清单与元数据

2)为什么多平台镜像构建很慢?

通常原因是:

  • 缓存没有跨 CI 复用(建议启用 registry cache)
  • Dockerfile 没有拆分依赖层,导致无效重建
  • 某些步骤无法并行(例如单个 RUN 做了过多工作)

3)cache mount 会不会把敏感文件缓存出去?

cache mount 的内容确实会进入缓存存储(本地或远程缓存后端),因此:

  • 不要把 secret 写进 cache 目录
  • secret 用 --secret,并确保只在单条 RUN 内使用

总结

BuildKit/Buildx 在近几年的核心价值可以概括为三句话:

  • 更快RUN --mount=type=cache + 远程缓存让构建从“分钟级”变成“秒级可预期”
  • 更强:多平台构建与更严格的 Dockerfile 语义/校验,让交付更稳定
  • 更可信:SBOM/Provenance 让镜像可审计,能对接更完整的软件供应链治理

参考资料

本文由 AI 辅助生成,如有错误或建议,欢迎指出。