使用远程节点构建多架构 Docker 镜像(按架构类型分发构建任务)

发表于 更新于

前言

使用 Docker 默认的 buildx 设置构建多架构镜像时,往往非常的慢。这是因为 Docker 在背后创建 QEMU 虚拟机来执行构建,一旦模拟差异大的硬件架构性能就会非常差。对于有条件的用户,其实是可以将构建任务分发给不同架构的远程服务器,并且原生就支持。

本文会告诉你怎么做!这是一篇新手向教程。过程十分简单!

准备

假设你本机是 AMD64(或者说 x86)架构,你需要一台 ARM64 的服务器,并且这台服务器可以使用 Docker。

你需要:

  • 在本机和 ARM64 远程服务器安装 Docker
  • 将本机的 SSH 公钥复制到远程服务器(支持免密码登录)

如果你没有 ARM64 服务器仍想尝试本文内容,可以在本地创建虚拟机,将该虚拟机当作远程服务器。

优势

在进行设置之前,我先具体谈谈它的优势:

  • 构建速度非常快,可能会比纯本机构建多架构镜像快 10 倍。一般情况下 QEMU 在 ARM64 和 x86 之间互相模拟时,性能会降低到 1/5 甚至 1/10。
  • 无兼容性故障。纯本机构建时,QEMU 在模拟某些架构是不够成熟的,例如 ARM 模拟 x86 遇到问题的概率很大,代码会莫名其妙的编译失败。反之 x86 模拟 ARM 成熟很多,但也无法避免兼容性问题。

但我要强调一下,使用远程节点不是必要的,毕竟它要付出更高的服务器成本。

后文会统一使用 amd64 这个称呼,而不是 x86x86_64

设置

我们首先要创建自己的构建器实例,并添加多个节点。重点是每一个节点优先构建的架构不同。

创建构建器

创建 multi_node_builder 构建器,并指定一个名为 node_amd64 的本地节点:

docker buildx create \
    --name multi_node_builder \
    --node node_amd64 \
    --platform linux/amd64

注意 --platform 参数,此参数设置了该节点支持的架构为 linux/amd64。输出 multi_node_builder 这个名称表示创建成功,但还未结束。

我们再次执行 buildx create 命令,但加上 --append 选项表示这是一个追加设置。添加名为 node_arm64 的远程节点到 multi_node_builder 构建器:

docker buildx create \
    --name multi_node_builder \
    --append \
    --node node_arm64 \
    --platform linux/arm64,linux/arm64/v8,linux/arm/v7,linux/arm/v6,linux/arm/v5 \
    ssh://root@10.88.50.212

这里的 10.88.50.212 是一个示例 IP,它实际上是我在本地创建的 LXC 容器。

注意 --platform 参数,此参数设置了该节点支持的架构为 linux/arm64 和一些其它 ARM 架构版本。同样的,输出 multi_node_builder 这个名称表示创建成功。

这里的 linux/arm64linux/arm64/v8 指的是同一个架构,而 armv5v7 则各不相同。考虑到 Debian 镜像最低支持到 arm/v5 而 Alpine 支持 arm/v6,所以此处我选择明确列出这些架构。

启用构建器

执行以下命令启用构建器:

docker buildx use multi_node_builder
docker buildx inspect --bootstrap

成功输出如下:

Name:          multi_node_builder
Driver:        docker-container
Last Activity: 2024-07-02 18:15:49 +0000 UTC

Nodes:
Name:     node_amd64
Endpoint: unix:///var/run/docker.sock
Error:    Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.45/containers/buildx_buildkit_node_amd64/json": context deadline exceeded

Name:     node_arm64
Endpoint: ssh://root@10.88.50.212
Error:    Get "http://docker.example.com/v1.45/containers/buildx_buildkit_node_arm64/json": context deadline exceeded
第二个命令会自动在这两个节点上部署 moby/buildkit 服务,请不要删除它们。

查看构建器

使用以下命令查看当前的构建器实例列表:

docker buildx ls

输出如下:

NAME/NODE             DRIVER/ENDPOINT                   STATUS    BUILDKIT               PLATFORMS
multi_node_builder*   docker-container
 \_ node_amd64         \_ unix:///var/run/docker.sock   running   v0.14.1                linux/amd64*, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
 \_ node_arm64         \_ ssh://root@10.88.50.212       running   v0.14.1                linux/arm64*, linux/arm/v7*, linux/arm/v6*, linux/arm/v5*, linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
default               docker
 \_ default            \_ default                       running   v0.11.7+435cb77e369c   linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386

注意看 multi_node_builder 后面有个星号,代表当前默认使用的构建器实例。此实例有两个节点,一个是本地节点(node_amd64),另一个是远程节点(node_arm64)。

理想情况下,我们的远程节点应该是 ARM64 架构的服务器。此处我为了举例,使用了一个本地的容器替代。

你可能会疑惑,为什么这两个节点的 PLATFORMS 字段都有一些与设置时不相关的架构?这是因为 BuildKit 会自动检测更多能支持的平台,而我们在添加节点时设置的架构会固定在最前面(注意星号),它们的优先级最高。

测试构建

我以自己的博客为例子,它有一个独立可用的 Dockerfile

docker buildx build . --platform=linux/amd64,linux/arm64/v8 -t multiarch/blog --load

我指定了两个架构的 --platform 参数值,分别对应 multi_node_builder 构建器实例中的两个节点各自优先的架构。现在分别看本地和远程节点的 htop 输出,会发现两个节点在同时构建,且构建的架构各不相同。

此时我们已实现了目标:

  • 多个服务器并行构建多架构的镜像。
  • 不同平台的构建分发给最适合的服务器,而不是用性能缓慢的跨架构 QEMU 模拟器。

结束语

这已经是多架构镜像构建的最佳方法之一了,如果有条件强烈推荐使用。对于个人的话可能买 ARM64 服务器用来构建不太划算,你可以继续在本机构建多架构镜像,一般来讲遇到的问题都可以克服(就是速度特别慢)。

作者头像 一点点入门知识 打赏作者
本文由作者按照 CC BY 4.0 进行授权
分享:

相关文章