buildx中怎么构建多平台 Docker 镜像,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
方法一:直接在目标硬件上编译
如果你能够访问目标 CPU 架构的系统,并且该操作系统支持运行构建所需的各种工具,那么你可以直接在目标系统上编译程序。
以构建 Docker 镜像为例,你可以在树莓派上安装 Docker,然后在树莓派上通过 Dockerfile
直接构建 arm 平台的镜像。
如果无法访问目标 CPU 架构的系统该怎么办?有没有办法通过某种方式直接在当前系统上构建目标 CPU 架构的程序?请看下文...
方法二:模拟目标硬件
还记得我们小时候在各种网吧台球室之类的场合玩的街机游戏吗?放张图给你们回忆一下:
如果现在我们想重新体验以前玩过的街机游戏该怎么办?这时候就需要用到模拟器(Emulator)了。借助模拟器,我们可以让时光倒流,体验经典游戏的乐趣。
模拟器除了可以用来玩游戏之外,还可以用来跨 CPU 架构构建程序。最常用的模拟器是开源的 QEMU,QEMU 支持许多常见的 CPU 架构,包括 ARM
、Power-PC
和 RISC-V
等。通过模拟一个完整的操作系统,可以创建通用的 ARM 虚拟机,该虚拟机可以引导 Linux,设置开发环境,也可以在虚拟机内编译程序。
然而,模拟整个操作系统还是有点浪费,因为在这种模式下,QEMU 将会模拟整个系统,包括计时器、内存控制器、总线控制器等硬件。但编译程序根本不需要关心这些,还可以再精简些。
方法三:通过 binfmt_misc 模拟目标硬件的用户空间
在 Linux 上,QEMU
除了可以模拟完整的操作系统之外,还有另外一种模式叫用户态模式
(User mod)。该模式下 QEMU 将通过 binfmt_misc 在 Linux 内核中注册一个二进制转换处理程序,并在程序运行时动态翻译二进制文件,根据需要将系统调用从目标 CPU 架构转换为当前系统的 CPU 架构。最终的效果看起来就像在本地运行目标 CPU 架构的二进制文件。
通过 QEMU 的用户态模式,我们可以创建轻量级的虚拟机(chroot 或容器),然后在虚拟机系统中编译程序,和本地编译一样简单轻松。后面我们就会看到,跨平台构建 Docker 镜像用的就是这个方法。
方法四:使用交叉编译器
最后介绍一种嵌入式系统社区常用的方法:交叉编译(cross-compilation)。
交叉编译器是专门为在给定的系统平台上运行而设计的编译器,但是可以编译出另一个系统平台的可执行文件。例如,amd64
架构的 Linux 系统上的 C++ 交叉编译器可以编译出运行在 aarch74
(64-bit ARM) 架构的嵌入式设备上的可执行文件。再举个真实的例子,安卓设备的 APP 基本上都是通过这种方法来编译的。
从性能角度来看,该方法与方法一没什么区别,因为不需要模拟器的参与,几乎没有性能损耗。但交叉编译不具有通用性,它的复杂度取决于程序使用的语言,如果使用 Golang 的话,那就超级容易了。
在全民容器时代,我们讨论构建时不仅包括构建单个可执行文件,还包括构建容器镜像。而且构建容器镜像比上面说的方法更复杂,再加上 Docker 本身的复杂性,这几乎是一个老大难的问题。
但引入了新的实验性插件之后,构建多平台架构的 Docker 镜像就比以前容易多了,至于这个插件到底是啥,下文会详细介绍。
2. 构建多平台 Docker 镜像
利用 Docker 19.03 引入的插件 buildx,可以很轻松地构建多平台 Docker 镜像。buildx 是 docker build ...
命令的下一代替代品,它利用 BuildKit 的全部功能扩展了 docker build
的功能。
下面就来演示一下如何在短短几分钟内使用 buildx
构建出不同平台的 Docker 镜像。步骤如下:
启用 buildx 插件
要想使用 buildx
,首先要确保 Docker 版本不低于 19.03
,同时还要通过设置环境变量 DOCKER_CLI_EXPERIMENTAL
来启用。可以通过下面的命令来为当前终端启用 buildx 插件:
???? → export DOCKER_CLI_EXPERIMENTAL=enabled
验证是否开启:
???? → docker buildx version
github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7
如果在某些系统上设置环境变量 DOCKER_CLI_EXPERIMENTAL
不生效(比如 Arch Linux),你可以选择从源代码编译:
???? → export DOCKER_BUILDKIT=1
???? → docker build --platform=local -o . git://github.com/docker/buildx
???? → mkdir -p ~/.docker/cli-plugins && mv buildx ~/.docker/cli-plugins/docker-buildx
启用 binfmt_misc
> 如果你使用的是 Docker 桌面版(MacOS 和 Windows),默认已经启用了 binfmt_misc
,可以跳过这一步。
如果你使用的是 Linux,需要手动启用 binfmt_misc
。大多数 Linux 发行版都很容易启用,不过还有一个更容易的办法,直接运行一个特权容器,容器里面写好了设置脚本:
???? → docker run --rm --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d
> 建议将 Linux 内核版本升级到 4.x 以上,特别是 CentOS 用户,你可能会遇到错误。
验证是 binfmt_misc 否开启:
???? → ls -al /proc/sys/fs/binfmt_misc/
总用量 0
总用量 0
-rw-r--r-- 1 root root 0 11月 18 00:12 qemu-aarch74
-rw-r--r-- 1 root root 0 11月 18 00:12 qemu-arm
-rw-r--r-- 1 root root 0 11月 18 00:12 qemu-ppc64le
-rw-r--r-- 1 root root 0 11月 18 00:12 qemu-s390x
--w------- 1 root root 0 11月 18 00:09 register
-rw-r--r-- 1 root root 0 11月 18 00:12 status
验证是否启用了相应的处理器:
???? → cat /proc/sys/fs/binfmt_misc/qemu-aarch74
enabled
interpreter /usr/bin/qemu-aarch74
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7
mask ffffffffffffff00fffffffffffffffffeffff
从默认的构建器切换到多平台构建器
Docker 默认会使用不支持多 CPU 架构的构建器,我们需要手动切换。
先创建一个新的构建器:
???? → docker buildx create --use --name mybuilder
启动构建器:
???? → docker buildx inspect mybuilder --bootstrap
[+] Building 5.0s (1/1) FINISHED
=> [internal] booting buildkit 5.0s
=> => pulling image moby/buildkit:buildx-stable-1 4.4s
=> => creating container buildx_buildkit_mybuilder0 0.6s
Name: mybuilder
Driver: docker-container
Nodes:
Name: mybuilder0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
查看当前使用的构建器及构建器支持的 CPU 架构,可以看到支持很多 CPU 架构:
???? → docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default docker
default default running linux/amd64, linux/386
构建多平台镜像
现在我们就可以构建支持多 CPU 架构的镜像了!假设有一个简单的 golang 程序源码:
???? → cat hello.go
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Printf("Hello, %s!\n", runtime.GOARCH)
}
创建一个 Dockerfile 将该应用容器化:
???? → cat Dockerfile
FROM golang:alpine AS builder
RUN mkdir /app
ADD . /app/
WORKDIR /app
RUN go build -o hello .
FROM alpine
RUN mkdir /app
WORKDIR /app
COPY --from=builder /app/hello .
CMD ["./hello"]
这是一个多阶段构建 Dockerfile,使用 Go 编译器来构建应用,并将构建好的二进制文件拷贝到 alpine 镜像中。
现在就可以使用 buildx 构建一个支持 arm、arm64 和 amd64 多架构的 Docker 镜像了,同时将其推送到 Docker Hub:
???? → docker buildx build -t yangchuansheng/hello-arch --platform=linux/arm,linux/arm64,linux/amd64 . --push
> 需要提前通过 docker login
命令登录认证 Docker Hub。
现在就可以通过 docker pull mirailabs/hello-arch
拉取刚刚创建的镜像了,Docker 将会根据你的 CPU 架构拉取匹配的镜像。
背后的原理也很简单,之前已经提到过了,buildx 会通过 QEMU
和 binfmt_misc
分别为 3 个不同的 CPU 架构(arm,arm64 和 amd64)构建 3 个不同的镜像。构建完成后,就会创建一个 manifest list,其中包含了指向这 3 个镜像的指针。
如果想将构建好的镜像保存在本地,可以将 type
指定为 docker
,但必须分别为不同的 CPU 架构构建不同的镜像,不能合并成一个镜像,即:
???? → docker buildx build -t yangchuansheng/hello-arch --platform=linux/arm -o type=docker .
???? → docker buildx build -t yangchuansheng/hello-arch --platform=linux/arm64 -o type=docker .
???? → docker buildx build -t yangchuansheng/hello-arch --platform=linux/amd64 -o type=docker .
测试多平台镜像
由于之前已经启用了 binfmt_misc
,现在我们就可以运行任何 CPU 架构的 Docker 镜像了,因此可以在本地系统上测试之前生成的 3 个镜像是否有问题。
首先列出每个镜像的 digests
:
???? → docker buildx imagetools inspect yangchuansheng/hello-arch
Name: docker.io/yangchuansheng/hello-arch:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:ec55f5ece9a12db0c6c367acda8fd1214f50ee502902f97b72f7bff268ebc35a
Manifests:
Name: docker.io/yangchuansheng/hello-arch:latest@sha256:38e083870044cfde7f23a2eec91e307ec645282e76fd0356a29b32122b11c639
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v7
Name: docker.io/yangchuansheng/hello-arch:latest@sha256:de273a2a3ce92a5dc1e6f2d796bb85a81fe1a61f82c4caaf08efed9cf05af66d
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm64
Name: docker.io/yangchuansheng/hello-arch:latest@sha256:8b735708d7d30e9cd6eb993449b1047b7229e53fbcebe940217cb36194e9e3a2
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
运行每一个镜像并观察输出结果:
???? → docker run --rm docker.io/yangchuansheng/hello-arch:latest@sha256:38e083870044cfde7f23a2eec91e307ec645282e76fd0356a29b32122b11c639
Hello, arm!
???? → docker run --rm docker.io/yangchuansheng/hello-arch:latest@sha256:de273a2a3ce92a5dc1e6f2d796bb85a81fe1a61f82c4caaf08efed9cf05af66d
Hello, arm64!
???? → docker run --rm docker.io/yangchuansheng/hello-arch:latest@sha256:8b735708d7d30e9cd6eb993449b1047b7229e53fbcebe940217cb36194e9e3a2
Hello, amd64!
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注天达云行业资讯频道,感谢您对天达云的支持。