使用 buildx 构建多平台镜像

安装 docker engine

在 Ubuntu 上安装 docker engine

  • 卸载非官方版本 docker engine,包含如下包
    • docker.io
    • docker-compose
    • docker-doc
    • podman-docker
# docker engine 依赖 containerd 和 runc ,因此需要一起卸载,避免冲突
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove -y $pkg; done
  • 通过 apt 仓库安装
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Enable docker
systemctl enable --now docker

构建多平台镜像

docker 插件工具 buildx 可以支持构建跨平台镜像,在构建镜像时通过 --platform 参数指定要构建的平台,例如:linux/amd64linux/arm64

构建策略

根据不同场景,构建多平台镜像可以支持以下三种不同的策略。

  • 使用 QEMU 模拟器在内核内支持。
  • 使用多平台本地节点构建。
  • Dockerfile 内使用不同的阶段交叉编译。(需要源码支持)

使用 QEMU 策略构建

使用 QEMU 方式构建多平台镜像是比较方便和容易的做法,这种方法不需要修改本身的 Dockerfile 文件。他是通过 BuildKit 自动检测主机支持的不同的架构,并通过 binfmt_misc 自动加载对应的平台库去构建。在 QEMU 构建其他平台镜像相对于构建本地架构的镜像要慢很多,同时会占用大量的性能开销。

对于 QEMU 这种构建方式,需要内核 4.8 及以后的版本以及 2.1.7 及以后版本的 binfmt-support

  • 检测主机是否支持
# 查看主机配置中 `flags` 是否为 `F`
grep flags /proc/sys/fs/binfmt_misc/qemu-*

# docker desktop 已经预安装了 `binfmt_misc`,其他安装方式需要通过 `tonistiigi/binfmt` 镜像安装
docker run --privileged --rm tonistiigi/binfmt --install all

开始构建镜像

创建 builder

使用 docker buildx ls 指令查看 builder

docker buildx ls
NAME/NODE  DRIVER/ENDPOINT  STATUS   BUILDKIT PLATFORMS
default *  docker
  default  default          running  v0.11.6  linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v6

上面显示的是默认的内置构建 drive,使用的是 docker driver , 他直接是用 docker engine 来构建镜像。

接下来创建一个 docker-container drive 的 builder ,这个 builder 能提供一些更加复杂的特性,例如多平台构建以及更加高级的缓存导出。

docker buildx create --name mybuilder --bootstrap --use
# 再次检查
docker buildx ls
NAME/NODE     DRIVER/ENDPOINT              STATUS   BUILDKIT PLATFORMS
mybuilder *   docker-container
  mybuilder0  unix:///var/run/docker.sock  running  v0.12.1  linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
default       docker
  default     default                      running  v0.12.1  linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v6

示例

创建如下 Dockerfile

# syntax=docker/dockerfile:1
FROM alpine:3.16
RUN apk add curl

构建多平台镜像

docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t <username>/<image>:latest --push .
...
#16 exporting to image
#16 exporting layers
#16 exporting layers 0.5s done
#16 exporting manifest sha256:71d7ecf3cd12d9a99e73ef448bf63ae12751fe3a436a007cb0969f0dc4184c8c 0.0s done
#16 exporting config sha256:a26f329a501da9e07dd9cffd9623e49229c3bb67939775f936a0eb3059a3d045 0.0s done
#16 exporting manifest sha256:5ba4ceea65579fdd1181dfa103cc437d8e19d87239683cf5040e633211387ccf 0.0s done
#16 exporting config sha256:9fcc6de03066ac1482b830d5dd7395da781bb69fe8f9873e7f9b456d29a9517c 0.0s done
#16 exporting manifest sha256:29666fb23261b1f77ca284b69f9212d69fe5b517392dbdd4870391b7defcc116 0.0s done
#16 exporting config sha256:92cbd688027227473d76e705c32f2abc18569c5cfabd00addd2071e91473b2e4 0.0s done
#16 exporting manifest list sha256:f3b552e65508d9203b46db507bb121f1b644e53a22f851185d8e53d873417c48 0.0s done
#16 ...

#17 [auth] <username>/<image>:pull,push token for registry-1.docker.io
#17 DONE 0.0s

#16 exporting to image
#16 pushing layers
#16 pushing layers 3.6s done
#16 pushing manifest for docker.io/<username>/<image>:latest@sha256:f3b552e65508d9203b46db507bb121f1b644e53a22f851185d8e53d873417c48
#16 pushing manifest for docker.io/<username>/<image>:latest@sha256:f3b552e65508d9203b46db507bb121f1b644e53a22f851185d8e53d873417c48 1.4s done
#16 DONE 5.6s

> <username> must be a valid Docker ID and <image> and valid repository on Docker Hub.
> The --platform flag informs buildx to create Linux images for AMD 64-bit, Arm 64-bit, and Armv7 architectures.
> The --push flag generates a multi-arch manifest and pushes all the images to Docker Hub.

验证

进入 docker hub 仓库检查刚刚推送的镜像是否支持linux/amd64,linux/arm64,linux/arm/v7 平台,并在 arm 架构机器上运行上述镜像,得到如下结果:

docker run --rm docker.io/<username>/<image>:latest@sha256:723c22f366ae44e419d12706453a544ae92711ae52f510e226f6467d8228d191 uname -m
armv7l
Infee Fang
Infee Fang
互联网二手搬砖工