构建多平台镜像的几种方法
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
的多阶段构建,可以构建时传入BUILDPLATFORM
和TARGETPLATFORM
参数选择运行的平台及编译的平台。
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_misc
和builder
都会停止,所以要进行一些修改。
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版本有差异,进行更新后可以正常使用
参考链接
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