使用 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 插件

参考之前的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 会通过

QEMUbinfmt_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 命令的一部分,最终所有上面提到的功能都会变成默认的功能。

results matching ""

    No results matching ""