多架构容器镜像构建实战

最近在一个国产化项目中遇到了这样一个场景,在同一个 Kubernetes 集群中的节点是混合架构的,也就是说,其中某些节点的 CPU 架构是 x86 的,而另一些节点是 ARM 的。为了让我们的镜像在这样的环境下运行,一种最简单的做法是根据节点类型为其打上相应的标签,然后针对不同的架构构建不同的镜像,比如 demo:v1-amd64 和 demo:v1-arm64,然后还需要写两套 YAML:一套使用 demo:v1-amd64 镜像,并通过 nodeSelector 选择 x86 的节点,另一套使用 demo:v1-arm64 镜像,并通过 nodeSelector 选择 ARM 的节点。很显然,这种做法不仅非常繁琐,而且管理起来也相当麻烦,如果集群中还有其他架构的节点,那么维护成本将成倍增加。

概述

你可能知道,每个 Docker 镜像都是通过一个 manifest 来描述的,manifest 中包含了这个镜像的基本信息,包括它的 mediaType、大小、摘要以及每一层的分层信息等。可以使用 docker manifest inspect 查看某个镜像的 manifest 信息:

$ docker manifest inspect aneasystone/hello-actuator:v1
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.v2+json","config": {"mediaType": "application/vnd.docker.container.image.v1+json","size": 3061,"digest": "sha256:d6d5f18d524ce43346098c5d5775de4572773146ce9c0c65485d60b8755c0014"},"layers": [{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 2811478,"digest": "sha256:5843afab387455b37944e709ee8c78d7520df80f8d01cf7f861aae63beeddb6b"},{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 928436,"digest": "sha256:53c9466125e464fed5626bde7b7a0f91aab09905f0a07e9ad4e930ae72e0fc63"},{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 186798299,"digest": "sha256:d8d715783b80cab158f5bf9726bcada5265c1624b64ca2bb46f42f94998d4662"},{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 19609795,"digest": "sha256:112ce4ba7a4e8c2b5bcf3f898ae40a61b416101eba468397bb426186ee435281"}]
}

可以加上 --verbose 查看更详细的信息,包括该 manifest 引用的镜像标签和架构信息:

$ docker manifest inspect --verbose aneasystone/hello-actuator:v1
{"Ref": "docker.io/aneasystone/hello-actuator:v1","Descriptor": {"mediaType": "application/vnd.docker.distribution.manifest.v2+json","digest": "sha256:f16a1fcd331a6d196574a0c0721688360bf53906ce0569bda529ba09335316a2","size": 1163,"platform": {"architecture": "amd64","os": "linux"}},"SchemaV2Manifest": {...}
}

我们一般不会直接使用 manifest,而是通过标签来关联它,方便人们使用。从上面的输出结果可以看出,该 manifest 通过 docker.io/aneasystone/hello-actuator:v1 这个镜像标签来关联,支持的平台是 linux/amd64,该镜像有四个分层,另外注意这里的 mediaType 字段,它的值是 application/vnd.docker.distribution.manifest.v2+json,表示这是 Docker 镜像格式(如果是 application/vnd.oci.image.manifest.v1+json 表示 OCI 镜像)。

可以看出这个镜像标签只关联了一个 manifest ,而一个 manifest 只对应一种架构;如果同一个镜像标签能关联多个 manifest ,不同的 manifest 对应不同的架构,那么当我们通过这个镜像标签启动容器时,容器引擎就可以自动根据当前系统的架构找到对应的 manifest 并下载对应的镜像。实际上这就是 多架构镜像( multi-arch images ) 的基本原理,我们把这里的多个 manifest 合称为 manifest list( 在 OCI 规范中被称为 image index ),镜像标签不仅可以关联 manifest,也可以关联 manifest list。

可以使用 docker manifest inspect 查看某个多架构镜像的 manifest list 信息:

$ docker manifest inspect alpine:3.17
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json","manifests": [{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c","platform": {"architecture": "amd64","os": "linux"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:ecc4c9eff5b0c4de6be6b4b90b5ab2c2c1558374852c2f5854d66f76514231bf","platform": {"architecture": "arm","os": "linux","variant": "v6"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:4c679bd1e6b6516faf8466986fc2a9f52496e61cada7c29ec746621a954a80ac","platform": {"architecture": "arm","os": "linux","variant": "v7"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:af06af3514c44a964d3b905b498cf6493db8f1cde7c10e078213a89c87308ba0","platform": {"architecture": "arm64", "os": "linux",}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:af6a986619d570c975f9a85b463f4aa866da44c70427e1ead1fd1efdf6150d38","platform": {"architecture": "386", "os": "linux"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:a7a53c2331d0c5fedeaaba8d716eb2b06f7a9c8d780407d487fd0fbc1244f7e6","platform": {"architecture": "ppc64le","os": "linux"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:07afab708df2326e8503aff2f860584f2bfe7a95aee839c8806897e808508e12","platform": {"architecture": "s390x","os": "linux"}}]
}

这里的 alpine:3.17 就是一个多架构镜像,从输出结果可以看到 mediaType 是 application/vnd.docker.distribution.manifest.list.v2+json,说明这个镜像标签关联的是一个 manifest list,它包含了多个 manifest,支持 amd64、arm/v6、arm/v7、arm64、i386、ppc64le、s390x 多个架构。我们也可以直接在 Docker Hub 上看到这些信息:
在这里插入图片描述
很显然,在我们这个混合架构的 Kubernetes 集群中,这个镜像是可以直接运行的。我们也可以将我们的应用构建成这样的多架构镜像,那么在这个 Kubernetes 集群中就可以自由地运行我们自己的应用了,这种方法比上面那种为每个架构构建一个镜像的方法要优雅得多。

那么,我们要如何构建这样的多架构镜像呢?一般来说,如果你使用 Docker 作为你的构建工具,通常有两种方法:docker manifestdocker buildx。

使用 docker manifest 创建多架构镜像

docker build 是最常用的镜像构建命令,首先,我们创建一个 Dockerfile 文件,内容如下:

FROM alpine:3.17``CMD ["echo", "Hello"]

然后使用 docker build 构建镜像:

$ docker build -f Dockerfile -t aneasystone/demo:v1 .

这样一个简单的镜像就构建好了,使用 docker run 对其进行测试:

$ docker run --rm -it aneasystone/demo:v1
Hello

非常顺利,镜像能正常运行。不过这样构建的镜像有一个问题,Docker Engine 是根据当前我们的系统自动拉取基础镜像的,我的系统是 x86 的,所以拉取的 alpine:3.17 镜像架构是 linux/amd64 的:

$ docker image inspect alpine:3.17 | grep Architecture"Architecture": "amd64",

如果要构建其他架构的镜像,可以有三种办法。第一种是最原始的方法,Docker 官方为每种 不同的架构创建了不同的独立账号,比如下面是一些常用的账号:
● ARMv6 32-bit (arm32v6): https://hub.docker.com/u/arm32v6/
● ARMv7 32-bit (arm32v7): https://hub.docker.com/u/arm32v7/
● ARMv8 64-bit (arm64v8): https://hub.docker.com/u/arm64v8/
● Linux x86-64 (amd64): https://hub.docker.com/u/amd64/
● Windows x86-64 (windows-amd64): https://hub.docker.com/u/winamd64/
所以我们就可以通过 amd64/alpinearm64v8/alpine 来拉取相应架构的镜像,我们对 Dockerfile 文件稍微修改一下:

ARG ARCH=amd64
FROM ${ARCH}/alpine:3.17
CMD ["echo", "Hello"]

然后使用 --build-arg 参数来构建不同架构的镜像:

docker build --build-arg ARCH=amd64 -f Dockerfile-arg -t aneasystone/demo:v1-amd64 .
docker build --build-arg ARCH=arm64v8 -f Dockerfile-arg -t aneasystone/demo:v1-arm64 .

不过从 2017 年 9 月开始,一个镜像可以支持多个架构了,这种方法就渐渐不用了。第二种办法就是直接使用 alpine:3.17 这个基础镜像,通过 FROM 指令的 --platform 参数,让 Docker Engine 自动拉取特定架构的镜像。我们新建两个文件 Dockerfile-amd64 和 Dockerfile-arm64Dockerfile-amd64 文件内容如下:

FROM --platform=linux/amd64 alpine:3.17
CMD ["echo", "Hello"]

Dockerfile-arm64 文件内容如下:

FROM --platform=linux/arm64 alpine:3.17
CMD ["echo", "Hello"]

然后使用 docker build 再次构建镜像即可:

$ docker build --pull -f Dockerfile-amd64 -t aneasystone/demo:v1-amd64 .
$ docker build --pull -f Dockerfile-arm64 -t aneasystone/demo:v1-arm64 .

注意这里的 --pull 参数,强制要求 Docker Engine 拉取基础镜像,要不然第二次构建时会使用第一次的缓存,这样基础镜像就不对了。

第三种方法不用修改 Dockerfile 文件,因为 docker build 也支持 --platform 参数,我们只需要像下面这样构建镜像即可:

$ docker build --pull --platform=linux/amd64 -f Dockerfile -t aneasystone/demo:v1-amd64 .
$ docker build --pull --platform=linux/arm64 -f Dockerfile -t aneasystone/demo:v1-arm64 .

在执行 docker build 命令时,可能会遇到下面这样的报错信息:

$ docker build -f Dockerfile-arm64 -t aneasystone/demo:v1-arm64 .
[+] Building 1.2s (3/3) FINISHED=> [internal] load build definition from > Dockerfile-arm64                   0.0s=> => transferring dockerfile: > 37B                                          0.0s=> [internal] load .> dockerignore                                            0.0s=> => transferring context: > 2B                                              0.0s=> ERROR [internal] load metadata for docker.io/library/alpine:3.> 17         1.1s
------> [internal] load metadata for docker.io/library/alpine:3.17:
------
failed to solve with frontend dockerfile.v0: failed to create LLB > definition: unexpected status code [manifests 3.17]: 403 Forbidden

根据 这里 的信息,修改 Docker Daemon 的配置文件,将 buildkit 设置为 false 即可:

"features": {"buildkit": false
},

构建完不同架构的镜像后,我们就可以使用 docker manifest 命令创建 manifest list,生成自己的多架构镜像了。由于目前创建 manifest list 必须引用远程仓库中的镜像,所以在这之前,我们需要先将刚刚生成的两个镜像推送到镜像仓库中:

$ docker push aneasystone/demo:v1-amd64
$ docker push aneasystone/demo:v1-arm64

然后使用 docker manifest create 创建一个 manifest list,包含我们的两个镜像:

$ docker manifest create aneasystone/demo:v1 \--amend aneasystone/demo:v1-amd64 \--amend aneasystone/demo:v1-arm64

最后将该 manifest list 也推送到镜像仓库中就大功告成了:

$ docker manifest push aneasystone/demo:v1

可以使用 docker manifest inspect 查看这个镜像的 manifest list 信息:

$ docker manifest inspect aneasystone/demo:v1
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json","manifests": [{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:170c4a5295f928a248dc58ce500fdb5a51e46f17866369fdcf4cbab9f7e4a1ab","platform": {"architecture": "amd64","os": "linux"}},{"mediaType": "application/vnd.docker.distribution.manifest.v2+json","size": 528,"digest": "sha256:3bb9c02263447e63c193c1196d92a25a1a7171fdacf6a29156f01c56989cf88b","platform": {"architecture": "arm64","os": "linux","variant": "v8"}}]
}

也可以在 Docker Hub 上看到这个镜像的架构信息:
在这里插入图片描述

使用 docker buildx 创建多架构镜像

从上一节可以看出,使用 docker manifest 来构建多架构镜像的步骤大致分为以下四步:

使用 docker build 依次构建每个架构的镜像;
使用 docker push 将镜像推送到镜像仓库;
使用 docker manifest create 创建一个 manifest list,包含上面的每个镜像;
使用 docker manifest push 将 manifest list 推送到镜像仓库;
每次构建多架构镜像都要经历这么多步骤还是非常麻烦的,这一节将介绍一种更方便的方式,使用 docker buildx 来创建多架构镜像。

buildx 是一款 Docker CLI 插件,它对 Moby BuildKit 的构建功能进行了大量的扩展,同时在使用体验上还保持和 docker build 一样,用户可以很快上手。如果你的系统是 Windows 或 MacOS,buildx 已经内置在 Docker Desktop 里了,无需额外安装;如果你的系统是 Linux,可以使用 DEB 或 RPM 包的形式安装,也可以手工安装,具体安装步骤参考 官方文档。

使用 docker buildx 创建多架构镜像只需简单一行命令即可:

docker buildx build --platform=linux/amd64,linux/arm64 -t aneasystone/demo:v2 .

不过第一次执行这行命令时会报下面这样的错:

ERROR: multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")

这是因为 buildx 默认使用的 构建器( builder ) 驱动是 docker driver,它不支持同时构建多个 platform 的镜像,我们可以使用 docker buildx create 创建其他驱动的构建器( 关于 buildx 的四种驱动以及它们支持的特性可以 参考这里 ):

docker buildx create --use``nice_cartwright

这样创建的构建器驱动是 docker-container driver,它目前还没有启动:

$ docker buildx lsNAME/NODE          DRIVER/ENDPOINT                STATUS   BUILDKIT PLATFORMSnice_cartwright *  docker-containernice_cartwright0 npipe:./pipe/docker_engine inactivedefault            dockerdefault          default                        running  20.10.17 linux/amd64, linux/arm64, ...

当执行 docker buildx build 时会自动启动构建器:

$ docker buildx build --platform=linux/amd64,linux/arm64 -t aneasystone/demo:v2 .
[+] Building 14.1s (7/7) FINISHED=> [internal] booting buildkit                                                                                                            1.2s => => starting container buildx_buildkit_nice_cartwright0                                                                                 1.2s => [internal] load build definition from Dockerfile                                                                                       0.1s => => transferring dockerfile: 78B                                                                                                        0.0s => [internal] load .dockerignore                                                                                                          0.0s => => transferring context: 2B                                                                                                            0.0s => [linux/amd64 internal] load metadata for docker.io/library/alpine:3.17                                                                12.3s => [linux/arm64 internal] load metadata for docker.io/library/alpine:3.17                                                                12.2s => [linux/arm64 1/1] FROM docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a           0.2s => => resolve docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a                       0.1s => [linux/amd64 1/1] FROM docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a           0.2s => => resolve docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a                       0.1s 
WARNING: No output specified with docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load

使用 docker ps 可以看到正在运行的构建器,实际上就是 buildkitd 服务,docker buildx build 为我们自动下载了 moby/buildkit:buildx-stable-1 镜像并运行:

$ docker ps
CONTAINER ID   IMAGE                           COMMAND       CREATED         STATUS         PORTS     NAMES
e776505153c0   moby/buildkit:buildx-stable-1   "buildkitd"   7 minutes ago   Up 7 minutes             buildx_buildkit_nice_cartwright0

上面的构建结果中有一行 WARNING 信息,意思是我们没有指定 output 参数,所以构建的结果只存在于构建缓存中,如果要将构建的镜像推送到镜像仓库,可以加上一个 --push 参数:

$ docker buildx build --push --platform=linux/amd64,linux/arm64 -t aneasystone/demo:v2 .
[+] Building 14.4s (10/10) FINISHED=> [internal] load build definition from Dockerfile                                                                                       0.1s => => transferring dockerfile: 78B                                                                                                        0.0s => [internal] load .dockerignore                                                                                                          0.0s => => transferring context: 2B                                                                                                            0.0s => [linux/arm64 internal] load metadata for docker.io/library/alpine:3.17                                                                 9.1s => [linux/amd64 internal] load metadata for docker.io/library/alpine:3.17                                                                 9.0s => [auth] library/alpine:pull token for registry-1.docker.io                                                                              0.0s => [linux/arm64 1/1] FROM docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a           0.1s => => resolve docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a                       0.1s => [linux/amd64 1/1] FROM docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a           0.1s => => resolve docker.io/library/alpine:3.17@sha256:f271e74b17ced29b915d351685fd4644785c6d1559dd1f2d4189a5e851ef753a                       0.1s => exporting to image                                                                                                                     5.1s => => exporting layers                                                                                                                    0.0s => => exporting manifest sha256:4463076cf4b016381c6722f6cce481e015487b35318ccc6dc933cf407c212b11                                          0.0s => => exporting config sha256:6057d58c0c6df1fbc55d89e1429ede402558ad4f9a243b06d81e26a40d31eb0d                                            0.0s => => exporting manifest sha256:05276d99512d2cdc401ac388891b0735bee28ff3fc8e08be207a0ef585842cef                                          0.0s => => exporting config sha256:86506d4d3917a7bb85cd3d147e651150b83943ee89199777ba214dd359d30b2e                                            0.0s => => exporting manifest list sha256:a26956bd9bd966b50312b4a7868d8461d596fe9380652272db612faef5ce9798                                     0.0s => => pushing layers                                                                                                                      3.0s => => pushing manifest for docker.io/aneasystone/demo:v2@sha256:a26956bd9bd966b50312b4a7868d8461d596fe9380652272db612faef5ce9798          2.0s => [auth] aneasystone/demo:pull,push token for registry-1.docker.io                                                                       0.0s => [auth] aneasystone/demo:pull,push library/alpine:pull token for registry-1.docker.io

访问 Docker Hub,可以看到我们的镜像已经成功推送到仓库中了:
在这里插入图片描述

更多

使用 QEMU 运行不同架构的程序

在构建好多个架构的镜像之后,我们可以使用 docker run 测试一下:

$ docker run --rm -it aneasystone/demo:v1-amd64
Hello$ docker run --rm -it aneasystone/demo:v1-arm64
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
Hello

这里可以发现一个非常奇怪的现象,我们的系统明明不是 arm64 的,为什么 arm64 的镜像也能正常运行呢?除了一行 WARNING 信息之外,看上去并没有异样,而且我们也可以使用 sh 进到容器内部正常操作:

> docker run --rm -it aneasystone/demo:v1-arm64 sh
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
/ # ls
bin    dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
/ #

不过当我们执行 ps 命令时,发现了一些端倪:

/ # ps aux
PID   USER     TIME  COMMAND1 root      0:00 {sh} /usr/bin/qemu-aarch64 /bin/sh sh8 root      0:00 ps aux

可以看出我们所执行的 sh 命令实际上被 /usr/bin/qemu-aarch64 转换了,而 QEMU 是一款强大的模拟器,可以在 x86 机器上模拟 arm 的指令。关于 QEMU 执行跨架构程序可以参考这篇文章:https://blog.lyle.ac.cn/2020/04/14/transparently-running-binaries-from-any-architecture-in-linux-with-qemu-and-binfmt-misc/)。

查看镜像的 manifest 信息

除了 docker manifest 命令,还有很多其他方法也可以查看镜像的 manifest 信息,比如:
● crane manifest
● manifest-tool

buildx 支持的几种输出类型

在上文中,我们使用了 –push 参数将镜像推送到镜像仓库中:

$ docker buildx build --push --platform=linux/amd64,linux/arm64 -t aneasystone/demo:v2 .

这个命令实际上等同于:

$ docker buildx build --output=type=image,name=aneasystone/demo:v2,push=true --platform=linux/amd64,linux/arm64 .

也等同于:

$ docker buildx build --output=type=registry,name=aneasystone/demo:v2 --platform=linux/amd64,linux/arm64 .

我们通过 --output 参数指定镜像的输出类型,这又被称为 导出器( exporter ),buildx 支持如下几种不同的导出器:
☑ image - 将构建结果导出到镜像
☑ registry - 将构建结果导出到镜像,并推送到镜像仓库
☑ local - 将构建的文件系统导出成本地目录
☑ tar - 将构建的文件系统打成 tar 包
☑ oci - 构建 OCI 镜像格式 的镜像
☑ docker - 构建 Docker 镜像格式 的镜像
☑ cacheonly - 将构建结果放在构建缓存中
其中 imageregistry 这两个导出器上面已经用过,一般用来将镜像推送到远程镜像仓库。如果我们只想构建本地镜像,而不希望将其推送到远程镜像仓库,可以使用 oci 或 docker 导出器,比如下面的命令使用 docker 导出器将构建结果导出成本地镜像:

$ docker buildx build --output=type=docker,name=aneasystone/demo:v2-amd64 --platform=linux/amd64 .

也可以使用 docker 导出器将构建结果导出成 tar 文件:

$ docker buildx build --output=type=docker,dest=./demo-v2-docker.tar --platform=linux/amd64 .

这个 tar 文件可以通过 docker load 加载:

$ docker load -i ./demo-v2-docker.tar

因为我本地运行的是 Docker 服务,不支持 OCI 镜像格式,所以指定 type=oci 时会报错:

$ docker buildx build --output=type=oci,name=aneasystone/demo:v2-amd64 --platform=linux/amd64 .
ERROR: output file is required for oci exporter. refusing to write to console

不过我们可以将 OCI 镜像导出成 tar 包:

$ docker buildx build --output=type=oci,dest=./demo-v2-oci.tar --platform=linux/amd64 .

将这个 tar 包解压后,可以看到一个标准的镜像是什么格式:

$ mkdir demo-v2-docker && tar -C demo-v2-docker -xf demo-v2-docker.tar
$ tree demo-v2-docker
demo-v2-docker
├── blobs
│   └── sha256
│       ├── 4463076cf4b016381c6722f6cce481e015487b35318ccc6dc933cf407c212b11
│       ├── 6057d58c0c6df1fbc55d89e1429ede402558ad4f9a243b06d81e26a40d31eb0d
│       └── 8921db27df2831fa6eaa85321205a2470c669b855f3ec95d5a3c2b46de0442c9
├── index.json
├── manifest.json
└── oci-layout2 directories, 6 files

有一点奇怪的是,OCI 镜像格式的 tar 包和 docker 镜像格式的 tar 包是完全一样的,不知道怎么回事?

如果我们不关心构建结果,而只是想看下构建镜像的文件系统,比如看看它的目录结构是什么样的,或是看看有没有我们需要的文件,可以使用 local 或 tar 导出器。local 导出器将文件系统导到本地的目录:

$ docker buildx build --output=type=local,dest=./demo-v2 --platform=linux/amd64 .

tar 导出器将文件系统导到一个 tar 文件中:

$ docker buildx build --output=type=tar,dest=./demo-v2.tar --platform=linux/amd64 .

值得注意的是,这个 tar 文件并不是标准的镜像格式,所以我们不能使用 docker load 加载,但是我们可以使用 docker import 加载,加载的镜像中只有文件系统,在运行这个镜像时,Dockerfile 中的 CMD 或 ENTRYPOINT 等命令是不会生效的:

$ mkdir demo-v2 && tar -C demo-v2 -xf demo-v2.tar``$ ls demo-v2``bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var

不安全的镜像仓库

在上文中,我们使用了两种方法构建了多架构镜像,并将镜像推送到官方的 Docker Hub 仓库,如果需要推送到自己搭建的镜像仓库( 关于如何搭建自己的镜像仓库,可以参考 博客 ),由于这个仓库可能是不安全的,可能会遇到一些问题。

第一种方式是直接使用 docker push 推送,推送前我们需要修改 Docker 的配置文件 /etc/docker/daemon.json,将仓库地址添加到 insecure-registries 配置项中:

{"insecure-registries" : ["192.168.1.39:5000"]
}

然后重启 Docker 后即可。

第二种方式是使用 docker buildx 的 image 或 registry 导出器推送,这个推送工作实际上是由 buildkitd 完成的,所以我们需要让 buildkitd 忽略这个不安全的镜像仓库。我们首先创建一个配置文件 buildkitd.toml:

[registry."192.168.1.39:5000"]http = trueinsecure = true

关于 buildkitd 的详细配置可以 参考这里。然后使用 docker buildx create 重新创建一个构建器:

$ docker buildx create --config=buildkitd.toml --use

这样就可以让 docker buildx 将镜像推送到不安全的镜像仓库了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/220665.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity SRP 管线【第四讲:URP 阴影】

URP 全文源码解析参照 引入 在UniversalRenderer.cs/ line 505行处 此处已经准备好了所有渲染数据(所有数据全部存储在了renderingData中) 我们只用renderingData中的数据初设置mainLightShadows bool mainLightShadows m_MainLightShadowCasterPass…

SVM —— 理论推导

SVM 支持向量线性可分最大间隔超平面最大间隔超平面的推导支持向量分类间隔的推导最优化问题 对偶问题拉格朗日乘子法强对偶性 SVM 优化软间隔解决问题优化目标及求解 核函数线性不可分核函数的作用常见核函数 SVM 算法优缺点 支持向量机(Support Vector Machine&am…

AR室内导航如何实现?技术与原理分析

随着科技的进步,我们生活中许多方面正在被重新定义。其中之一就是导航,尤其是室内导航。增强现实(AR)技术的出现为室内导航带来了革命性的变革。本文将深入探讨AR室内导航的技术与原理,以及它如何改变我们的生活方式。…

【组合数学】生成函数

目录 1.形式幂级数2.生成函数性质3.生成函数求解递推关系4.生成函数在计数问题中的应用 1.形式幂级数 生成函数是解决计数问题的一种有效方法,它的中心思想是:对于一个有限或无限数列 a 0 , a 1 , a 2 , . . . {a_0,a_1,a_2,...} a0​,a1​,a2​,...&am…

数据分析的基本步骤

了解过数据分析的概念之后,我们再来说下数据分析的常规步骤。 明确目标 首先我们要确定一个目标,即我们要从数据中得到什么。比如我们要看某个指标A随时间的变化趋势,以期进行简单的预测。 数据收集 当确定了目标之后,就有了取…

SQL Server 查询处理过程

查询处理--由 SQL Server 中的关系引擎执行,它获取编写的 T-SQL 语句并将其转换为可以向存储引擎发出请求并检索所需结果的过程。 SQL Server 需要四个步骤来处理查询:分析、代化、优化和执行。 前三个步骤都由关系引擎执行;第三步输出的是…

camera曝光时间

曝光和传感器读数 相机上的图像采集过程由两个不同的部分组成。第一部分是曝光。曝光完成后,第二步就是从传感器的寄存器中读取数据并传输(readout)。 曝光:曝光是图像传感器进行感光的一个过程,相机曝光时间&#xf…

深度学习中的潜在空间

1 潜在空间定义 Latent Space 潜在空间:Latent ,这个词的语义是“隐藏”的意思。“Latent Space 潜在空间”也可以理解为“隐藏的空间”。Latent Space 这一概念是十分重要的,它在“深度学习”领域中处于核心地位,即它是用来学习…

和葡萄酒时为什么要写品酒笔记?

如果你不把你的想法写下来,它们可能会在你离开房间之前就离开你的大脑。写笔记,包括令人难忘的品酒笔记,它是关于记录一些超越今天和明天的有意义的事情。这是你的记忆葡萄酒,对你来说最相关、最有区别的就是最重要的。最后&#…

桌面概率长按键盘无法连续输入问题

问题描述:概率性长按键盘无法连续输入文本 问题定位: 系统按键流程分析 图一 系统按键流程 按键是由X Server接收的,这一点只要明白了X Window的工作机制就不难理解了。X Server在接收到按键后,会转发到相应程序的窗口中。在窗…

CogVLM与CogAgent:开源视觉语言模型的新里程碑

引言 随着机器学习的快速发展,视觉语言模型(VLM)的研究取得了显著的进步。今天,我们很高兴介绍两款强大的开源视觉语言模型:CogVLM和CogAgent。这两款模型在图像理解和多轮对话等领域表现出色,为人工智能的…

【算法日志】非排序数组的二分查找应用

文章目录 前言 二分查找是一种比较简单且基础的查找算法,多用于排序数组的快速查找。但其实二分查找也有非排序数组的应用。 引例 Leetcode162 寻找峰值 本题是一道经典的二分查找算法题,要求找到一个比左右相邻值大的峰值。如果用暴力解法&#xff0…

【网络安全】网络防护之旅 - Java安全机制探秘与数字证书引爆网络防线

🌈个人主页:Sarapines Programmer🔥 系列专栏:《网络安全之道 | 数字征程》⏰墨香寄清辞:千里传信如电光,密码奥妙似仙方。 挑战黑暗剑拔弩张,网络战场誓守长。 目录 😈1. 初识网络安…

JS的浅拷贝和深拷贝

首先理解什么是浅拷贝和深拷贝: 浅拷贝: 浅拷贝只会复制对象的第一层属性,而不会递归地复制嵌套的对象。浅拷贝仅复制对象的引用,新对象和原始对象仍然共享相同的引用,因此对新对象的修改可能会影响到原始对象。浅拷…

自动化测试 (五) 读写64位操作系统的注册表

自动化测试经常需要修改注册表 很多系统的设置(比如:IE的设置)都是存在注册表中。 桌面应用程序的设置也是存在注册表中。 所以做自动化测试的时候,经常需要去修改注册表 Windows注册表简介 注册表编辑器在 C:\Windows\regedit…

WebSocket开发

目录 前言 1.介绍 2.原理解析 3.简单的聊天室搭建 4.点到点消息传输 总结 前言 WebSocket 是互联网项目中画龙点睛的应用,可以用于消息推送、站内信、在线聊天等业务。 1.介绍 WebSocket 是一种基于 TCP 的新网络协议,它是一种持久化的协议&…

Java精品项目源码新基于协同过滤算法的旅游推荐系统(编号V69)

Java精品项目源码新基于协同过滤算法的旅游推荐系统(编号V69) 大家好,小辰今天给大家介绍一个基于协同过滤算法的旅游推荐系统

056:vue工具 --- CSS在线格式化

第056个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…

Netty应用(七) ----MQTT编解码器

目录 0.前言1. MqttEncoder--编码器1.1 构造方法1.2 encodeConnectMessage -- 连接消息1.3 encodeConnAckMessage - 确认连接1.4 encodePublishMessage -- 发布消息1.5 encodeSubscribeMessage - 订阅主题1.6 encodeUnsubscribeMessage - 取消订阅1.7 encodeSubAckMessage - 订…

HarmonyOS应用开发实战—开箱即用的应用首页页面【ArkTS】【鸿蒙专栏-34】

一.HarmonyOS应用开发实战—开箱即用的应用首页页面【ArkTS】【鸿蒙专栏-34】 1.1 项目背景 HarmonyOS(鸿蒙操作系统)是华为公司推出的一种分布式操作系统。它被设计为一种全场景、全连接的操作系统,旨在实现在各种设备之间的无缝协同和共享,包括智能手机、平板电脑、智能…