利用 docker buildx 构建多平台镜像

什么是 docker buildx

Docker Buildx是一个CLI插件,它扩展了Docker命令,完全支持Moby BuildKit builder toolkit提供的功能。它提供了与docker build相同的用户体验,并提供了许多新功能,如创建作用域生成器实例和针对多个节点并发构建。

Docker Buildx包含在Docker 19.03中,并与以下Docker Desktop版本捆绑在一起。请注意,必须启用“实验特性”选项才能使用Docker Buildx。

Docker Desktop Enterprise version 2.1.0
Docker Desktop Edge version 2.0.4.0 or higher

创建 builder 实例

由于 Docker 默认的 builder 实例不支持同时指定多个 –platform,所有我们必须先创建一个 builder 实例

$ docker buildx create --use --name=mybuilder-cn --driver docker-container

--use 表示使用当前创建的 builder 实例
--name 实例名称
--driver 实例驱动(docker、 docker-container 和 kubernetes)

更多用法通过命令 docker buildx create -h 查看

查看新实例

NAME/NODE       DRIVER/ENDPOINT             STATUS   PLATFORMS
mybuilder-cn *  docker-container                     
  mybuilder-cn0 unix:///var/run/docker.sock inactive 
default         docker                               
  default       default                     running  linux/amd64, linux/386

其中 * 表示当前正在使用的builder实例。

我们查看一下实例详细信息

docker buildx inspect
Name:   mybuilder-cn
Driver: docker-container

Nodes:
Name:      mybuilder-cn0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/386

构建镜像

  1. 创建Dockerfile 文件
FROM --platform=$TARGETPLATFORM alpine
RUN uname -a > /os.txt
CMD cat /os.txt

这里使用到了变量 TARGETPLATFORM, 内置的一些变量在本文下方了解

  1. 使用 docker buildx build 命令构建镜像

构建指定平台

最简单的方法

$ docker buildx build .
WARN[0000] No output specified for 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 
[+] Building 32.9s (6/6) FINISHED                                                                                                                                                                          
 => [internal] booting buildkit                                                                                                                                                                      25.4s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                                                   23.9s
 => => creating container buildx_buildkit_mybuilder-cn0                                                                                                                                               1.5s
 => [internal] load build definition from Dockerfile                                                                                                                                                  0.0s
 => => transferring dockerfile: 115B                                                                                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                                                                                                      5.1s
 => [1/2] FROM docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                                       1.9s
 => => resolve docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                                       0.0s
 => => sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e 2.81MB / 2.81MB                                                                                                        1.8s
 => => extracting sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e                                                                                                             0.1s
 => [2/2] RUN uname -a > /os.txt 

从上面的输出可以看出,构建步骤大概如下:

  1. 加载引导buildkit
  2. 下载镜像文件 image moby/buildkit:buildx-stable-1
  3. 使用镜像创建容器 buildx_buildkit_mybuilder-cn0
  4. 将当前 Dockerfile 文件传输到容器内
  5. 在容器内下载镜像 docker.io/library/alpine:latest
  6. 运行Dockerfile中的 RUN 指令

如果你用 docker ps 命令查看容器的话,会看到创建了一个 buildkitd 容器

构建多平台镜像

也可以指定一些设置,如构建的目标平台等

$ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t cfanbo/hello . --push
[+] Building 18.4s (18/18) FINISHED                                                                                                                                                                        
 => [internal] load build definition from Dockerfile                                                                                                                                                  0.0s
 => => transferring dockerfile: 115B                                                                                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                                                                       0.0s
 => [linux/amd64 internal] load metadata for docker.io/library/alpine:latest                                                                                                                          2.3s
 => [linux/arm64 internal] load metadata for docker.io/library/alpine:latest                                                                                                                          2.4s
 => [linux/arm/v7 internal] load metadata for docker.io/library/alpine:latest                                                                                                                         2.7s
 => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                         0.0s
 => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                         0.0s
 => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                         0.0s
 => [linux/arm/v7 1/2] FROM docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                          0.0s
 => => resolve docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                                       0.0s
 => [linux/arm64 1/2] FROM docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                           0.0s
 => => resolve docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                                       0.0s
 => [linux/amd64 1/2] FROM docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                           0.0s
 => => resolve docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a                                                                                       0.0s
 => CACHED [linux/arm/v7 2/2] RUN uname -a > /os.txt                                                                                                                                                  0.0s
 => CACHED [linux/arm64 2/2] RUN uname -a > /os.txt                                                                                                                                                   0.0s
 => CACHED [linux/amd64 2/2] RUN uname -a > /os.txt                                                                                                                                                   0.0s
 => exporting to image                                                                                                                                                                               15.5s
 => => exporting layers                                                                                                                                                                               0.0s
 => => exporting manifest sha256:fc3842a7caa48e4124d6a76dcb1cff3944e55eeefd5daeb21dfe8201de8d856c                                                                                                     0.0s
 => => exporting config sha256:4db341c87f5e347b12f2f8955da60ac4db9bcf2bc17aed18079d2ac55b759cca                                                                                                       0.0s
 => => exporting manifest sha256:91bcf398194707f1b9ce9fe6b7dcc29b809dc3278cddc1a13cb5dc76d60bb281                                                                                                     0.0s
 => => exporting config sha256:e0f919da8544b23e919c1257c6b40c54a4c9114447e1d637f4d965204757dbab                                                                                                       0.0s
 => => exporting manifest sha256:4a5479758b57441933984c2209bccff4f0c47c7dd74be95b4b22162d105e639a                                                                                                     0.0s
 => => exporting config sha256:6af666ad21adae02a9147dfcec4c9397e2be9e12d5861614e5fc20a287fe4086                                                                                                       0.0s
 => => exporting manifest list sha256:e8758d75c4d6cf2a633c645616c1d4f76006ee0503d7b16532a744346bca0cf7                                                                                                0.0s
 => => pushing layers                                                                                                                                                                                12.4s
 => => pushing manifest for docker.io/cfanbo/hello:latest@sha256:e8758d75c4d6cf2a633c645616c1d4f76006ee0503d7b16532a744346bca0cf7                                                                     3.1s
 => [auth] cfanbo/hello:pull,push token for registry-1.docker.io                                                                                                                                      0.0s
 => [auth] cfanbo/hello:pull,push token for registry-1.docker.io                                                                                                                                      0.0s
 => [auth] cfanbo/hello:pull,push token for registry-1.docker.io 

这里我们指定了三个平台,它们将同时分别进行构建。由于我这里多次执行了构建命令,所以三个平台构建过程中都显示 CACHED,表示用到了缓存。

我这里 https://hub.docker.com 平台的用户名为 cfanbo, 替换为自己的 Docker Hub 用户名。

--push 参数表示将构建好的镜像推送到 Docker 仓库,在构建前记得先执行 docker login 命令登录。这里通过 https://hub.docker.com/repository/docker/cfanbo/hello/tags?page=1&ordering=last_updated 可以看到分别为三个平台构建了不同的镜像。

  1. 查看镜像信息
$ docker buildx imagetools inspect cfanbo/hello
Name:      docker.io/cfanbo/hello:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:e8758d75c4d6cf2a633c645616c1d4f76006ee0503d7b16532a744346bca0cf7
           
Manifests: 
  Name:      docker.io/cfanbo/hello:latest@sha256:fc3842a7caa48e4124d6a76dcb1cff3944e55eeefd5daeb21dfe8201de8d856c
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm/v7
             
  Name:      docker.io/cfanbo/hello:latest@sha256:91bcf398194707f1b9ce9fe6b7dcc29b809dc3278cddc1a13cb5dc76d60bb281
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm64
             
  Name:      docker.io/cfanbo/hello:latest@sha256:4a5479758b57441933984c2209bccff4f0c47c7dd74be95b4b22162d105e639a
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64
  1. 验证镜像
### Linux
$ docker run --rm cfanbo/hello:latest
Linux buildkitsandbox 5.11.0-31-generic #33-Ubuntu SMP Wed Aug 11 13:19:04 UTC 2021 x86_64 Linux

### aarch64
$ sudo docker run --rm cfanbo/hello:latest
Linux buildkitsandbox 5.11.0-31-generic #33-Ubuntu SMP Wed Aug 11 13:19:04 UTC 2021 aarch64 Linux

常量

Dockerfile 支持如下架构相关的变量

TARGETPLATFORM 构建镜像的目标平台,例如 linux/amd64, linux/arm/v7, windows/amd64。

TARGETOS 构建镜像目标平台的 OS 类型,例如 linux, windows

TARGETARCH 构建镜像目标平台的架构类型,例如 amd64, arm

TARGETVARIANT 构建镜像目标平台的变种,该变量可能为空,例如 v7

BUILDPLATFORM 构建镜像所使用的主机平台,例如 linux/amd64

BUILDOS 构建镜像所使用主机平台 的 OS 类型,例如 linux

BUILDARCH 构建镜像所使用主机平台 的架构类型,例如 amd64

BUILDVARIANT构建镜像所使用主机平台 的变种,该变量可能为空,例如 v7

使用举例
例如我们要构建支持 linux/arm/v7 和 linux/amd64 两种架构的镜像。假设已经生成了两个平台对应的二进制文件:

bin/dist-linux-arm
bin/dist-linux-amd64

那么 Dockerfile 可以这样书写:

FROM scratch

# 使用变量必须申明
ARG TARGETOS
ARG TARGETARCH

COPY bin/dist-${TARGETOS}-${TARGETARCH} /dist

ENTRYPOINT ["dist"]

参考资料