Docker buildx 实现跨架构构建与多架构镜像
内容提要
文章介绍在 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 互通。