Docker Buildx多平台镜像构建

Docker Buildx 构建 amd64、arm64 等多平台镜像的使用方法、缓存配置和 CI/CD 建议。

Docker Buildx 多平台镜像构建

Docker Buildx 是 Docker 提供的新一代构建工具,基于 BuildKit,可以构建多平台镜像、使用高级缓存、输出镜像清单,并更好地适配 CI/CD 构建场景。它常用于同时构建 linux/amd64linux/arm64 镜像,满足 x86 服务器、ARM 服务器、Mac Apple Silicon 和云原生集群混合部署需求。

适用场景

  • 同一个应用需要运行在 x86 和 ARM 节点上。
  • Kubernetes 集群中同时存在 amd64arm64 节点。
  • CI/CD 需要一次构建并推送多平台镜像。
  • 本地 Apple Silicon 机器需要构建 Linux amd64 镜像。
  • 希望使用远端缓存或 registry 缓存提升镜像构建速度。

查看 Buildx

检查 Docker 是否支持 buildx:

docker buildx version

查看当前 builder:

docker buildx ls

如果当前 builder 不支持多平台构建,可以创建一个新的 builder。

创建 Builder

创建并切换到新的 builder:

docker buildx create --name multi-platform-builder --use

初始化 builder:

docker buildx inspect --bootstrap

查看支持的平台:

docker buildx inspect

如果输出中包含 linux/amd64linux/arm64 等平台,说明可以构建对应架构的镜像。

安装 QEMU 支持

在非目标架构机器上构建多平台镜像时,通常需要 QEMU 模拟支持。

docker run --privileged --rm tonistiigi/binfmt --install all

再次确认 builder 支持的平台:

docker buildx inspect --bootstrap

追加原生架构构建节点

如果有条件准备两台不同架构的构建机,推荐让 amd64 机器只负责 linux/amd64,arm64 机器只负责 linux/arm64。这种方式属于原生架构构建,通常比 QEMU 模拟更快,也更接近最终运行环境。

示例环境:

节点架构访问地址负责平台
build-amd64amd64ssh://builder@10.0.0.11linux/amd64
build-arm64arm64ssh://builder@10.0.0.12linux/arm64

前置要求:

  • 两台机器都已经安装 Docker。
  • 当前机器可以通过 SSH 免密登录两台构建机。
  • 登录用户有权限访问远端 Docker。
  • 两台机器都可以访问源码依赖和镜像仓库。

先验证 SSH 和远端 Docker:

ssh builder@10.0.0.11 docker version
ssh builder@10.0.0.12 docker version

创建 amd64 构建节点:

docker buildx create \
  --name native-multi-builder \
  --node build-amd64 \
  --platform linux/amd64 \
  ssh://builder@10.0.0.11

追加 arm64 构建节点:

docker buildx create \
  --name native-multi-builder \
  --append \
  --node build-arm64 \
  --platform linux/arm64 \
  ssh://builder@10.0.0.12

切换到这个 builder:

docker buildx use native-multi-builder

初始化并查看节点:

docker buildx inspect --bootstrap

正常情况下可以看到两个节点,每个节点对应自己的平台:

Name:          native-multi-builder
Driver:        docker-container

Nodes:
Name:      build-amd64
Endpoint:  ssh://builder@10.0.0.11
Platforms: linux/amd64

Name:      build-arm64
Endpoint:  ssh://builder@10.0.0.12
Platforms: linux/arm64

使用该 builder 构建多平台镜像:

docker buildx build \
  --builder native-multi-builder \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/ops/demo-api:1.0.0 \
  --push \
  .

构建时,Buildx 会把 linux/amd64 的构建任务分配到 amd64 节点,把 linux/arm64 的构建任务分配到 arm64 节点,最后在镜像仓库中生成同一个多平台镜像清单。

构建单平台镜像

构建当前平台镜像并加载到本地 Docker:

docker buildx build \
  --platform linux/amd64 \
  -t registry.example.com/ops/demo-api:1.0.0 \
  --load \
  .

--load 适合单平台镜像,会把构建结果加载到本地 Docker 镜像列表中。

查看本地镜像:

docker images registry.example.com/ops/demo-api

构建并推送多平台镜像

多平台镜像不能直接用 --load 加载到本地 Docker,通常需要直接推送到镜像仓库。

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/ops/demo-api:1.0.0 \
  --push \
  .

构建完成后,镜像仓库中会存在一个多平台镜像清单。不同架构的节点拉取同一个 tag 时,会自动选择对应架构。

查看镜像清单:

docker buildx imagetools inspect registry.example.com/ops/demo-api:1.0.0

同时打多个 Tag

发布时通常会同时打版本号和环境 tag。

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/ops/demo-api:1.0.0 \
  -t registry.example.com/ops/demo-api:stable \
  --push \
  .

使用构建参数

Dockerfile 中可以声明构建参数:

ARG APP_VERSION
ARG BUILD_DATE

LABEL org.opencontainers.image.version="${APP_VERSION}"
LABEL org.opencontainers.image.created="${BUILD_DATE}"

构建时传入参数:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --build-arg APP_VERSION=1.0.0 \
  --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -t registry.example.com/ops/demo-api:1.0.0 \
  --push \
  .

Dockerfile 多平台变量

BuildKit 会提供一些常用平台变量:

变量说明
BUILDPLATFORM执行构建的机器平台
TARGETPLATFORM目标镜像平台
TARGETOS目标操作系统
TARGETARCH目标 CPU 架构
TARGETVARIANT架构变体,例如 arm/v7

示例:

FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder

ARG TARGETOS
ARG TARGETARCH

WORKDIR /src
COPY . .
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/app ./cmd/app

FROM alpine:3.20
COPY --from=builder /out/app /usr/local/bin/app
ENTRYPOINT ["app"]

这种方式适合 Go、Rust 等可以交叉编译的语言,通常比 QEMU 模拟构建更快。

使用缓存加速构建

使用 registry 缓存:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --cache-from type=registry,ref=registry.example.com/ops/demo-api:buildcache \
  --cache-to type=registry,ref=registry.example.com/ops/demo-api:buildcache,mode=max \
  -t registry.example.com/ops/demo-api:1.0.0 \
  --push \
  .

CI/CD 中推荐使用远端缓存,避免每次流水线都从零开始构建。

GitLab CI 示例

build:image:
  stage: build
  image: docker:27
  services:
    - docker:27-dind
  variables:
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker buildx create --use --name ci-builder
    - docker buildx inspect --bootstrap
  script:
    - docker buildx build
      --platform linux/amd64,linux/arm64
      --cache-from type=registry,ref="$CI_REGISTRY_IMAGE:buildcache"
      --cache-to type=registry,ref="$CI_REGISTRY_IMAGE:buildcache",mode=max
      -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
      -t "$CI_REGISTRY_IMAGE:latest"
      --push
      .

常用命令

命令说明
docker buildx version查看 buildx 版本
docker buildx ls查看 builder 列表
docker buildx create --use创建并切换 builder
docker buildx inspect --bootstrap初始化并查看 builder
docker buildx build --platform linux/amd64 .构建指定平台镜像
docker buildx build --push ...构建并推送镜像
docker buildx build --load ...构建单平台镜像并加载到本地
docker buildx imagetools inspect IMAGE查看多平台镜像清单
docker buildx rm NAME删除 builder

常见问题

multiple platforms feature is currently not supported for docker driver

当前 builder 使用的是默认 docker driver,不适合多平台输出。创建一个 docker-container driver 的 builder:

docker buildx create --name multi-platform-builder --driver docker-container --use
docker buildx inspect --bootstrap

多平台构建后本地看不到镜像

使用 --push 构建多平台镜像时,镜像会直接推送到仓库,不会出现在本地 docker images 中。

如果只想本地测试单平台镜像,使用:

docker buildx build --platform linux/amd64 -t demo-api:test --load .

exec format error

通常是镜像架构和运行节点架构不匹配。检查镜像支持的平台:

docker buildx imagetools inspect registry.example.com/ops/demo-api:1.0.0

检查节点架构:

uname -m
kubectl get nodes -o wide

构建很慢

常见原因:

  • 使用 QEMU 模拟非本机架构。
  • 没有配置远端缓存。
  • Dockerfile 中依赖安装层没有充分复用。
  • 每次构建都复制了过多上下文文件。

建议添加 .dockerignore,并把依赖安装层放在源码复制之前。

.git
node_modules
dist
.venv
*.log