Docker buildx 实现跨架构构建与多架构镜像

💡 原文中文,约10600字,阅读约需26分钟。
📝

内容提要

文章介绍在 arm64 构建机与 amd64 生产环境下,用 Docker 实现跨架构构建与运行:借助 QEMU 用户态模拟和 binfmt_misc 注册让容器透明执行异构二进制;用 buildx 一次构建多平台镜像,生成 manifest list 使同一 tag 对应多架构,并推送至私有 Registry 分发;同时记录 containerd 镜像存储、缓存导出等踩坑经验。

🔎

延伸解读

QEMU 模拟的适用边界

文章指出 QEMU 用户态模拟性能比真实环境慢 5~20 倍,因此适合 CI/CD 中偶尔的跨架构构建,但不适合长期以模拟方式运行服务。这意味着在 arm64 构建机上为 amd64 生产环境构建镜像时,模拟只应出现在构建阶段,生产运行仍应使用原生架构。若长期依赖模拟运行,性能损耗会持续存在。

镜像存储格式决定多架构能力

经典镜像存储下,同一个 tag 只能对应一个平台的镜像,因此默认 docker driver 无法直接构建多平台镜像。文章通过开启 containerd 镜像存储(containerd-snapshotter)解决该限制,使一个 tag 可保存多个平台镜像。但切换存储格式后,原有镜像对 Docker 不可见,需提前关闭相关容器并做好回退准备。

两种多架构构建路径的取舍

文章给出两条路径:开启 containerd 镜像存储后直接用默认 buildx 构建;或创建 docker-container driver 的 builder,由独立 BuildKit 容器完成构建。后者构建缓存与 Docker 本身独立,容器删除即丢失,CI 中可用 --cache-to/--cache-from 导出缓存复用。若需彻底绕开 QEMU 模拟,可追加多节点让 buildx 按平台分发原生构建,但需多台构建机与 SSH 互通。

手工合并 manifest 的注意事项

文章演示了手工制作 manifest list:先分别拉取不同平台镜像并立即 docker tag 改名,否则第二次 pull 会替换本地同名 tag。随后用 docker manifest create 合并并 push。过程中需注意 --insecure 参数、No such manifest 报错,以及重复创建时需加 --amend 或先 docker manifest rm 删除已有列表。这些细节在自动化脚本中容易遗漏。

❓

Q&A

在 arm64 构建机上如何让 CI/CD 产出的镜像能在 amd64 生产环境直接运行?

使用 Docker buildx 配合 QEMU 用户态模拟和 binfmt_misc 注册,让构建机能够透明执行 amd64 二进制;再用 buildx 一次构建多平台镜像并生成 manifest list,使同一个 tag 对应多个架构,最后推送到私有 Registry 分发。

QEMU 用户态模拟和 binfmt_misc 在跨架构构建中起什么作用?

QEMU 用户态模拟用于解释执行目标架构的二进制,性能比真实环境慢 5~20 倍,适合 CI/CD 中偶尔的跨架构构建。binfmt_misc 是 Linux 内核机制,通过注册规则将异构二进制透明转交给 QEMU 解释执行,注册后全局生效且对容器透明,需要 --privileged 权限,fix-binary 标志让解释器常驻内存。

如何用 buildx 一次构建多架构镜像并推送到私有仓库?

先确保 Docker 开启 containerd 镜像存储(在 /etc/docker/daemon.json 中设置 features.containerd-snapshotter 为 true),然后使用命令 docker buildx build --platform linux/amd64,linux/arm64 -t 私有仓库地址/镜像名:tag . --push,buildx 会分别构建各平台镜像并自动生成 manifest list 推送。

为什么经典镜像存储下无法直接构建多平台镜像?如何解决?

经典镜像存储(graph driver)使用 Docker 私有格式,同一个 tag 只能对应一个平台的镜像,因此默认的 docker driver 无法构建多平台镜像。解决方法是开启 containerd 镜像存储:修改 /etc/docker/daemon.json 添加 "features": { "containerd-snapshotter": true },重启 Docker 后即可支持一个 tag 对应多个平台。注意切换后原有镜像不可见,回退配置可恢复。

如何手工制作一个多架构镜像并推送到私有 Registry?

步骤:1. 分别拉取不同平台的镜像并立即用 docker tag 重命名到私有仓库名下;2. 推送各单架构镜像到私有仓库;3. 使用 docker manifest create --insecure 私有仓库地址/镜像名:tag 镜像1 镜像2 创建 manifest list;4. 使用 docker manifest push --insecure 推送。之后可用 docker manifest inspect 验证。

使用 docker-container driver 的 builder 构建多架构镜像有什么优势和注意事项?

优势:不依赖 Docker 自身的镜像存储,由独立 BuildKit 容器完成多架构构建,适合无法开启 containerd 镜像存储的场景。注意事项:构建缓存和镜像与 Docker 本身独立,缓存保存在 builder 容器内,容器删除即丢失;CI 中可用 --cache-to/--cache-from 导出缓存到 registry 复用;还支持多节点(--append)按平台分发构建,绕开 QEMU 模拟的慢和崩溃问题,但需要多台构建机和 SSH 互通。

🏷️

标签

➡️

继续阅读