Atlantis
GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

3.5 多架构容器镜像

1. 前言

镜像格式变更历史 中,笔者已经展示了多架构镜像的格式,如同 Manifest 之于 Blob,多构架镜像的配置文件也是一个指向不同架构 Manifest 的索引文件。

Docker 和 OCI 分别使用 application/vnd.docker.distribution.manifest.list.v2+jsonapplication/vnd.oci.image.index.v1+json 作为多架构镜像的 mediaType。

由于笔者在前司的工作内容涉及到信创产品,所有镜像需要同时兼容 AMD64 和 ARM64 架构,发版用的容器镜像都是双架构镜像,在多架构镜像方面也积累了一些经验,这里稍作展开。

2. 构建与推送多架构镜像

Docker 在 19.03 版本中引入了 buildx 作为多平台构建工具,允许在单个命令中构建多个平台的镜像,但本质上是使用 qemu 模拟目标架构执行 docker build,终究比不过原生架构的性能。

对于 Go 这样对多架构编译友好的语言,可以利用到 buildx 构建镜像时传递的参数实现交叉编译,详细内容可以参考笔者的 GitHub 项目:github.com/wbuntu/images

笔者最常用的配置如下:

首先创建一个支持 amd64 和 arm64 的多架构 buildx 实例:

docker buildx create --use --name multiarch --platform linux/amd64,linux/arm64

然后在 Dockerfile 中根据 buildx 传递的多架构参数执行交叉编译:

# buildkit跨架构编译缓慢,统一使用本机架构进行交叉编译
FROM --platform=$BUILDPLATFORM wbuntu/golang:1.19 AS builder
ARG TARGETARCH
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=$TARGETARCH
WORKDIR /custom-service
COPY . /custom-service
RUN make build
# 编译完成后拷贝到目标架构的基础镜像中
FROM --platform=$TARGETPLATFORM wbuntu/alpine:3.15
COPY --from=builder /custom-service/custom-service /usr/bin/custom-service
CMD ["/usr/bin/custom-service","-c","/etc/custom-service/config.toml"]

构建镜像时只需传递所需的目标架构即可:

docker buildx build --platform linux/amd64,linux/arm64 -t wbuntu/custom-service:v0.0.1 --push .

不过 Docker 目前还不支持存储多架构镜像(受限于 snapshot driver 与 storage driver),我们只能将构建结果直接推送到镜像仓库或者缓存在 buildx 容器内。

而另一种古老的构建多架构镜像办法更直接:

  1. 分别在 X86 和 ARM64 处理器上的构建出各自架构的镜像,如:wbuntu/custom-service:v0.0.1-amd64wbuntu/custom-service:v0.0.1-arm64
  2. 分别推送单架构镜像到镜像仓库
  3. 使用 docker manifest 命令创建多架构镜像 manifest:docker manifest create wbuntu/custom-service:v0.0.1 wbuntu/custom-service:v0.0.1-amd64、wbuntu/custom-service:v0.0.1-arm64
  4. 推送多架构镜像 manifest 到镜像仓库:docker manifest push wbuntu/custom-service:v0.0.1

3. 拷贝多架构镜像

在单架构的世界里,拷贝镜像等同于 docker pull + docker tag + docker push,但遇上多架构,简单的 docker 命令行就无法满足需求了,尤其是还需要保持 digest 一致(OCP 的要求)。

目前好用的、现成的镜像拷贝工具只有一个:skopeo。尽管 podman、cri-o 以及 skopeo 都是开源项目, 但它们本质上还是红帽 OpenShift Container Platform 的基础组件,笔者遇到需要在两个镜像仓库直接拷贝多架构镜像时,常用的命令如下:

skopeo copy --multi-arch all --preserve-digests --src-creds srcUser:srcPasswd --dest-creds destUser:destPasswd docker://src-registry.example.com/skopeo/stable:latest docker://dest-registry.example.com/skopeo/stable:latest

skopeo 会直接在两个镜像仓库之间拷贝容器镜像,不需要先缓存镜像到本地。