如何在Docker for macOS中导入和运行多平台OCI映像



我有一个go web服务,我已经用docker build构建了一段时间,并在M1 Mac和各种Linux机器上用docker run运行了一段时期。这是一个安装在发行版基础上的简单二进制文件,效果很好。现在我想制作一个多平台图像导出,发送给同事在他们的电脑(M1和英特尔Mac)上运行。我做了一个OC出口:

docker buildx build --platform linux/amd64,linux/arm64 -t toy -o type=oci,dest=toy.oci .

导入效果良好:

docker image import toy.oci toy
sha256:02f7342d9d6ec2a1b66440aedb8d9a6ae0e968373fc8f2b698f7c8e73e6747e0

运行它是另一回事:

docker run -d --name toy -p 4242:4242 toy:latest
docker: Error response from daemon: No command specified.
See 'docker run --help'.

这很奇怪,因为我的Dockerfile有一个ENTRYPOINT。不管怎样,我试着告诉它要运行的命令:

docker run -d --name toy -p 4242:4242 toy:latest /bin/toy
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/toy": stat /bin/toy: no such file or directory: unknown.

这几乎就像容器中没有文件一样!docker image ls显示了我的32MB图像,所以我知道那里有东西,但它在哪里?是不是少了一层?

FWIW我也尝试了type=tar,得到了同样的结果。

我想我错过了一步,但我的DuckDuckGo foo让我失败了。如何构建可导出的多平台Docker镜像?

这里同时发生了几件事:

  • 您只能导入一个平台映像
  • 要导入图像,应使用docker loaddocker import命令从容器加载文件系统导出,并且不会有任何图像元数据
  • docker load命令期望其自己的元数据不同于OCI布局

我在regctl中有功能,可以提取特定的平台,并进行刚刚检查到main中的转换(因此它将在v0.4.5中发布,如果你不想自己构建,也可以直接从GHA中提取二进制文件)。这样做的命令看起来像:

# regclient works with directories rather than tars, so import the OCI tar to a directory
regctl image import ocidir://toy toy.oci
# get the digest from your local platform, you can also change "local" to "linux/amd64"
dig="$(regctl image digest --platform local ocidir://toy)"
# export the single platform image using the digest
regctl image export "ocidir://toy@${dig}" toy-docker.tar
# load into docker
docker load <toy-docker.tar

docker的这种行为将随着向用于图像管理的containerd的转换而改变。当这种情况发生时,多平台图像可以直接加载到docker中,并且它们的导入命令应该直接支持OCI布局。截至本次更新,这在Docker Desktop中是实验性的,尚未在Linux上与Docker引擎一起发布。

我在M1上构建支持多拱门的图像的方式是:

  • 运行docker buildx create --bootstrap
  • 确保节点已启动并使用适当的平台docker buildx ls运行,如果选中,则节点名称旁边会有一个星号,否则运行:docker buildx use [node]
  • 开始塑造你的形象:docker buildx build --platform linux/amd64,linux/arm64 -t sample:v1 . --push。这将把图像推送到注册表

一旦图像进入注册表,您的同事就可以开始提取它。这是最简单的方法,因为我过去在尝试其他目标输出时遇到过问题。

@BMitch的回答很好地解决了这里的各种问题,足以让我达到完全使用Docker的目的。问题是缺乏对加载多平台图像的支持,因此每个平台必须有单独的图像。咒语是:

docker buildx build --platform linux/amd64 -t toy -o type=docker,dest=toy-amd64.tar .
docker buildx build --platform linux/arm64 -t toy -o type=docker,dest=toy-arm64.tar .

然后,单独的tar文件可以与任何人共享,并加载:

docker load <toy-arm64.tar
docker run -d --name toy -p 4242:4242 toy:latest /bin/toy

在AMD64平台上切换到toy-amd64.tar

对于踢球,我设定了make目标来进行构建和加载,如下所示:

docker-export: $(patsubst %,toy-%.tar, arm64 amd64)
toy-%.tar: vendor cmd/toy/toy.go schema/*.schema.json corpus/user/valid/*.json
docker buildx build --platform linux/$* -t nytimes/toy -o type=docker,dest=$@ .
# Import an exported docker image for the current architecture.
docker-import: toy-$(patsubst aarch64,$(shell docker --debug info | grep Architecture | awk '{print $2}'),arm64).tar
docker load <toy-$(patsubst aarch64,$(shell docker --debug info | grep Architecture | awk '{print $2}'),arm64).tar

最新更新