使用 buildx 构建多种系统架构支持的 Docker 镜像
在 Docker 19.03+ 版本中可以使用 $ docker buildx build
命令使用 BuildKit
构建镜像。该命令支持 --platform
参数可以同时构建支持多种系统架构的 Docker 镜像,大大简化了构建步骤。
跨 CPU 架构编译程序的方法
通过 binfmt_misc 模拟目标硬件的用户空间
在 Linux 上,
QEMU
除了可以模拟完整的操作系统之外,还有另外一种模式叫用户态模式
(User mod)。该模式下 QEMU 将通过 binfmt_misc 在 Linux 内核中注册一个二进制转换处理程序,并在程序运行时动态翻译二进制文件,根据需要将系统调用从目标 CPU 架构转换为当前系统的 CPU 架构。最终的效果看起来就像在本地运行目标 CPU 架构的二进制文件。
通过 QEMU 的用户态模式,我们可以创建轻量级的虚拟机(chroot 或容器),然后在虚拟机系统中编译程序,和本地编译一样简单轻松。后面我们就会看到,跨平台构建 Docker 镜像用的就是这个方法。
启用 buildx 插件
启用 binfmt_misc
如果你使用的是 Docker 桌面版(MacOS 和 Windows),默认已经启用了 binfmt_misc
,可以跳过这一步。
如果你使用的是 Linux,需要手动启用 binfmt_misc
。大多数 Linux 发行版都很容易启用,不过还有一个更容易的办法,直接运行一个特权容器,容器里面写好了设置脚本:
$ docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
验证是 binfmt_misc 否开启:
[root@centos8~]$ ls -al /proc/sys/fs/binfmt_misc/
total 0
drwxr-xr-x 2 root root 0 Jun 16 18:46 .
dr-xr-xr-x 1 root root 0 Jun 5 21:42 ..
-rw-r--r-- 1 root root 0 Jun 16 19:22 qemu-aarch64
-rw-r--r-- 1 root root 0 Jun 16 19:22 qemu-arm
-rw-r--r-- 1 root root 0 Jun 16 19:22 qemu-ppc64le
-rw-r--r-- 1 root root 0 Jun 16 19:22 qemu-riscv64
-rw-r--r-- 1 root root 0 Jun 16 19:22 qemu-s390x
--w------- 1 root root 0 Jun 16 18:46 register
-rw-r--r-- 1 root root 0 Jun 16 18:46 status
验证是否启用了相应的处理器:
[root@centos8 ~]$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7
mask ffffffffffffff00fffffffffffffffffeffff
注意
请将 Linux 内核版本升级到 4.x 以上,特别是 CentOS7 用户。
centos7 用户可以用以下命令开启
$ docker run --rm --privileged multiarch/qemu-user-static:register --reset
$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags:
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff
$ docker create -it --name dummy multiarch/qemu-user-static:x86_64-aarch64 bash
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6ab622a76dfa multiarch/qemu-user-static:x86_64-aarch64 "bash" 3 minutes ago Created dummy
$ docker cp dummy:/usr/bin/qemu-aarch64-static qemu-aarch64-static
$ ls qemu-aarch64-static
qemu-aarch64-static*
$ docker rm -f dummy
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker run --rm -t -v $(pwd)/qemu-aarch64-static:/usr/bin/qemu-aarch64-static arm64v8/ubuntu uname -m
aarch64docker run --rm --privileged multiarch/qemu-user-static:register --reset
从默认的构建器切换到多平台构建器
由于 Docker 默认的 builder
实例不支持同时指定多个 --platform
,我们必须首先创建一个新的 builder
实例手动替换
docker buildx create --use --name mybuilder
启动构建器:
[root@centos8 ~]$ docker buildx inspect mybuilder --bootstrap
[+] Building 6.2s (1/1) FINISHED
=> [internal] booting buildkit 6.2s
=> => pulling image moby/buildkit:buildx-stable-1 4.6s
=> => creating container buildx_buildkit_mybuilder-new0 1.5s
Name: mybuilder
Driver: docker-container
Nodes:
Name: mybuilder0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6, linux/riscv64
查看当前使用的构建器及构建器支持的 CPU 架构,可以看到支持很多 CPU 架构:
[root@centos8 ~]$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6, linux/riscv64
default docker
default default running linux/amd64, linux/386
构建多平台镜像
新建 Dockerfile 文件,将该应用容器化
FROM --platform=$TARGETPLATFORM alpine
RUN uname -a > /os.txt
CMD cat /os.txt
现在就可以使用 buildx 构建一个支持 arm、arm64 和 amd64 多架构的 Docker 镜像了,同时将其推送到 Docker Hub:
--push
参数表示将构建好的镜像推送到 Docker 仓库。
提前使用docker login
命令登录认证 Docker Hub
$ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t foxchan/hello . --push
# 查看镜像信息
$ docker buildx imagetools inspect foxchan/hello
背后的原理也很简单,之前已经提到过了,buildx 会通过
QEMU
和 binfmt_misc
分别为 3 个不同的 CPU 架构(arm,arm64 和 amd64)构建 3 个不同的镜像。构建完成后,就会创建一个 manifest list,其中包含了指向这 3 个镜像的指针。
如果想将构建好的镜像保存在本地,可以将
type
指定为 docker
,但必须分别为不同的 CPU 架构构建不同的镜像,不能合并成一个镜像,即:
docker buildx build -t hello-arch:arm --platform=linux/arm -o type=docker .
docker buildx build -t hello-arch:arm64 --platform=linux/arm64 -o type=docker .
docker buildx build -t hello-arch:amd64 --platform=linux/amd64 -o type=docker .
测试多平台镜像
由于之前已经启用了 binfmt_misc
,现在我们就可以运行任何 CPU 架构的 Docker 镜像了,因此可以在本地系统上测试之前生成的 3 个镜像是否有问题。
# arm
$ docker run -it --rm hello-arch:arm
Linux buildkitsandbox 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 armv7l Linux
# arm64
$ docker run -it --rm hello-arch:arm64
Linux buildkitsandbox 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 aarch64 Linux
# amd64
$ docker run -it --rm hello-arch:amd64
Linux buildkitsandbox 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 Linux
总结
回顾一下,本文带大家了解了在不同的 CPU 架构上运行软件的挑战性,以及 buildx
如何帮助我们解决了其中的一些挑战。使用 buildx
,我们无需对 Dockerfile 进行任何修改,就可以创建支持多种 CPU 架构的 Docker 镜像,然后将其推送到 Docker Hub。任何安装了 Docker 的系统都可以拉取到与它的 CPU 架构相对应的镜像。
未来 buildx 可能会成为 docker build
命令的一部分,最终所有上面提到的功能都会变成默认的功能。