目录

使用buildx构建多平台镜像

构建多平台镜像的几种方法

QEMU仿真

使用qemu 仿真出很多平台,buildx集成了该特性,可以实现在其他平台构建不需要修改dockerfile ,当它需要对不同的架构运行一个二进制文件时,它会自己从binfmt_misc 处理器中已经注册的架构去加载对应的二进制文件。当然,我们需要手动在binfmt_misc处理器里去注册我们想要的架构的。

本篇文章主要介绍使用该方法来实现多平台镜像构建。

使用不同节点来构建

这种方式性能较好,但是并未选择这种方式,如果想深入了解,可以参考其他资料。

官方例子:

1
2
3
4
5
$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 .

交叉编译

如果开发语言对交叉编译支持较好,可以使用dockerfiles的多阶段构建,可以构建时传入BUILDPLATFORMTARGETPLATFORM 参数选择运行的平台及编译的平台。

1
2
3
4
5
6
7
FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" > /log
FROM alpine
COPY --from=build /log /log

使用buildx构建

开启docker buildx

开启docker buildx插件,要求版本不低于19.03

第一种方法临时开启:

1
2
3
$ export DOCKER_CLI_EXPERIMENTAL=enabled


第二种方法:在config.json 中开启"experimental": "enabled"

1
2
3
4
5
6
7
$ vim ~/.docker/config.json
{
  ...
  "experimental": "enabled"
}
 

验证

1
2
3
4
5
$ docker buildx version 
github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7



开启binfmt_misc

如果使用的是docker desktop默认是已经启用binfmt_misc的了 内核版本最好大于3.x,我这里用的是5.x的内核。

这里使用[docker/binfmt](https://github.com/docker/binfmt) 开启特权模式启用binfmt_misc

1
docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64

检查是否开启成功

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ ls -l /proc/sys/fs/binfmt_misc/
total 0
-rw-r--r--. 1 root root 0 Jan  7 17:41 jexec
-rw-r--r--. 1 root root 0 Jan  7 17:43 qemu-aarch64
-rw-r--r--. 1 root root 0 Jan  7 17:43 qemu-arm
-rw-r--r--. 1 root root 0 Jan  7 17:43 qemu-ppc64le
-rw-r--r--. 1 root root 0 Jan  7 17:43 qemu-riscv64
-rw-r--r--. 1 root root 0 Jan  7 17:43 qemu-s390x
--w-------. 1 root root 0 Jan  7 17:41 register
-rw-r--r--. 1 root root 0 Jan  7 17:41 status
 

检查是否启用处理器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ grep -r "enabled" /proc/sys/fs/binfmt_misc/ 
/proc/sys/fs/binfmt_misc/qemu-riscv64:enabled
/proc/sys/fs/binfmt_misc/qemu-s390x:enabled
/proc/sys/fs/binfmt_misc/qemu-ppc64le:enabled
/proc/sys/fs/binfmt_misc/qemu-arm:enabled
/proc/sys/fs/binfmt_misc/qemu-aarch64:enabled
/proc/sys/fs/binfmt_misc/jexec:enabled
grep: /proc/sys/fs/binfmt_misc/register: Invalid argument
/proc/sys/fs/binfmt_misc/status:enabled

创建builder

需要创建一个新的docker builder,名称叫builder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ docker buildx create --name builder --use
builder
$ docker buildx ls
NAME/NODE      DRIVER/ENDPOINT             STATUS   PLATFORMS
builder    docker-container                     
  builder0 unix:///var/run/docker.sock inactive 
default *      docker                               
  default      default                     running  linux/amd64, linux/386


这时可以看到一个默认builder和我们新建的一个builder,目前新建的builder还处于inactive的状态。

通过如下方式启动新的builder。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ docker buildx inspect builder --bootstrap
[+] Building 63.8s (1/1) FINISHED                                                                                                                                                
 => [internal] booting buildkit                                                                                                                                            63.8s
 => => pulling image moby/buildkit:buildx-stable-1                                                                                                                         61.9s
 => => creating container buildx_buildkit_builder0                                                                                                                      1.9s
Name:   builder
Driver: docker-container

Nodes:
Name:      builder0
Endpoint:  unix:///var/run/docker.sock
Status:    running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6



构建多平台镜像

当前目录结构:

1
2
3
4
5
$ tree  
├── Dockerfile
└── test.go


Dockerfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM golang:1.15-alpine AS builder
WORKDIR /opt
RUN go build -o test .

FROM alpine
WORKDIR /opt
COPY --from=builder /opt/test .
CMD ["./test"]


test.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
        "fmt"
)

func main() {
  fmt.Println("test")
}


开始构建并上传,注意harbor 版本要在2.0 以上才支持。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ docker buildx build -t reg.xxxxxx.cn/app/test-golang:v1 --platform=linux/arm64,linux/amd64 --push . 

[+] Building 5.2s (22/22) FINISHED                                                                                                                                               
 => [internal] load build definition from Dockerfile                                                                                                                        0.0s
 => => transferring dockerfile: 92B                                                                                                                                         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/amd64 internal] load metadata for docker.io/library/golang:1.15-alpine                                                                                           2.0s
 => [linux/arm64 internal] load metadata for docker.io/library/alpine:latest                                                                                                3.1s
 => [linux/arm64 internal] load metadata for docker.io/library/golang:1.15-alpine                                                                                           2.1s
 => [linux/amd64 builder 1/4] FROM docker.io/library/golang:1.15-alpine@sha256:49b4eac11640066bc72c74b70202478b7d431c7d8918e0973d6e4aeb8b3129d2                             0.0s
 => => resolve docker.io/library/golang:1.15-alpine@sha256:49b4eac11640066bc72c74b70202478b7d431c7d8918e0973d6e4aeb8b3129d2                                                 0.0s
 => [linux/arm64 stage-1 1/3] FROM docker.io/library/alpine@sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436                                         0.1s
 => => resolve docker.io/library/alpine@sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436                                                             1.8s
 => [linux/arm64 builder 1/4] FROM docker.io/library/golang:1.15-alpine@sha256:49b4eac11640066bc72c74b70202478b7d431c7d8918e0973d6e4aeb8b3129d2                             0.1s
 => => resolve docker.io/library/golang:1.15-alpine@sha256:49b4eac11640066bc72c74b70202478b7d431c7d8918e0973d6e4aeb8b3129d2                                                 0.0s
 => [linux/amd64 stage-1 1/3] FROM docker.io/library/alpine@sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436                                         0.1s
 => => resolve docker.io/library/alpine@sha256:3c7497bf0c7af93428242d6176e8f7905f2201d8fc5861f45be7a346b5f23436                                                             0.0s
 => [internal] load build context                                                                                                                                           0.0s
 => => transferring context: 178B                                                                                                                                           0.0s
 => CACHED [linux/amd64 stage-1 2/3] WORKDIR /opt                                                                                                                           0.0s
 => CACHED [linux/amd64 builder 2/4] WORKDIR /opt                                                                                                                           0.0s
 => CACHED [linux/amd64 builder 3/4] ADD . /opt/                                                                                                                            0.0s
 => CACHED [linux/amd64 builder 4/4] RUN go build -o test .                                                                                                                 0.0s
 => CACHED [linux/amd64 stage-1 3/3] COPY --from=builder /opt/test .                                                                                                        0.0s
 => CACHED [linux/arm64 stage-1 2/3] WORKDIR /opt                                                                                                                           0.0s
 => CACHED [linux/arm64 builder 2/4] WORKDIR /opt                                                                                                                           0.0s
 => CACHED [linux/arm64 builder 3/4] ADD . /opt/                                                                                                                            0.0s
 => CACHED [linux/arm64 builder 4/4] RUN go build -o test .                                                                                                                 0.0s
 => CACHED [linux/arm64 stage-1 3/3] COPY --from=builder /opt/test .                                                                                                        0.0s
 => exporting to image                                                                                                                                                      1.8s
 => => exporting layers                                                                                                                                                     0.6s
 => => exporting manifest sha256:714e4f18895bb698c65392eda3b39e35b38df0e84c34ce1f604bfa8fdb466a77                                                                           0.0s
 => => exporting config sha256:0339ab69edd09e4038046c8c056305733f050918725b6f265780189c0bdd76e3                                                                             0.0s
 => => exporting manifest sha256:a5c4695278c99a1801c557e68145d7a1e61fa82288a9d2b43b1f85219dea5b8a                                                                           0.0s
 => => exporting config sha256:a69d7dfa854e466d743d09c3c95ebca68f2feb6cb335bc0aecc4a256cc95e68f                                                                             0.0s
 => => exporting manifest list sha256:77677ebebf2dda103c6cd8598018a1d9bb79e06102173588fef5c501e81f6cb3                                                                      0.0s
 => => pushing layers                                                                                                                                                       0.5s
 => => pushing manifest for reg.xxxxxx.cn/app/test-golang:v1  


开机启动

由于重启后binfmt_miscbuilder 都会停止,所以要进行一些修改。

binfmt_misc 加入开机启动脚本

1
2
3
4
cat >  /etc/rc.d/init.d/enable-binfmt.sh << EOF
#!/bin/bash
docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
EOF

builder 添加--restart=always

1
2
3
BUILDER=$(docker ps | grep buildkitd  | awk '{print $1}')

docker update --restart=always $BUILDER 

遇到的错误

上传镜像时x509: certificate signed by unknown authority

完整报错:

1
2
failed to solve: rpc error: code = Unknown desc = failed to do request: Head [<https://reg.xxxxxx.cn/v2/app/test-golang/blobs/sha256:0c1f186a05c7a4d0cb23b4a339473c5e96115be11f4deb24f74d3f2324120c87:>](<https://reg.xxxxxx.cn/v2/app/test-golang/blobs/sha256:0c1f186a05c7a4d0cb23b4a339473c5e96115be11f4deb24f74d3f2324120c87:>) x509: certificate signed by unknown authority

这是因为公司所用的harbor证书是自签名证书,暂时的解决方法是将harbor证书加到builder容器中。

1
2
3
4
5
$ BUILDER=$(docker ps | grep buildkitd  | awk '{print $1}')
$ docker cp ./harbor.crt $BUILDER:/usr/local/share/ca-certificates/ # harbor.crt为harbor ca证书
$ sudo docker exec $BUILDER update-ca-certificates
$ sudo docker restart $BUILDER 

重新上传镜像即可。

docker: ‘buildx’ is not a docker command.

开启buildx后还是报这个错,查看了下和之前成功安装的环境信息

有问题的:

1
2
cat /etc/redhat-release 
CentOS Linux release 7.3.1611 (Core)

正常的:

1
2
cat /etc/redhat-release 
CentOS Linux release 7.5.1804 (Core)

发现是linux版本有差异,进行更新后可以正常使用

1
yum update -y

参考链接

https://docs.docker.com/buildx/working-with-buildx/

https://zhuanlan.zhihu.com/p/227048978

https://github.com/docker/buildx/issues/80

https://github.com/bryant-rh/buildx-example