Docker多架构镜像构建全过程
作者:techzhi
文章介绍了如何使用DockerBuildx工具实现一次构建同时支持ARM64和AMD64平台的Docker镜像,并详细描述了构建过程、技术方案和问题排查,最终,通过脚本使用指南和常见问题解答,帮助用户更好地理解和使用该工具
- 项目:cust-cont (自定义容器服务)
- 目标:实现一次构建同时支持 ARM64 和 AMD64 平台的 Docker 镜像
一、背景与需求
1.1 原有方案
项目原有两个独立的构建脚本:
| 脚本 | 用途 | 构建命令 |
|---|---|---|
| docker_build_arm.sh | 构建 ARM 镜像 | docker build |
| docker_build_x86.sh | 构建 x86 镜像 | docker build --platform linux/amd64 |
痛点:
- 每次发版需要分别执行两个脚本
- 生成两个独立的 tar 包,管理成本高
- 无法生成统一的多架构镜像
1.2 目标方案
实现一个统一的构建脚本 docker_build.sh,支持:
- 一次构建生成多架构镜像(ARM64 + AMD64)
- 支持推送到私有仓库或导出离线 tar 包
- 通过参数灵活控制构建行为
二、技术方案
2.1 核心技术:Docker Buildx
Docker Buildx 是 Docker 官方的多架构构建工具,基于 BuildKit 实现。
架构原理:
┌─ linux/amd64 镜像层
registration_info:v1.0 ──┤
└─ linux/arm64 镜像层
一个镜像 tag 包含多架构 manifest,部署时 Docker 自动选择对应架构。
2.2 构建策略
| 场景 | Driver | 说明 |
|---|---|---|
| 单架构构建 | docker (默认) | 使用本地 Docker daemon,可利用本地镜像缓存 |
| 多架构构建 | docker-container | 在独立 BuildKit 容器中构建,支持跨架构 |
2.3 输出格式
| 模式 | 格式 | 导入方式 |
|---|---|---|
| 单架构导出 | Docker tar | docker load < image.tar |
| 多架构导出 | OCI tar | docker load < image.tar |
| 推送仓库 | Registry | docker pull registry/image:tag |
三、实施过程
3.1 环境要求
# 检查 Docker 版本(需要 19.03+) docker version # 实际版本: 29.1.4 # 检查 Buildx 是否可用 docker buildx version # 实际版本: v0.30.1
3.2 创建构建脚本
创建 docker_build.sh,主要功能:
# 参数解析 --push # 推送到私有仓库 --save # 导出为离线 tar 包 --registry <addr> # 私有仓库地址(默认: 192.168.50.32) --platform <p> # 目标平台: amd64, arm64, all(默认: all) --version <ver> # 镜像版本(默认: 时间戳) --skip-build # 跳过 Maven 构建
3.3 安装 QEMU 模拟器
在 x86 机器上构建 ARM 镜像需要 QEMU:
# 安装 ARM64 QEMU 模拟器 docker run --rm --privileged tonistiigi/binfmt --install arm64 # 验证支持的架构 # 输出: linux/amd64, linux/arm64, ...
四、问题排查与解决
4.1 问题一:单架构构建时 docker driver 不支持导出
现象:
ERROR: Docker exporter is not supported for the docker driver.
原因:
docker buildx build --output type=docker,dest=file.tar
在使用默认 docker driver 时不支持直接导出到文件。
解决方案:
单架构导出时改用传统的 docker build + docker save 方式:
# 单架构构建 docker build --platform linux/amd64 -t image:tag . docker save -o image.tar image:tag
4.2 问题二:跨架构构建报 exec format error
现象:
exec /bin/sh: exec format error
原因:
在 x86 机器上构建 ARM 镜像时,需要 QEMU 模拟器来执行 ARM 二进制文件。
解决方案:
# 安装 QEMU docker run --rm --privileged tonistiigi/binfmt --install arm64
4.3 问题三:Buildx 无法访问 Docker Hub(核心问题)
现象:
ERROR: failed to do request: Head "https://registry-1.docker.io/v2/...": dial tcp xxx:443: i/o timeout
原因分析:
| 构建方式 | Driver | 网络环境 | 镜像缓存 |
|---|---|---|---|
| docker build | docker | 宿主机网络 | 使用宿主机缓存 |
| docker buildx (多架构) | docker-container | 独立容器网络 | 独立缓存,需重新拉取 |
关键区别:
- 普通
docker build使用宿主机的 Docker daemon,可以使用/etc/docker/daemon.json中配置的镜像加速器 - Buildx docker-container driver 运行在独立的 BuildKit 容器中,有自己独立的网络和配置,不会自动使用宿主机的镜像加速器配置
验证步骤:
# 检查宿主机是否能访问 Docker Hub curl -s --connect-timeout 5 https://registry-1.docker.io/v2/ # 返回 UNAUTHORIZED 说明可以连接 # 查看宿主机镜像加速器配置 cat /etc/docker/daemon.json # 已配置: docker.1ms.run, docker.xuanyuan.me
解决方案:
步骤一:创建 BuildKit 配置文件
mkdir -p ~/.docker/buildx cat > ~/.docker/buildx/buildkitd.toml << 'EOF' [registry."docker.io"] mirrors = ["docker.1ms.run", "docker.xuanyuan.me"] EOF
步骤二:创建使用该配置的 Builder
# 删除旧的 builder docker buildx rm multiarch 2>/dev/null || true # 创建新的 builder,关键参数: # --driver-opt network=host 使用宿主机网络 # --config 指定 buildkit 配置文件 docker buildx create \ --name multiarch \ --driver docker-container \ --driver-opt network=host \ --config ~/.docker/buildx/buildkitd.toml \ --use # 启动并验证 docker buildx inspect --bootstrap
验证配置生效:
File#buildkitd.toml: > [registry] > [registry."docker.io"] > mirrors = ["docker.1ms.run", "docker.xuanyuan.me"]
五、最终测试结果
| 测试场景 | 命令 | 结果 | 输出 |
|---|---|---|---|
| 单架构 AMD64 | --save --platform amd64 | ✅ 成功 | *_amd64.tar (300M) |
| 单架构 ARM64 | --save --platform arm64 | ✅ 成功 | *_arm64.tar (324M) |
| 多架构 | --save | ✅ 成功 | *_multiarch.tar (258M) |
六、脚本使用指南
6.1 基本用法
# 查看帮助 ./docker_build.sh --help
6.2 导出离线 tar 包
# 导出多架构包(ARM64 + AMD64) ./docker_build.sh --save # 导出指定版本 ./docker_build.sh --save --version 1.2.0 # 只导出 x86 架构 ./docker_build.sh --save --platform amd64 # 只导出 ARM 架构 ./docker_build.sh --save --platform arm64 # 跳过 Maven 构建(jar 包已存在时) ./docker_build.sh --save --skip-build
6.3 推送到私有仓库
# 推送到默认仓库 (192.168.50.32) ./docker_build.sh --push # 推送到指定仓库 ./docker_build.sh --push --registry 10.0.0.100 # 指定版本推送 ./docker_build.sh --push --version 1.2.0
6.4 输出文件说明
| 命令 | 输出文件 | 格式 |
|---|---|---|
| --save | images/registration_info_<版本>_multiarch.tar | OCI |
| --save --platform amd64 | images/registration_info_<版本>_amd64.tar | Docker |
| --save --platform arm64 | images/registration_info_<版本>_arm64.tar | Docker |
6.5 目标机器导入
# 导入镜像 docker load < registration_info_<版本>_multiarch.tar # 查看导入的镜像 docker images | grep registration_info
七、常见问题 FAQ
Q1: 首次运行多架构构建很慢?
首次运行需要:
- 拉取 BuildKit 镜像 (
moby/buildkit:buildx-stable-1) - 拉取基础镜像的多架构层
后续构建会使用缓存,速度会快很多。
Q2: 如何更新镜像加速器地址?
编辑配置文件:
vim ~/.docker/buildx/buildkitd.toml
然后重建 builder:
docker buildx rm multiarch # 下次运行脚本会自动重建
Q3: 如何清理构建缓存?
# 清理 buildx 缓存 docker buildx prune # 清理所有缓存(谨慎使用) docker buildx prune -a
Q4: 多架构包比单架构包小?
是的,OCI 格式的多架构包使用了更高效的压缩和层去重,所以可能比两个单架构包加起来更小。
八、相关文件
| 文件 | 说明 |
|---|---|
| docker_build.sh | 多架构构建脚本 |
| docker_build_arm.sh | 原 ARM 构建脚本(保留) |
| docker_build_x86.sh | 原 x86 构建脚本(保留) |
| ~/.docker/buildx/buildkitd.toml | BuildKit 镜像加速器配置 |
| Dockerfile | 镜像构建文件 |
参考资料:
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
