娥姐 發表於 2026-1-13 08:59:17

Docker多架构镜像构建全过程

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、背景与需求</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1.1 原有方案</a></li><li><a href="#_lab2_0_1">1.2 目标方案</a></li></ul><li><a href="#_label1">二、技术方案</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_2">2.1 核心技术:Docker Buildx</a></li><li><a href="#_lab2_1_3">2.2 构建策略</a></li><li><a href="#_lab2_1_4">2.3 输出格式</a></li></ul><li><a href="#_label2">三、实施过程</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_5">3.1 环境要求</a></li><li><a href="#_lab2_2_6">3.2 创建构建脚本</a></li><li><a href="#_lab2_2_7">3.3 安装 QEMU 模拟器</a></li></ul><li><a href="#_label3">四、问题排查与解决</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_8">4.1 问题一:单架构构建时 docker driver 不支持导出</a></li><li><a href="#_lab2_3_9">4.2 问题二:跨架构构建报 exec format error</a></li><li><a href="#_lab2_3_10">4.3 问题三:Buildx 无法访问 Docker Hub(核心问题)</a></li></ul><li><a href="#_label4">五、最终测试结果</a></li><ul class="second_class_ul"></ul><li><a href="#_label5">六、脚本使用指南</a></li><ul class="second_class_ul"><li><a href="#_lab2_5_11">6.1 基本用法</a></li><li><a href="#_lab2_5_12">6.2 导出离线 tar 包</a></li><li><a href="#_lab2_5_13">6.3 推送到私有仓库</a></li><li><a href="#_lab2_5_14">6.4 输出文件说明</a></li><li><a href="#_lab2_5_15">6.5 目标机器导入</a></li></ul><li><a href="#_label6">七、常见问题 FAQ</a></li><ul class="second_class_ul"><li><a href="#_lab2_6_16">Q1: 首次运行多架构构建很慢?</a></li><li><a href="#_lab2_6_17">Q2: 如何更新镜像加速器地址?</a></li><li><a href="#_lab2_6_18">Q3: 如何清理构建缓存?</a></li><li><a href="#_lab2_6_19">Q4: 多架构包比单架构包小?</a></li></ul><li><a href="#_label7">八、相关文件</a></li><ul class="second_class_ul"></ul><li><a href="#_label8">总结</a></li><ul class="second_class_ul"></ul></ul></div><ul><li>项目:cust-cont (自定义容器服务)</li><li>目标:实现一次构建同时支持 ARM64 和 AMD64 平台的 Docker 镜像</li></ul>
<p class="maodian"><a name="_label0"></a></p><h2>一、背景与需求</h2>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1.1 原有方案</h3>
<p>项目原有两个独立的构建脚本:</p>
<table><thead><tr><th>脚本</th><th>用途</th><th>构建命令</th></tr></thead><tbody><tr><td>docker_build_arm.sh</td><td>构建 ARM 镜像</td><td>docker build</td></tr><tr><td>docker_build_x86.sh</td><td>构建 x86 镜像</td><td>docker build --platform linux/amd64</td></tr></tbody></table>
<p><strong>痛点</strong>:</p>
<ul><li>每次发版需要分别执行两个脚本</li><li>生成两个独立的 tar 包,管理成本高</li><li>无法生成统一的多架构镜像</li></ul>
<p class="maodian"><a name="_lab2_0_1"></a></p><h3>1.2 目标方案</h3>
<p>实现一个统一的构建脚本 <code>docker_build.sh</code>,支持:</p>
<ul><li>一次构建生成多架构镜像(ARM64 + AMD64)</li><li>支持推送到私有仓库或导出离线 tar 包</li><li>通过参数灵活控制构建行为</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>二、技术方案</h2>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>2.1 核心技术:Docker Buildx</h3>
<p>Docker Buildx 是 Docker 官方的多架构构建工具,基于 BuildKit 实现。</p>
<p><strong>架构原理</strong>:</p>
<div class="jb51code"><pre class="brush:bash;">                         ┌─ linux/amd64 镜像层
registration_info:v1.0 ──┤
                         └─ linux/arm64 镜像层
</pre></div>
<p>一个镜像 tag 包含多架构 manifest,部署时 Docker 自动选择对应架构。</p>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>2.2 构建策略</h3>
<table><thead><tr><th>场景</th><th>Driver</th><th>说明</th></tr></thead><tbody><tr><td>单架构构建</td><td>docker (默认)</td><td>使用本地 Docker daemon,可利用本地镜像缓存</td></tr><tr><td>多架构构建</td><td>docker-container</td><td>在独立 BuildKit 容器中构建,支持跨架构</td></tr></tbody></table>
<p class="maodian"><a name="_lab2_1_4"></a></p><h3>2.3 输出格式</h3>
<table><thead><tr><th>模式</th><th>格式</th><th>导入方式</th></tr></thead><tbody><tr><td>单架构导出</td><td>Docker tar</td><td>docker load &lt; image.tar</td></tr><tr><td>多架构导出</td><td>OCI tar</td><td>docker load &lt; image.tar</td></tr><tr><td>推送仓库</td><td>Registry</td><td>docker pull registry/image:tag</td></tr></tbody></table>
<p class="maodian"><a name="_label2"></a></p><h2>三、实施过程</h2>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>3.1 环境要求</h3>
<div class="jb51code"><pre class="brush:bash;"># 检查 Docker 版本(需要 19.03+)
docker version
# 实际版本: 29.1.4

# 检查 Buildx 是否可用
docker buildx version
# 实际版本: v0.30.1
</pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>3.2 创建构建脚本</h3>
<p>创建 <code>docker_build.sh</code>,主要功能:</p>
<div class="jb51code"><pre class="brush:bash;"># 参数解析
--push            # 推送到私有仓库
--save            # 导出为离线 tar 包
--registry &lt;addr&gt;   # 私有仓库地址(默认: 192.168.50.32)
--platform &lt;p&gt;      # 目标平台: amd64, arm64, all(默认: all)
--version &lt;ver&gt;   # 镜像版本(默认: 时间戳)
--skip-build      # 跳过 Maven 构建
</pre></div>
<p class="maodian"><a name="_lab2_2_7"></a></p><h3>3.3 安装 QEMU 模拟器</h3>
<p>在 x86 机器上构建 ARM 镜像需要 QEMU:</p>
<div class="jb51code"><pre class="brush:bash;"># 安装 ARM64 QEMU 模拟器
docker run --rm --privileged tonistiigi/binfmt --install arm64

# 验证支持的架构
# 输出: linux/amd64, linux/arm64, ...
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>四、问题排查与解决</h2>
<p class="maodian"><a name="_lab2_3_8"></a></p><h3>4.1 问题一:单架构构建时 docker driver 不支持导出</h3>
<p><strong>现象</strong>:</p>
<blockquote><p>ERROR: Docker exporter is not supported for the docker driver.</p></blockquote>
<p>​</p>
<p><strong>原因</strong>:</p>
<div class="jb51code"><pre class="brush:bash;">docker buildx build --output type=docker,dest=file.tar</pre></div>
<p>在使用默认 docker driver 时不支持直接导出到文件。</p>
<p><strong>解决方案</strong>:</p>
<p>单架构导出时改用传统的 <code>docker build</code> + <code>docker save</code> 方式:</p>
<div class="jb51code"><pre class="brush:bash;"># 单架构构建
docker build --platform linux/amd64 -t image:tag .
docker save -o image.tar image:tag
</pre></div>
<p class="maodian"><a name="_lab2_3_9"></a></p><h3>4.2 问题二:跨架构构建报 exec format error</h3>
<p><strong>现象</strong>:</p>
<div class="jb51code"><pre class="brush:bash;">exec /bin/sh: exec format error
</pre></div>
<p><strong>原因</strong>:</p>
<p>在 x86 机器上构建 ARM 镜像时,需要 QEMU 模拟器来执行 ARM 二进制文件。</p>
<p><strong>解决方案</strong>:</p>
<div class="jb51code"><pre class="brush:bash;"># 安装 QEMU
docker run --rm --privileged tonistiigi/binfmt --install arm64
</pre></div>
<p class="maodian"><a name="_lab2_3_10"></a></p><h3>4.3 问题三:Buildx 无法访问 Docker Hub(核心问题)</h3>
<p><strong>现象</strong>:</p>
<div class="jb51code"><pre class="brush:bash;">ERROR: failed to do request: Head "https://registry-1.docker.io/v2/...":
dial tcp xxx:443: i/o timeout
</pre></div>
<p><strong>原因分析</strong>:</p>
<table><thead><tr><th>构建方式</th><th>Driver</th><th>网络环境</th><th>镜像缓存</th></tr></thead><tbody><tr><td>docker build</td><td>docker</td><td>宿主机网络</td><td>使用宿主机缓存</td></tr><tr><td>docker buildx (多架构)</td><td>docker-container</td><td>独立容器网络</td><td>独立缓存,需重新拉取</td></tr></tbody></table>
<p>关键区别:</p>
<ul><li><strong>普通 </strong><code>docker build</code> 使用宿主机的 Docker daemon,可以使用 <code>/etc/docker/daemon.json</code> 中配置的镜像加速器</li><li><strong>Buildx docker-container driver</strong> 运行在独立的 BuildKit 容器中,有自己独立的网络和配置,<strong>不会自动使用宿主机的镜像加速器配置</strong></li></ul>
<p><strong>验证步骤</strong>:</p>
<div class="jb51code"><pre class="brush:bash;"># 检查宿主机是否能访问 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
</pre></div>
<p><strong>解决方案</strong>:</p>
<p><strong>步骤一:创建 BuildKit 配置文件</strong></p>
<div class="jb51code"><pre class="brush:bash;">mkdir -p ~/.docker/buildx

cat &gt; ~/.docker/buildx/buildkitd.toml &lt;&lt; 'EOF'

mirrors = ["docker.1ms.run", "docker.xuanyuan.me"]
EOF
</pre></div>
<p><strong>步骤二:创建使用该配置的 Builder</strong></p>
<div class="jb51code"><pre class="brush:bash;"># 删除旧的 builder
docker buildx rm multiarch 2&gt;/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
</pre></div>
<p><strong>验证配置生效</strong>:</p>
<div class="jb51code"><pre class="brush:bash;">File#buildkitd.toml:
&gt;
&gt;   
&gt;   mirrors = ["docker.1ms.run", "docker.xuanyuan.me"]
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>五、最终测试结果</h2>
<table><thead><tr><th>测试场景</th><th>命令</th><th>结果</th><th>输出</th></tr></thead><tbody><tr><td>单架构 AMD64</td><td>--save --platform amd64</td><td>✅ 成功</td><td>*_amd64.tar (300M)</td></tr><tr><td>单架构 ARM64</td><td>--save --platform arm64</td><td>✅ 成功</td><td>*_arm64.tar (324M)</td></tr><tr><td>多架构</td><td>--save</td><td>✅ 成功</td><td>*_multiarch.tar (258M)</td></tr></tbody></table>
<p class="maodian"><a name="_label5"></a></p><h2>六、脚本使用指南</h2>
<p class="maodian"><a name="_lab2_5_11"></a></p><h3>6.1 基本用法</h3>
<div class="jb51code"><pre class="brush:bash;"># 查看帮助
./docker_build.sh --help
</pre></div>
<p class="maodian"><a name="_lab2_5_12"></a></p><h3>6.2 导出离线 tar 包</h3>
<div class="jb51code"><pre class="brush:bash;"># 导出多架构包(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
</pre></div>
<p class="maodian"><a name="_lab2_5_13"></a></p><h3>6.3 推送到私有仓库</h3>
<div class="jb51code"><pre class="brush:bash;"># 推送到默认仓库 (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
</pre></div>
<p class="maodian"><a name="_lab2_5_14"></a></p><h3>6.4 输出文件说明</h3>
<table><thead><tr><th>命令</th><th>输出文件</th><th>格式</th></tr></thead><tbody><tr><td>--save</td><td>images/registration_info_&lt;版本&gt;_multiarch.tar</td><td>OCI</td></tr><tr><td>--save --platform amd64</td><td>images/registration_info_&lt;版本&gt;_amd64.tar</td><td>Docker</td></tr><tr><td>--save --platform arm64</td><td>images/registration_info_&lt;版本&gt;_arm64.tar</td><td>Docker</td></tr></tbody></table>
<p class="maodian"><a name="_lab2_5_15"></a></p><h3>6.5 目标机器导入</h3>
<div class="jb51code"><pre class="brush:bash;"># 导入镜像
docker load &lt; registration_info_&lt;版本&gt;_multiarch.tar

# 查看导入的镜像
docker images | grep registration_info
</pre></div>
<p class="maodian"><a name="_label6"></a></p><h2>七、常见问题 FAQ</h2>
<p class="maodian"><a name="_lab2_6_16"></a></p><h3>Q1: 首次运行多架构构建很慢?</h3>
<p>首次运行需要:</p>
<ul><li>拉取 BuildKit 镜像 (<code>moby/buildkit:buildx-stable-1</code>)</li><li>拉取基础镜像的多架构层</li></ul>
<p>后续构建会使用缓存,速度会快很多。</p>
<p class="maodian"><a name="_lab2_6_17"></a></p><h3>Q2: 如何更新镜像加速器地址?</h3>
<p>编辑配置文件:</p>
<div class="jb51code"><pre class="brush:bash;">vim ~/.docker/buildx/buildkitd.toml
</pre></div>
<p>然后重建 builder:</p>
<div class="jb51code"><pre class="brush:bash;">docker buildx rm multiarch
# 下次运行脚本会自动重建
</pre></div>
<p class="maodian"><a name="_lab2_6_18"></a></p><h3>Q3: 如何清理构建缓存?</h3>
<div class="jb51code"><pre class="brush:bash;"># 清理 buildx 缓存
docker buildx prune

# 清理所有缓存(谨慎使用)
docker buildx prune -a
</pre></div>
<p class="maodian"><a name="_lab2_6_19"></a></p><h3>Q4: 多架构包比单架构包小?</h3>
<p>是的,OCI 格式的多架构包使用了更高效的压缩和层去重,所以可能比两个单架构包加起来更小。</p>
<p class="maodian"><a name="_label7"></a></p><h2>八、相关文件</h2>
<table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td>docker_build.sh</td><td>多架构构建脚本</td></tr><tr><td>docker_build_arm.sh</td><td>原 ARM 构建脚本(保留)</td></tr><tr><td>docker_build_x86.sh</td><td>原 x86 构建脚本(保留)</td></tr><tr><td>~/.docker/buildx/buildkitd.toml</td><td>BuildKit 镜像加速器配置</td></tr><tr><td>Dockerfile</td><td>镜像构建文件</td></tr></tbody></table>
<p>参考资料:</p>
<ul><li><a href="https://docs.docker.com/buildx/working-with-buildx/" rel="external nofollow"   target="_blank">Docker Buildx 官方文档</a></li><li><a href="https://docs.docker.com/build/building/multi-platform/" rel="external nofollow"   target="_blank">Multi-platform images</a></li><li><a href="https://docs.docker.com/build/buildkit/toml-configuration/" rel="external nofollow"   target="_blank">BuildKit 配置</a></li></ul>
<p class="maodian"><a name="_label8"></a></p><h2>总结</h2>
<p>以上为个人经验,希望能给大家一个参考,也希望大家多多支持琼殿技术社区。</p>
頁: [1]
查看完整版本: Docker多架构镜像构建全过程