多平台镜像的“一个 tag 多个平台”是怎么实现的?这篇构建一个 amd64 + arm64 的镜像,拆开 docker save 出来的索引,看里面到底放了哪些 manifest,哪些是构建附带的证明,并实测跨架构运行要慢多少。
--platform linux/arm64:退出码 0,用时 650 ms --platform linux/amd64,linux/arm64 --load:退出码 0,用时 419 ms;成功
用 FROM scratch 加两份交叉编译好的静态二进制,Dockerfile 里用 ${TARGETARCH} 选文件,docker buildx build --platform linux/amd64,linux/arm64 --load 就把两个平台一次构建出来并加载到本地。这个 Docker 用的是 containerd 镜像存储,所以多平台镜像可以直接加载;经典的存储驱动下不行(本文没有测)。
index.json:1 个条目,mediaType = application/vnd.oci.image.index.v1+json
顶层索引(mediaType = application/vnd.oci.image.index.v1+json)里有 4 个 manifest:
linux/amd64 digest=d4d001400e4d… size=480 注解={}
linux/arm64 digest=b29f7d7637cd… size=480 注解={}
unknown/unknown digest=8e0a1ae62f1b… size=835 注解={'vnd.docker.reference.digest': 'sha256:d4d001400e4d75e5bb2cd36c4c101c54d', 'vnd.docker.reference.type': 'attestation-manifest'}
unknown/unknown digest=7d3f8f43cf43… size=835 注解={'vnd.docker.reference.digest': 'sha256:b29f7d7637cd5c82e8571a2b1dcc4e543', 'vnd.docker.reference.type': 'attestation-manifest'}读 docker save 出来的 index.json,它指向一个 OCI index,里面有 4 个条目:两个真正的平台 manifest(linux/amd64、linux/arm64,各 480 字节),和两个平台标成 unknown/unknown 的条目——它们的注解 vnd.docker.reference.type 是 attestation-manifest,指向对应平台的 manifest,是 BuildKit 默认附带的构建证明。看到 unknown/unknown 别当成多余的平台。
--platform linux/arm64:我是 linux/arm64 的二进制 --platform linux/amd64:我是 linux/amd64 的二进制
在 arm64 的 Docker Desktop 上 --platform linux/amd64 也能运行同一个 tag,输出确认是 amd64 的二进制。纯整数的紧循环,模拟只慢了约 1.36 倍(496 ms 对 366 ms);这只是一种负载,具体是哪种翻译技术在起作用,我没有查。
linux/arm64:3 次取最快 366 ms linux/amd64:3 次取最快 496 ms
docker image inspect 对多平台镜像只显示宿主机平台那一份(arm64),不代表镜像只有这一个平台。
bwlab-c11-single:Os=linux Architecture=arm64 Size=3702660 bwlab-c11-multi:Os=linux Architecture=arm64 Size=3702660
推送到仓库后的 manifest list 形态、--provenance=false 去掉证明、构建 amd64 版本时的模拟构建速度,都没有在本文实验。