Docker buildx 多架构镜像实战:一次构建 amd64+arm64,告别 exec format error

Docker buildx 多架构镜像实战:一次构建 amd64+arm64,告别 exec format error

你在 M 系列 Mac(arm64)上 docker build 打了个镜像,推到仓库,部署到云上的 x86 服务器(amd64),容器起不来,日志一行:

复制代码
exec /app: exec format error

或者反过来:在 x86 CI 上构建的镜像,拉到树莓派、arm 云主机上跑不了。根因是镜像里的二进制是按构建机的 CPU 架构编译的,架构不匹配,内核根本没法执行。

解法不是"给每个架构各建一个仓库",而是用 docker buildx 一次构建出多架构镜像 ,推成一个 manifest list。用户 docker pull 同一个 tag,Docker 会自动挑对应自己 CPU 架构的那份。这篇手把手把它跑通。

先理解:为什么普通 build 是"单架构"的

docker build 默认只为当前机器的架构构建。你可以查镜像的架构:

bash 复制代码
docker inspect --format '{{.Architecture}}' myapp:latest
# arm64   ← 在 M1 Mac 上就是这个

推上去后,x86 服务器拉下来,架构对不上,exec format error。你或许试过在服务器上加 --platform:

bash 复制代码
docker run --platform linux/amd64 myapp:latest   # 靠 QEMU 模拟,能跑但巨慢

模拟能应急,但性能差、也不该用在生产。真正的解法是构建期就把两个架构都产出来。

一次性准备:开启 buildx 和 QEMU

buildx 是 Docker 官方的增强构建器,基于 BuildKit,新版 Docker Desktop / Docker Engine 已自带。要在一台机器上构建"非本机架构"的镜像,得靠 QEMU 做跨架构模拟编译。先注册 QEMU:

bash 复制代码
# 注册 binfmt handlers,让本机能模拟执行其它架构的二进制
docker run --privileged --rm tonistiigi/binfmt --install all

然后创建一个支持多架构的 builder 实例(默认的 builder 不支持多平台输出):

bash 复制代码
# 创建并切换到一个新的 builder
docker buildx create --name multiarch --driver docker-container --use

# 启动并查看它支持哪些平台
docker buildx inspect --bootstrap

inspect 输出里 Platforms: 那行应该能看到 linux/amd64, linux/arm64, linux/arm/v7 ...。看到 amd64 和 arm64 就够了。

这里有个关键点:--driver docker-container。默认的 docker driver 不支持多平台构建 ,必须用 docker-container 驱动(它跑一个独立的 BuildKit 容器)。很多人第一次卡在"构建报错说不支持多 platform",就是因为没换 driver。

核心命令:一条命令建两个架构并推送

bash 复制代码
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/myapp:1.0.0 \
  --push \
  .

三个关键参数:

  • --platform linux/amd64,linux/arm64:一次构建这两个架构。
  • --push:构建完直接推到仓库。多架构构建必须直接 push ,不能只 --load 到本地------因为本地 Docker 镜像存储没法存"一个 tag 对应多架构"的 manifest list,只有仓库能存。这是第二个高频坑:多平台构建加 --load 会报错,必须 --push(或 --output 到 tar)。
  • 结尾的 .:构建上下文。

推完后验证一下 manifest,确认两个架构都在:

bash 复制代码
docker buildx imagetools inspect registry.example.com/myapp:1.0.0

输出会列出一个 manifest list,底下挂着 platform: linux/amd64 和 platform: linux/arm64 两个子 manifest。之后不管是 x86 服务器还是 arm 机器,docker pull registry.example.com/myapp:1.0.0 都会自动拉对的那份,exec format error 彻底消失。

Dockerfile 要注意的:别把架构写死

多架构构建对 Dockerfile 有要求------凡是"下载某架构专属二进制"的地方,都不能写死架构。BuildKit 会自动注入几个构建参数,用它们:

dockerfile 复制代码
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:1.22 AS builder

# BuildKit 自动提供这些 ARG,声明后即可使用
ARG TARGETOS
ARG TARGETARCH

WORKDIR /src
COPY . .
# 关键:用 TARGETOS/TARGETARCH 交叉编译出目标架构的二进制
# 这样构建在 amd64 机器上、也能产出 arm64 的可执行文件,不走 QEMU 更快
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /app .

FROM alpine:3.20
COPY --from=builder /app /app
ENTRYPOINT ["/app"]

几个要点:

  • $BUILDPLATFORM 是构建机 的架构,$TARGETPLATFORM/$TARGETOS/$TARGETARCH 是目标 架构。FROM --platform=$BUILDPLATFORM 让 builder 阶段始终跑在本机架构上(快),再靠 Go 的交叉编译产出目标架构二进制。这比让 QEMU 模拟整个编译过程快得多。
  • ARG TARGETOS / ARG TARGETARCH 必须显式声明 才能在 RUN 里用,否则是空值。
  • 如果你下载的是预编译工具(比如 kubectl、某个 CLI),记得用 $TARGETARCH 拼下载 URL,别写死 amd64:
dockerfile 复制代码
ARG TARGETARCH
RUN curl -fsSL "https://example.com/tool-linux-${TARGETARCH}" -o /usr/local/bin/tool \
    && chmod +x /usr/local/bin/tool

对非 Go 项目(Node、Python 等解释型),通常不涉及交叉编译,直接 --platform 多架构构建、让基础镜像自己按架构拉对应版本即可,更省心。

放进 CI:GitHub Actions 示例

日常最实用的是让 CI 自动出多架构镜像。官方 action 已经把 QEMU、buildx 都封装好了:

yaml 复制代码
name: build-and-push
on:
  push:
    tags: ["v*"]

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to registry
        uses: docker/login-action@v3
        with:
          registry: registry.example.com
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Build and push multi-arch
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: registry.example.com/myapp:${{ github.ref_name }}
          cache-from: type=gha         # 用 GitHub Actions 缓存加速
          cache-to: type=gha,mode=max

cache-from/cache-to: type=gha 会把 BuildKit 层缓存存进 GitHub Actions 缓存,第二次构建能省掉没变的层,多架构构建本来慢,这个缓存很关键。

小结

  • exec format error 的根因 是镜像架构和运行机器的 CPU 架构不匹配;解法是构建成多架构镜像 (manifest list),让 docker pull 自动选对架构。
  • 准备工作:binfmt 注册 QEMU + docker buildx create --driver docker-container --use(默认 docker driver 不支持多平台)。
  • 构建命令:docker buildx build --platform linux/amd64,linux/arm64 -t ... --push .。多架构必须 --push,不能 --load(本地存储存不下 manifest list)。
  • Dockerfile 里用 $BUILDPLATFORM + $TARGETOS/$TARGETARCH 交叉编译,别把架构写死;下载预编译二进制时用 $TARGETARCH 拼 URL。
  • CI 用官方 setup-qemu-action + build-push-action,配 cache-from/to: type=gha 提速。

一句话记住:用 buildx 一次产出多架构、推成一个 tag,让每台机器各取所需------这才是彻底根治 exec format error 的正道。

相关推荐
小白鸽i2 分钟前
【linux】麒麟桌面版安装人大金仓V8
linux·运维
Dawson Zhu2 分钟前
《Agentic Design Patterns》第 19 章导读:评估与监控
人工智能·语言模型·架构·aigc·agi
vivo互联网技术6 分钟前
Agent 不能只记录调用了什么,还要记录为什么调用 | KDC 系列 03
架构·agent
Ruiery7 分钟前
Linux 6.6内核内存管理深度解析(三):页表与缺页处理 — 虚拟地址怎么按需映射到物理页
linux·运维·服务器
研发小能8 分钟前
DevOps 流水线怎么选?自研、开源拼装还是企业级平台
运维·开源·devops·嘉为蓝鲸
rannn_1119 分钟前
项目缓存实战:热点 Key 击穿、缓存穿透与多级缓存架构
java·缓存·架构
迪康Defender15 分钟前
终端安全行为分析:审计日志如何转化为业务报表?落地实践与避坑指南
运维·经验分享·安全·信息可视化·自动化·安全威胁分析·运维开发
Dawson Zhu17 分钟前
《Agentic Design Patterns》第 20 章导读:优先级排序
人工智能·语言模型·架构·aigc·agi
专注API从业者27 分钟前
告别手工盯盘:用 Open‑Claw 搭建电商商品自动化监控与数据分析实战
运维·数据结构·数据挖掘·数据分析·自动化
Rebecca_aLi29 分钟前
码匠教育:从手工表格到脚本自动化,Python 改造工作流的思路
运维·python·自动化