1 背景
2 解决方案
2.1 QEMU
2.2 Multiple platform nodes
2.3 交叉编译buildx
2.3.1 安装buildx
2.3.2 部署Harbor
2.3.3 配置buildx环境
2.3.4 编写Dockerfile
2.3.5 构建镜像并测试
1、背景
最近在一个国产化项目中遇到了这样一个场景,在同一个 Kubernetes 集群中的节点是混合架构的,也就是说,其中某些节点的 CPU 架构是 x86 的,而另一些节点是 ARM 的。
为了让我们的镜像在这样的环境下运行,一种最简单的做法是根据节点类型为其打上相应的标签,然后针对不同的架构构建不同的镜像,比如 demo:v1-amd64 和 demo:v1-arm64,然后还需要写两套 YAML:一套使用 demo:v1-amd64 镜像,并通过亲和性或 nodeSelector 选择 x86 的节点,另一套使用 demo:v1-arm64 镜像,并通过亲和性或 nodeSelector 选择 ARM 的节点。
很显然,这种做法不仅非常繁琐,而且管理起来也相当麻烦,如果集群中还有其他架构的节点,那么维护成本将成倍增加。
2 解决方案
(1)什么是多架构镜像?
多架构镜像并不是把多个架构的程序打包在一个镜像里,而是通过 Docker Manifest 清单,给同一个镜像标签绑定多个不同 CPU 架构(linux/amd64、linux/arm64)的独立镜像。
当在不同架构服务器拉取镜像时,Docker 会自动识别当前硬件架构,下载对应架构的镜像文件,实现一个镜像地址,全平台通用。
典型场景:
(2)三种解决方案
1)在内核中使用 QEMU 仿真支持
2)直接在对应平台的硬件上构建镜像, 所以需要准备各个平台的主机。因为此方法门槛比较高, 所以并不常使用。
3)使用 Dockerfile 中的多阶段构建, 交叉编译到不同的平台架构中。
① buildx方式
② docker manifest
③ buildx 方式和 docker manifest 区别
buildx
Docker 生态中目前最优的构建方式,目前使用还需要在构建镜像的主机上额外部署插件,然后才能使用,未来预计会集成到docker服务中
https://docs.docker.com/reference/cli/docker/buildx/
docker manifest
https://docs.docker.com/reference/cli/docker/manifest/
官网提示:【实验功能旨在用于测试和反馈,因为它们功能或设计可能会在版本之间更改,恕不另行通知,或者可以在将来的版本中完全删除。】
2.1 QEMU
(1)说明
QEMU 是最简单的构建跨平台镜像策略。它不需要对原有的 Dockerfile 进行任何更改, 会通过 binfmt_misc 这一 Linux 内核功能实现跨平台程序的执行。
QEMU 是一个处理器模拟器, 可以模拟不同的 CPU 架构, 我们可以把它理解为是另一种形式的虚拟机。QEMU 模拟仿真过程中执行非本地架构的二进制文件。例如, 在 x86 主机上构建一个 ARM 镜像时, QEMU 可以模拟 ARM 环境并运行 ARM 二进制文件。
binfmt_misc 是 Linux 内核的一个模块, 它允许用户注册可执行文件格式和相应的解释器。当内核遇到未知格式的可执行文件时, 会使用 binfmt_misc 查找与该文件格式关联的解释器(在这种情况下是 QEMU)并运行文件。
QEMU 和 binfmt_misc 的结合使得通过 buildx 跨平台构建成为可能。这样我们就可以在一个架构的主机上构建针对其他架构的 Docker 镜像, 而无需拥有实际的目标硬件。
(2)实验模拟
1)下载qemu插件
请提前下载好 qemu-aarch64-static.tar.gz
2)安装qemu插件
tar -zxvf qemu-aarch64-static.tar.gzsudo cp qemu-aarch64-static /usr/bin/chmod +x /usr/bin/qemu-aarch64-static
3)注册qemu解释器
docker run --rm --privileged multiarch/qemu-user-static:register --reset
【上面输出是正确的】
4)docker部署测试
## 部署一个ARM容器看看docker run -t \-v /usr/bin/qemu-aarch64-static:/usr/bin/qemu-aarch64-static \arm64v8/ubuntu:20.04 uname -m
docker run -d --name qq \-v /usr/bin/qemu-aarch64-static:/usr/bin/qemu-aarch64-static \arm64v8/ubuntu:20.04 \tail -F /qq/qq
(3)注意
由于qemu本质上是使用x86指令对arm64指令集进行仿真(虚拟机),因此实际运行性能会比真机差很多 (可能耗时差几十甚至上百倍)。
(3)注意
由于qemu本质上是使用x86指令对arm64指令集进行仿真(虚拟机),因此实际运行性能会比真机差很多 (可能耗时差几十甚至上百倍)。
非特殊情况不建议这么使用
2.2 Multiple platform nodes
使用多个不同架构的节点来运行业务,x86架构运行x86的业务,ARM架构运行ARM架构业务。
这也是多架构解决方案之一,也是笨重的方法,不在本次讨论范围内
2.3 交叉编译buildx
2.3.1 安装buildx
(1)说明
在构建镜像时,是通过设置 --platform 参数来指定目标平台的。例如, linux/amd64 、 linux/arm64 或 darwin/amd64 。
默认的构建驱动程序不支持并发多平台构建,一次只能针对一个平台进行构建,x86_64平台只能构建x86_64镜像,ARM平台只能构建ARM镜像。
对多个平台进行构建(例如 --platform=linux/amd64,linux/arm64 ),则需要创建构建器(builder)。
要使用不同的驱动程序,需要使用 Docker Buildx。 Buildx 是下一代构建客户端,它提供与 docker build 命令类似的用户体验,同时支持其他功能,而且未来功能会更多。
(2)环境说明
内核版本:3.10.0-957.el7.x86_64 系统版本:CentOS Linux release 7.6.1810 docker版本:Docker version 20.10.1 网络环境:主机需要联网 docker版本要求:要安装并使用 buildx,需要 Docker Engine 版本号大于等于 19.03。 备注:本次直接使用外网进行构建的,也可以使用内网环境,内网环境需要一些配置,可自行研究 |
(3)下载安装
#下载Buildx
wget https://github.com/docker/buildx/releases/download/v0.14.0/buildx-v0.14.0.linux-amd64
#创建目标文件夹
mkdir -p $HOME/.docker/cli-plugins
#移动文件
mv buildx-v0.14.0.linux-amd64 $HOME/.docker/cli-plugins/docker-buildx
#添加执行权限
chmod +x ~/.docker/cli-plugins/docker-buildx
#检查安装情况

2.3.2 部署Harbor
(1)配置私有仓库
确保构建的镜像都通过push,传到私有仓库中,这里的镜像是多架构的,所以Harbor的版本必须大于v2.0,否则会push失败,这里使用的版本是v2.2.2,满足要求。
配置docker的仓库地址是Harbor的地址
[root@localhost ~]# cat /etc/docker/daemon.json { "oom-score-adjust": -1000, "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "max-concurrent-downloads": 10, "max-concurrent-uploads": 10, "experimental": true, "registry-mirrors": [ "https://7bezldxe.mirror.aliyuncs.com", "http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://hub.docker.com" ], "insecure-registries": ["192.168.25.99"], "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ]}[root@localhost ~]#
(2)创建项目
2.3.3 配置buildx环境
(1)配置私有仓库地址
使用buildx构建镜像,配置仓库地址,除了配置 daemon.json 文件之外还需要配置 /etc/buildkitd.toml 文件
[root@localhost ~]# cat /etc/buildkitd.tomldebug = true# insecure-entitlements allows insecure entitlements, disabled by default.insecure-entitlements = [ "network.host", "security.insecure" ]# optionally mirror configuration can be done by defining it as a registry.[registry."192.168.25.99"] http = true insecure = true[root@localhost ~]#
(2)创建构建器
① 查看默认的构建器
默认的构建器只有构建本主机架构的,没有构建其他架构的,如下所示:
② 创建新的构建器
#### 方式一(不建议创建时使用,排查问题时可这么使用):
docker buildx create --name builderx docker buildx use builderx docker buildx inspect --bootstrap
#### 删除构建器:
docker buildx rm builderx
#### 方式二(建议使用):
若私有仓库非 https 的情况下需指定--driver-opt network=host 和 --config /etc/buildkit/buildkitd.toml 两个参数
docker buildx create --use --bootstrap \ --name mybuilder-http \ --driver-opt network=host \ --driver docker-container \ --platform 'linux/arm64,linux/amd64,linux/amd64/v2,linux/arm/v7,linux/arm/v6,linux/amd64/v3,linux/386' \ --config /etc/buildkitd.toml
#### 注意:
--name:构建器名称,必填
--driver:构建器驱动程序,默认为 docker-container
常用的有两种驱动:docker 与 docker-container: --driver docker,表示使用绑定到 dockerd 的 BuildKit 工具来进行构建操作。但是,由于绑定到 dockerd 的 Buildkit 库使用不同的存储组件,所以该驱动未支持某些特性。比如说,--platform只能指定一个参数,不支持多平台同时构建。 --driver docker-container,表示使用容器运行 BuildKit 工具,在此容器中执行构建任务。而该构建方式产生的镜像,无法直接通过 docker images 查看,需要指定 -output 选项来导出。 |
--driver-opt:驱动程序选项,默认值是 moby/buildkit
创建后会生成一个容器,可查看容器的详细信息
③ 查看创建的构建器
docker buildx inspect mybuilder-http --bootstrap
【* 表示当前正在使用该构建器构建镜像】
2.3.4 编写Dockerfile
(1)在这个构建机上登录Harbor
登录Harbor,不然会push失败,多架构镜像构建,默认会推送到仓库中,不会在本地的docker images中看得到
默认会push到dockerhub中,我们设置了私有仓库,所以会push到私有仓库中
(2)编写dockerfile
[root@localhost buildx]# cat Dockerfile FROM --platform=$TARGETPLATFORM nginxRUN uname -m > /usr/share/nginx/html/index.htmlRUN uname -m >> /usr/share/nginx/html/index.html[root@localhost buildx]#
-----------------------------------------------------------------
TARGETPLATFORM:是 BuildKit 提供的全局变量,是构建镜像的目标架构平台
(3)详解
BuildKit 后端预定义了一组 ARG 全局变量(共 8 个)可供使用, 其定义和说明如下:
在构建镜像时, BuildKit 会将当前所在平台信息传递给 Dockerfile 中的 BUILDPLATFORM 参数(如 linux/arm64)。
我们在docker buildx build时,会使用--platform 参数,docker通过 --platform 参数传递的 linux/arm64,linux/amd64 镜像目标平台列表会依次传递给 TARGETPLATFORM 变量,这样就生成了我们想要的多架构镜像了。
2.3.5 构建镜像并测试
(1)构建镜像
① 构建镜像
docker buildx build --platform linux/amd64,linux/arm64 -t 192.168.25.99/test/nginx-0513:v1 . --push
② 注意事项
在构建时必须添加 --push 或 --load 参数,否则会得到警告WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load。意思是构建结果只保存在缓存中,不会落到主机上,且即使构建完毕,使用 docker images 也不会看到,使用 --push 可以将镜像推送到远程仓库,使用 --load 可以将镜像保存在本地。
需要注意的是,若参数添加的是 --load 不是 --push,且--platform 后面跟了多个平台,如 --platform=linux/amd64,linux/arm64,linux/arm/v7,那么还是会得到报错ERROR: docker exporter does not currently support exporting manifest lists,这是因为本地无法保存多个同名的镜像导致的。
所以如果要保存构建出的多个平台的镜像到本地,就得分多次执行命令,每次只在 --platform 后添加一个平台,并且每次的 --tag 后面的镜像名称都不同。
例如为三个平台构建了镜像,就得分三次执行命令:
docker buildx build \ --builder=mybuilder-http \ --platform=linux/amd64 \ --tag=nginx_amd64:latest \ --load .
docker buildx build \ --builder=mybuilder-http \ --platform=linux/arm/v7 \ --tag=nginx_arm_v7:latest \ --load .
docker buildx build \ --builder=mybuilder-http \ --platform=linux/arm64 \ --tag=nginx_arm64:latest \ --load .
(2)检查Harbor上镜像

我们的镜像支持多平台,拉取镜像的时候会根据我们的平台自动下载对应平台镜像。
默认x86主机上下载的镜像是amd64 默认ARM主机上下载的镜像是arm64 |
(3)查看镜像详情
docker buildx imagetools inspect 192.168.25.99/test/nginx-0513:v1
(4)本地是没有新构建的镜像的
(5)测试
(6)最后完结撒花