告别繁琐编译,docker buildx 让你在任意平台构建任意架构的镜像

有时候,我们需要在 ARM 架构的平台构建 x86 架构的镜像,这时就可以使用 docker 在 19.03 版本引入的 docker buildx 特性。

[!info] Buildx
在 Docker 19.03 版本中,官方引入了一个强大的新功能 buildx,它是一个 Docker CLI 插件,通过集成 Moby BuildKit 提供了扩展的构建能力。123 其中最核心的功能之一就是支持跨平台构建,让你可以在单一主机上(例如 x86)构建出能在多种架构(例如 ARM64)上运行的镜像。低于 19.03 版本的话,需要另外想办法。

1.1 使用 Buildx 功能的先决条件

1.1.1 Docker 版本

Docker Engine 版本必须是 19.03 或更高。

1.1.2 开启 Docker CLI 实验性功能

在 Docker 19.03 中,buildx 作为一个实验性功能提供。

要开启 docker buildx 功能,不仅 docker engine 要开启『实验性』功能,docker cli 也需要开启。

在 docker 20.03 中,『实验性』功能是默认开启的,不需要任何操作,就可以直接使用,但是如果低于 20.03,则需要手动开启。

docker version 查看是否已经开启:

image-20230310225029172

如果没有开启,则需要在配置中对其进行开启。

开启 docker-cli 的实验性功能

家目录下的 .docker/config.json(如果没有该文件,则自行创建)中加入:

1
2
3
{
"experimental": "enabled"
}

开启 docker-engine 的实验性功能

在 daemon.json 中加入:

1
2
3
{
"experimental": true
}

1.1.3 安装 QEMU 模拟器

为了在主机上模拟其他 CPU 架构(例如在 x86 主机上模拟 ARM),需要 QEMU 的支持。Docker Desktop for Mac 和 Windows 已经内置了这项功能。但在 Linux 上,需要通过运行一个特权容器来安装和注册 QEMU 处理器模拟器:

1
2
3
4
5
# 使用 tonistiigi/binfmt
docker run --privileged --rm tonistiigi/binfmt --install all

# 或者使用 multiarch/qemu-user-static
docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes

1.2 通过 Builder 进行验证

[!info] 信息
不同的 builder 之间相互隔离,可以确保环境不被污染。

验证 buildx 是否可用:

1
docker buildx version

创建新的 Builder 实例

buildx 允许我们创建多个独立的 builder 实例。创建一个新的 builder 可以提供一个干净且隔离的构建环境。

1
docker buildx create --name mybuilder --use --driver docker-container
  • --name mybuilder: 为 builder 指定一个名字(例如 mybuilder),方便后续管理。
  • --use: 创建后立即切换到这个新的 builder
  • --driver:指定驱动

启动并检查 Builder:

创建 builder 后,检查它的状态并确保它支持需要的平台。--bootstrap 参数会确保 builder 实例处于可运行状态。

1
docker buildx inspect --bootstrap

本地构建镜像并推送:

1
docker buildx build --platform linux/arm, linux/arm64, linux/amd64 -t test/arch --push -f ./dockerfile .

1.3 开机自动安装 QEMU 模拟器(最佳实践)

在 Linux 机器上,通过 docker run --privileged --rm tonistiigi/binfmt --install all 命令安装的 QEMU 模拟器注册信息在系统重启后会失效。

这个命令的本质是向 Linux 内核的 binfmt_misc 模块注册一系列规则。这些规则告诉内核,当遇到特定架构(如 ARM64)的可执行文件时,应该使用哪个解释器(即 QEMU 模拟器)来运行它。

这些注册信息是保存在一个特殊的、基于内存的虚拟文件系统中的(通常挂载在 /proc/sys/fs/binfmt_misc)。因为它是虚拟文件系统,其内容并不会被保存到硬盘上。当你的 Linux 系统重启时,内核会重新初始化,这个虚拟文件系统也会被重置为空,所有之前注册的规则都会丢失。

因此,每次重启后,buildx 会因为找不到执行非本地架构二进制文件的模拟器而无法进行跨平台构建。

为了解决这个问题,需要在系统每次启动时自动重新运行注册命令。在现代使用 systemd 的 Linux 发行版(如 CentOS 7+, Ubuntu 16.04+, Debian 8+)上,最佳实践是创建一个 systemd 服务。

这样可以确保在 Docker 服务启动之后,自动完成 QEMU 的注册。

创建一个新的服务单元文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo vi /etc/systemd/system/register-qemu-binfmt.service

# 内容如下:
[Unit]
Description=Register QEMU binfmt_misc handlers
# 确保在 Docker 服务启动后才执行此服务
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
# 使用绝对路径来运行 docker 命令
ExecStart=/usr/bin/docker run --privileged --rm tonistiigi/binfmt --install all
RemainAfterExit=yes

[Install]
# 表明这个服务应该在多用户目标(标准运行级别)下被启用
WantedBy=multi-user.target

保存并关闭文件后,执行以下命令来让 systemd 加载并启用这个新服务:

1
2
3
4
5
6
7
8
# 1. 重新加载 systemd 管理器配置
sudo systemctl daemon-reload

# 2. 启用服务,使其开机自启
sudo systemctl enable register-qemu-binfmt.service

# 3. 立即启动服务以完成本次注册(无需重启)
sudo systemctl start register-qemu-binfmt.service

可以检查服务的状态来确认它是否成功运行:

1
sudo systemctl status register-qemu-binfmt.service

如果一切正常,会看到 active (exited) 的状态,并且日志中没有错误信息。

现在, QEMU 模拟器就已经被配置为开机自动注册了。即使重启机器,docker buildx 的多架构构建环境也会保持可用,无需任何手动干预。这是一个一劳永逸的解决方案。