背景
- 在国产化和信创的潮流下,应用程序容器镜像支持多 CPU 架构的需求已经很普遍。常见的做法是为不同平台单独构建一个版本,或者分别构建不同架构的镜像再利用
docker manifest
的能力来做镜像合并 。当用来开发的平台与部署的目标平台不同时,实现这一目标并不容易,例如在 x86 架构上开发一个应用程序并将其部署到 ARM 平台的机器上,通常需要准备 ARM 平台的基础设施用于编译打包。在这个场景下,一次构建多处部署的必要性就比较高了,在没有一个成熟的 CI 流水线的情况下,利用docker buildx
构建跨平台的镜像也是一种比较高效的解决方案。
前置知识
镜像 Registry 对多系统架构镜像的支持
目前大部分镜像托管平台(如 dockerhub、各个公有云的容器镜像服务以及开源镜像仓库工具 harbor)都支持多系统架构镜像,以下图
dockerhub 平台的 golang
镜像为例,单个镜像 tag golang:latest
就包含了 10 种不同系统架构的镜像。
Docker 对多系统架构镜像的管理
- Docker 使用
manifest
组来管理多 CPU 架构的镜像,manifest
是一个 JSON 文件,指容器镜像的元数据文件,一个manifest
对应一个镜像 tag 下一个CPU 架构的镜像。 - 一个
manifest
组的文件并不直接表示镜像信息,而是使用了一个列表指向了该清单中包含的多份子清单文件,每一份子清单文件均表示一种架构的镜像清单。 - 查看一个镜像的manifest 列表
docker manifest inspect golang:latest
结果:
多系统架构镜像的使用
我们通过 docker pull
或者 docker run
进行镜像的拉取或使用时,docker 会自动拉取匹配当前系统架构的镜像。
# 镜像拉取
docker pull golang:latest
# 镜像 manifest 查看
docker inspect golang:latest
# 只看 CPU 架构
docker inspect golang:latest | grep Architecture
- ARM 平台
- linux X86 平台
Docker Buildx
简介
buildx
是一个 Docker CLI 插件,用于使用 BuildKit 扩展构建功能。使用docker buildx
CLI 插件可以轻松构建多系统架构 Docker 镜像。
版本要求
- 在
Docker Engine 19.03
及以上版本,可以安装和使用buildx
插件,低版本 Docker Engine 可能会出现兼容性的问题。
安装
Windows 或者 MacOS
- 参考
Docker Buildx
默认包含在适用于 Windows 和 macOS 的 Docker Desktop 中 。
Linux
- 参考 Docker Engine 安装文档,
Docker Engine
软件包存储库包含Docker Buildx
软件包,安装 docker-buildx-plugin 包以安装 Buildx 插件。
# centos
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
# ubuntu
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- 启用
binfmt_misc
binfmt_misc 是 Linux 内核的一个模块,全称是混杂二进制格式的内核支持(Kernel Support for miscellaneous Binary Formats) ,它允许用户在 Linux 系统上注册一些特定格式的二进制文件,并指定相应的解释器进行解释和执行。 这些注册的文件类型可以是任何格式,例如 Windows 可执行文件、Java 类文件或 Python 脚本等,而解释器可以是任何可执行程序,例如 shell、Python 解释器或者 Java 虚拟机等。通过 binfmt_misc,用户可以在 Linux 系统上方便地运行各种不同的二进制文件,从而提高了系统的灵活性和可用性。
Docker Desktop 版本不需要执行此项,默认就是启用的。
docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
验证
docker buildx version
# 输出示例:github.com/docker/buildx v0.11.2-desktop.1 986ab6afe790e25f022969a18bc0111cff170bc2
多架构镜像构建
构建器 builder
docker buildx
通过 builder
实例对象来管理构建配置和节点,命令行将构建任务发送至 builder
实例,再由 builder
指派给符合条件的节点执行。
我们可以基于同一个 docker 服务程序创建多个 builder 实例,提供给不同的项目使用以隔离各个项目的配置。
- 查看构建器列表
docker buildx ls
# 在结果列表中可以看到目前使用的构建器及其支持的架构,如果目前使用的构建器支持我们需要的架构,则不需要创建新的构建器
# 示例
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock running linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default docker
default default running linux/amd64, linux/386
- 创建新的构建器
docker buildx create --use --name mybuilder
- 检查和启动
docker buildx inspect mybuilder --bootstrap
镜像构建
- Dockerfile 示例
FROM nginx:latest
CMD ["nginx", "-g", "daemon off;"]
- 进入 Dockerfile 所在目录,执行如下示例的命令,就可以构建自己指定的架构版本镜像并推送到当前用户的dockerhub 镜像仓库。
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t yanhuan6252/nginx:v1 --push .
- 构建结果示例
原理
buildx
通过QEMU
和binfmt_misc
分别为 不同的 CPU 架构构建不同的镜像。构建完成后,就会创建一个manifest list
,其中包含了指向这几个镜像的指针,当在不同的架构环境使用时会根据其环境拉取相应架构的镜像。
缺点
- 构建速度比较慢,效率比较低。
- 只能推送到镜像仓库后在查看和使用,没办法构建到本地。
- 没办法构建复杂镜像,比如说 MySQL 这种,安装时要 copy 不同架构的 rpm 包到镜像中,用这种方法就实现不了,还是得使用两个
Dockerfile 分别构建镜像,然后再合并
manifest list
的方法来实现。 - 更适合个人/小团队开发测试使用,企业级使用的话最好还是使用内部流水线或者 github action 这种自动化程度更高的工具来完成。