Docker构建多架构镜像

218次阅读
没有评论

共计 6771 个字符,预计需要花费 17 分钟才能阅读完成。

写在前面

随着 ARM 架构服务器的普及和容器化部署的发展, 我们经常需要为不同架构构建镜像。以前这个过程相当繁琐, 需要在不同架构的机器上分别构建。Docker 19.03 引入的 Buildx 插件彻底改变了这个局面, 让跨平台构建变得简单高效。

环境准备

安装 Docker

如果你的系统还没有安装 Docker, 可以使用这个一键安装脚本:

bash <(curl -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/DockerInstallation.sh)

检查 Buildx 是否已安装

Docker 19.03 及以上版本通常已经内置了 Buildx 插件。首先检查当前是否已安装:

docker buildx version

如果能看到版本号输出, 说明已经安装, 可以跳过下面的安装步骤, 直接进入 "配置跨架构模拟环境" 部分。

安装 Buildx 插件(仅当未安装时)

方式一: 通过包管理器安装(推荐)

这是官方推荐的安装方式, 可以自动接收安全更新:

# Debian/Ubuntu 系统
sudo apt-get update
sudo apt-get install docker-buildx-plugin

# RHEL/CentOS 系统
sudo yum install docker-buildx-plugin

# Fedora 系统
sudo dnf install docker-buildx-plugin

Docker Desktop 用户: macOS 和 Windows 用户使用 Docker Desktop 时已经内置 Buildx, 无需额外安装。

方式二: 二进制安装

从 Docker 官方 GitHub 仓库下载最新版 buildx(替换版本号和架构为你的实际情况):

wget https://github.com/docker/buildx/releases/download/v0.30.1/buildx-v0.30.1.linux-amd64

安装并配置二进制文件

# 创建插件目录(确保存在)sudo mkdir -p /usr/libexec/docker/cli-plugins/

# 移动二进制文件,添加执行权限
sudo mv docker-buildx /usr/libexec/docker/cli-plugins/docker-buildx
sudo chmod +x /usr/libexec/docker/cli-plugins/docker-buildx

# 验证文件权限
ls -l /usr/libexec/docker/cli-plugins/docker-buildx

让 Docker 默认使用 buildx 进行构建:

# 启用 buildx 为默认构建器
docker buildx install

# 验证安装
docker buildx version

配置跨架构模拟环境

macOS 和 Windows 的 Docker Desktop 已经内置了跨架构支持, 可以跳过这一步。Linux 用户需要手动配置 QEMU 模拟器:

docker run --privileged --rm tonistiigi/binfmt --install all

这个命令会安装 QEMU 模拟器并配置 binfmt_misc, 让系统能够识别和执行不同架构的二进制文件。

验证配置:

[root@centos ~]# ls -al /proc/sys/fs/binfmt_misc/
total 0
drwxr-xr-x 2 root root 0 Jan  6 11:17 .
dr-xr-xr-x 1 root root 0 Nov 20 09:23 ..
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-aarch64
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-arm
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-loongarch64
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-mips64
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-mips64el
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-ppc64le
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-riscv64
-rw-r--r-- 1 root root 0 Jan  6 11:17 qemu-s390x
--w------- 1 root root 0 Jan  6 11:17 register
-rw-r--r-- 1 root root 0 Jan  6 11:17 status

能看到这些 qemu-* 文件就说明配置成功。

实战场景

场景一: 提取构建产物

在多阶段构建中, 我们经常需要从中间阶段提取编译好的二进制文件。这里有个技巧。

先看一个简单的 Go 程序构建:

FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go

错误做法

直接导出 build 阶段:

docker buildx build --target build --output ./test1 .

这样会把整个 golang:1.21 基础镜像的文件系统都导出来:

bad-output/
  ├── bin/           (bash, sh, 等系统命令)
  ├── usr/           (系统库和工具)
  ├── lib/           (系统库)
  ├── go/            (整个 Go 工具链,几百 MB)
  ├── app/
  │   └── myapp      ← 你真正想要的文件
  └── ... 还有更多系统文件

我们真正需要的只是那个小小的 myapp 可执行文件。

正确做法

使用 FROM scratch 作为过滤层:

FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go

FROM scratch AS export
COPY --from=build /app/myapp /myapp

scratch 是 Docker 官方提供的空镜像, 它不是一个实际存在的镜像, 而是一个特殊的标记, 代表完全空的文件系统。通过它我们可以精确控制导出的内容:

docker buildx build --target export --output ./good-output .

这次输出目录就只有需要的文件了:

good-output/
  └── myapp          ← 完美!只有这一个文件

多阶段构建的意义在于: 构建阶段的所有内容 (Go 编译器、系统文件等) 仅用于编译, 不会进入最终输出。

高级技巧: 自定义目录结构

如果需要组织更复杂的输出结构:

FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go

FROM scratch AS organized
COPY --from=build /app/myapp /bin/myapp
COPY --from=build /app/main.go /src/main.go

执行导出:

docker buildx build --target organized --output ./organized-output .

导出后会得到规范的目录结构:

organized-output/
  ├── bin/
  │   └── myapp
  └── src/
      └── main.go

场景二: 多架构构建

这是 Buildx 最强大的功能。准备好 Dockerfile:

FROM golang:1.21 AS build
WORKDIR /app
RUN echo 'package main; import"fmt"; func main() { fmt.Println("Hello") }' > main.go
RUN CGO_ENABLED=0 go build -o myapp main.go

FROM scratch AS multi-arch
COPY --from=build /app/myapp /myapp

尝试执行多架构导出:

docker buildx build 
  --platform linux/amd64,linux/arm64 
  --target multi-arch 
  --output ./dist 
  .

你会遇到这个错误:

[root@centos sun]# docker buildx build 
>   --platform linux/amd64,linux/arm64 
>   --target multi-arch 
>   --output ./dist 
>   .
[+] Building 0.0s (0/0)                                                                             docker:default
ERROR: Multi-platform build is not supported for the docker driver.
Switch to a different driver, or turn on the containerd image store, and try again.
Learn more at https://docs.docker.com/go/build-multi-platform/

这是因为默认的 docker 驱动只能构建与宿主机架构一致的镜像, 多平台构建需要使用 docker-container 驱动。

创建多平台构建器

# 创建构建器配置目录
mkdir -p /etc/docker/buildx

# 配置私有仓库(如果有的话)
cat > /etc/docker/buildx/config.toml << EOF
[registry."192.168.100.189"]
  http = true
  insecure = true
EOF

# 创建新的构建器
docker buildx create 
  --name multiarch-builder 
  --driver docker-container 
  --config /etc/docker/buildx/config.toml 
  --use

# 查看构建器状态
docker buildx ls

输出结果:

[root@centos sun]# docker buildx ls
NAME/NODE                DRIVER/ENDPOINT                   STATUS     BUILDKIT   PLATFORMS
multiarch-builder*       docker-container                                        
 _ multiarch-builder0    _ unix:///var/run/docker.sock   inactive              
default                  docker                                                  
 _ default               _ default                       running    v0.13.2    linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386

注意 multiarch-builder 的状态是 inactive。这是因为 docker-container 驱动的构建器本质是一个后台运行的 BuildKit 容器, 创建后默认不会自动启动。

启动并初始化构建器:

docker buildx inspect multiarch-builder --bootstrap

同时确保 QEMU 模拟器已安装:

docker run --privileged --rm tonistiigi/binfmt --install all

安装输出:

[root@centos sun]# docker run --privileged --rm tonistiigi/binfmt --install all
Unable to find image 'tonistiigi/binfmt:latest' locally
latest: Pulling from tonistiigi/binfmt
f4700b809f99: Pull complete 
2adec5d296ac: Pull complete 
Digest: sha256:30cc9a4d03765acac9be2ed0afc23af1ad018aed2c28ea4be8c2eb9afe03fbd1
Status: Downloaded newer image for tonistiigi/binfmt:latest
installing: mips64 OK
installing: loong64 OK
installing: arm64 OK
installing: arm OK
installing: ppc64le OK
installing: riscv64 OK
installing: s390x OK
installing: mips64le OK
{
  "supported": [
    "linux/amd64",
    "linux/amd64/v2",
    "linux/amd64/v3",
    "linux/amd64/v4",
    "linux/arm64",
    "linux/riscv64",
    "linux/ppc64le",
    "linux/s390x",
    "linux/386",
    "linux/mips64le",
    "linux/mips64",
    "linux/loong64",
    "linux/arm/v7",
    ...
  ]
}

输出方式对比

1. 导出到本地文件系统

现在重新执行多架构构建:

docker buildx build 
  --platform linux/amd64,linux/arm64 
  --target multi-arch 
  --output ./dist 
  .

输出结果:

[root@centos sun]# tree dist/
dist/
├── linux_amd64
│   └── myapp
└── linux_arm64
    └── myapp

这种方式适合需要手动分发二进制文件的场景。

2. 加载到本地 Docker(仅支持单架构)

注意:--load 只支持单架构构建。

构建 amd64 版本:

docker buildx build 
  --platform linux/amd64 
  -t myimage:amd64 
  --load 
  .

查看结果:

[root@centos sun]# docker images | grep myimage
myimage                                                         amd64                          dbeefa0220ea   7 minutes ago   2.54MB

构建 arm64 版本:

docker buildx build 
  --platform linux/arm64 
  -t myimage:arm64 
  --load 
  .

查看结果:

[root@centos sun]# docker images | grep myimage
myimage                                                         arm64                          1ad9de4d7e24   6 minutes ago   2.51MB
myimage                                                         amd64                          dbeefa0220ea   7 minutes ago   2.54MB

这种方式适合本地测试不同架构的镜像。

3. 推送到镜像仓库(支持多架构)

这是最常用的方式:

docker buildx build 
  --platform linux/amd64,linux/arm64 
  -t 192.168.100.189/test/myimage:v1 
  --push 
  .

一次推送就包含了两个架构的镜像。镜像仓库 (如 Harbor) 只会显示一个镜像条目, 但它实际上是一个 manifest list, 包含了多个架构的镜像索引。这是完全正常的现象。

4. 导出为 OCI 归档

docker buildx build 
  --platform linux/amd64,linux/arm64 
  -t myimage:v3 
  --output type=oci,dest=./myimage.tar 
  .

# 查看导出的文件
ls -lh myimage.tar

这种方式适合离线分发或迁移场景。

实践经验

  1. 版本要求: 确保 Docker 版本不低于 19.03。现代版本已经默认集成 Buildx, 无需额外配置。
  2. 构建器状态管理: docker-container 驱动的构建器是个独立容器, 重启系统后需要重新 bootstrap。
  3. 缓存优化: Buildx 的缓存机制很强大, 合理利用可以大幅提升构建速度。推荐使用 --cache-from--cache-to 参数。
  4. 私有仓库配置: 如果使用私有仓库, 一定要在创建构建器时配置好 config.toml, 否则推送时会失败。
  5. 架构选择: 不是所有基础镜像都支持所有架构。构建前最好先确认基础镜像支持你需要的目标架构。
  6. 性能考虑: 跨架构构建需要 QEMU 模拟, 速度会比原生构建慢很多。生产环境建议使用原生架构的 CI/CD runner。
  7. 安全更新: 生产环境优先通过包管理器安装 Buildx, 可以自动接收安全补丁。

总结

Buildx 让多架构镜像构建变得简单可控。掌握了这些技巧, 无论是导出构建产物还是构建多平台镜像, 都能游刃有余。建议在实际项目中多实践, 找到最适合自己场景的工作流。

正文完
 0
nwnusun
版权声明:本站原创文章,由 nwnusun 于2026-01-06发表,共计6771字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)