首页 体育 教育 财经 社会 娱乐 军事 国内 科技 互联网 房产 国际 女人 汽车 游戏

Docker 镜像如何做到“一次构建,到处运行”?

2020-01-16

译者 | 金灵杰

本文将首要介绍 Docker 19.03 怎么简化支撑多硬件渠道镜像的构建。

在每个黑客的职业生涯中总有这么一个时间需求为另一种 CPU 架构编译运用程序。这种场景或许呈现在为树莓派项目编译运用程序,为嵌入式设备创立自定义镜像,或许让自己的软件支撑不同渠道。亦或是,咱们仅仅想知道这个进程是怎么样的,或许猎奇终究汇编代码和桌面电脑上无处不在的 x86-64/amd64 架构汇编有何差异。

不论是哪种原因,一般咱们都需求整理好行装进行一段朝圣之旅。可是这个旅程不是登上孤单的山顶,而是通向阴间深渊,是一段从开发运用程序的阳光平原走向计算机体系结构的漆黑窟窿之旅:底层体系和嵌入式改变带来的难以捉摸的国际。介于这次行进的远景堪忧,大部分黑客终究经过 Ctrl+Z 完毕了旅程,回到了地上,一边喘气一边正告火伴穿插编译、QEMU 和 chroot 的恐惧之处。

好了,我或许有点夸张了。可是本相是为其他 CPU 架构构建运用程序没有那么开门见山。多亏了 Docker 19.03 带来实验性的插件,让多架构构建比以往要便利许多。

为了了解 Docker 对多架构构建支撑的重要性,首要咱们需求了解怎么为生疏架构构建运用程序。

读者假如对本节概念现已了解,或许仅仅想知道怎么构建镜像,能够越过本节。

让咱们快速了解下其时关于为生疏架构编译运用程序的办法。

假如咱们能够拜访方针架构硬件,一同操作体系上有咱们所需的一切构建数据,那么就能够直接在硬件上编译运用程序。

例如,对咱们特定场景下构建多架构 Docker 镜像,能够在树莓派上装置 Docker 运转时环境,然后和在开发机上相同,直接在上面经过运用程序的 Dockerfile 构建镜像。该办法是可行的,因为树莓派的官方操作体系 Raspbian 支撑本地装置 Docker。

可是,假如咱们无法办法便利的拜访方针硬件呢?咱们能够在开发机器上直接构建非本地架构的运用程序吗?

办法 2:模仿方针硬件

假如咱们能够拜访方针架构硬件,一同操作体系上有咱们所需的一切构建数据,那么就能够直接在硬件上编译运用程序。

例如,对咱们特定场景下构建多架构 Docker 镜像,能够在树莓派上装置 Docker 运转时环境,然后和在开发机上相同,直接在上面经过运用程序的 Dockerfile 构建镜像。该办法是可行的,因为树莓派的官方操作体系 Raspbian 支撑本地装置 Docker。

可是,假如咱们无法办法便利的拜访方针硬件呢?咱们能够在开发机器上直接构建非本地架构的运用程序吗?

还记得和 16 位任天堂游戏机一同的高兴韶光吗?其时我仅仅一个小孩子,可是当我长大一点之后,我发现对比如《超级玛丽》和《时空之轮》等经典游戏十分思念。不过我没有机会具有一台超级任天堂游戏机,可是多亏了像 ZSNES 这样的模仿器,让我能回到曩昔,在 32 位个人电脑上体会这些经典游戏带来的趣味。

经过模仿器,咱们不只能够玩电子游戏,还能够构建非本地二进制文件。当然这儿不是运用 ZSNES,而是运用愈加强壮更灵敏的模仿器:QEMU。QEMU 是一个自在且开源的模仿器,支撑许多通用架构,包括:ARM、Power-PC 和 RISC-V。经过运转一个全功用模仿器,咱们能够发动一个能够运转 Linux 操作体系的通用 ARM 虚拟机,然后在虚拟机中设置开发环境,编译运用程序。

可是,假如细心考虑下,一个全功用虚拟机有一些浪费资源。在该形式下,QEMU 会模仿整个体系,包括比如定时器、内存控制器、SPI 和 I2C 总线控制器等硬件。可是大部分情况下,咱们编译运用程序不会关怀以上所说到的硬件特性。还能更好么?

在 Linux 体系上,QEMU 有别的一种操作形式,能够经过用户形式模仿器来运转非本地架构的二进制程序。该形式下,QEMU 会越过办法 2 中描绘的对整个方针体系硬件的模仿,取而代之的是经过 binfmt_misc 在 Linux 内核注册一个二进制格局处理程序,将生疏二进制代码阻拦并转化后再履行,一同将体系调用按需从方针体系转化成其时体系。终究关于用户来说,他们会发现能够在本机运转这些异构二进制程序。

经过用户态模仿器和 QEMU,咱们能够经过轻量级虚拟化来装置其他 Linux 发行版,并像在本地相同编译咱们需求的异构二进制程序。

下面咱们会看到这将会是构建多架构 Docker 镜像的可选办法。

办法 4:运用穿插编译器

在 Linux 体系上,QEMU 有别的一种操作形式,能够经过用户形式模仿器来运转非本地架构的二进制程序。该形式下,QEMU 会越过办法 2 中描绘的对整个方针体系硬件的模仿,取而代之的是经过 binfmt_misc 在 Linux 内核注册一个二进制格局处理程序,将生疏二进制代码阻拦并转化后再履行,一同将体系调用按需从方针体系转化成其时体系。终究关于用户来说,他们会发现能够在本机运转这些异构二进制程序。

经过用户态模仿器和 QEMU,咱们能够经过轻量级虚拟化来装置其他 Linux 发行版,并像在本地相同编译咱们需求的异构二进制程序。

下面咱们会看到这将会是构建多架构 Docker 镜像的可选办法。

最终,咱们还有一种在嵌入式体系社区规范的做法:穿插编译。

穿插编译器是一个特别的编译器,它运转在主机架构上,可是能够为不同的方针架构生成的二进制程序。例如,咱们能够有一个 amd64 架构的 C++ 穿插编译器,方针架构是一个 aarch64的嵌入式设备。依据这种办法的一个实际中的比如是,国际上数十亿安卓设备都运用这种办法来构建软件。

从性能上考虑,这种办法有和直接在方针硬件上构建相同的功率,因为它没有运转在模仿器上。可是穿插编译的变数取决于运用的编程言语,假如是 Go 言语就十分便利。

留意前面说到的一切编译办法都仅仅生成单一的运用程序二进制文件。关于现代容器来说,当咱们引进 Docker 镜像的时分,不只仅是关于构建独自的二进制文件,而是构建一整个异构容器镜像!这比之前说的要愈加费事。

假如一切这些听上去很苦楚,不要伤心,因为构建非本地渠道二进制程序原本就很苦楚。在此之上添加 Docker 带来的复杂度,看起来应该留给专家来处理。

感谢最新版别 Docker 运转时环境带来的实验性扩展,构建多架构镜像现在比曾经便利多了。

构建多架构 Docker 镜像

留意前面说到的一切编译办法都仅仅生成单一的运用程序二进制文件。关于现代容器来说,当咱们引进 Docker 镜像的时分,不只仅是关于构建独自的二进制文件,而是构建一整个异构容器镜像!这比之前说的要愈加费事。

假如一切这些听上去很苦楚,不要伤心,因为构建非本地渠道二进制程序原本就很苦楚。在此之上添加 Docker 带来的复杂度,看起来应该留给专家来处理。

感谢最新版别 Docker 运转时环境带来的实验性扩展,构建多架构镜像现在比曾经便利多了。

为了能够更便利的构建多架构 Docker 镜像,咱们能够运用最近发布的 Docker 扩展:buildx。buildx 是下一代规范 docker build 指令的前端,既咱们了解的用于构建 Docker 镜像的指令。经过凭借 BuildKit 的一切功用,buildx 扩展了表中 docker build 指令的功用,成为 Docker 构建体系的新后端。

让咱们花几分钟看下怎么运用 buildx 来构建多架构镜像。

要运用 buildx,首要要承认咱们的 Docker 运转时环境现已是最新版别 19.03。新版别中,buildx 事实上现已默许和 Docker 绑缚在一同,可是需求经过设置环境变量 DOCKER_CLI_EXPERIMENTAL 来敞开。让咱们在其时指令行会话中敞开:

$ export DOCKER_CLI_EXPERIMENTAL=enabled

经过查看版原本验证现在咱们现已能够运用 buildx:

$ docker buildx version

github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7

可选过程:从源码构建

要运用 buildx,首要要承认咱们的 Docker 运转时环境现已是最新版别 19.03。新版别中,buildx 事实上现已默许和 Docker 绑缚在一同,可是需求经过设置环境变量 DOCKER_CLI_EXPERIMENTAL 来敞开。让咱们在其时指令行会话中敞开:

$ export DOCKER_CLI_EXPERIMENTAL=enabled

经过查看版原本验证现在咱们现已能够运用 buildx:

$ docker buildx version

github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7

$ docker buildx version

github.com/docker/buildx v0.3.1-tp-docker 6db68d029599c6710a32aa7adcba8e5a344795a7

假如要运用最新版别的 buildx,或许在其时环境下设置 DOCKER_CLI_EXPERIMENTAL 环境变量不收效,咱们能够从源码构建 buildx:

$ 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

$ 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

假如读者运用的是 Mac 或许 Windows 版别 Docker 桌面版,能够越过这个过程,因为 binfmt_misc 默许敞开。

假如运用是 Linux 体系,需求设置 binfmt_misc。在大部分发行版中,这个操作十分简略,可是现在能够经过运转一个特权 Docker 容器来更便利的设置:

$ docker run --rm --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d

经过查看 QEMU 处理程序来验证 binfmt_misc 设置是否正确:

$ ls -al /proc/sys/fs/binfmt_misc/

total 0

drwxr-xr-x 2 root root 0 Nov 12 09:19 .

dr-xr-xr-x 1 root root 0 Nov 12 09:16 ..

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-aarch64

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-arm

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-ppc64le

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-s390x

--w------- 1 root root 0 Nov 12 09:19 register

-rw-r--r-- 1 root root 0 Nov 12 09:19 status

然后,验证下指定架构处理程序现已启用,例如:

$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64

enabled

interpreter /usr/bin/qemu-aarch64

flags: OCF

offset 0

magic 7f454c460201010000000000000000000200b7

mask ffffffffffffff00fffffffffffffffffeffff

过程 3:将默许 Docker 镜像构建器切换成多架构构建器

假如读者运用的是 Mac 或许 Windows 版别 Docker 桌面版,能够越过这个过程,因为 binfmt_misc 默许敞开。

假如运用是 Linux 体系,需求设置 binfmt_misc。在大部分发行版中,这个操作十分简略,可是现在能够经过运转一个特权 Docker 容器来更便利的设置:

$ docker run --rm --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d

经过查看 QEMU 处理程序来验证 binfmt_misc 设置是否正确:

$ ls -al /proc/sys/fs/binfmt_misc/

total 0

drwxr-xr-x 2 root root 0 Nov 12 09:19 .

dr-xr-xr-x 1 root root 0 Nov 12 09:16 ..

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-aarch64

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-arm

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-ppc64le

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-s390x

--w------- 1 root root 0 Nov 12 09:19 register

-rw-r--r-- 1 root root 0 Nov 12 09:19 status

$ ls -al /proc/sys/fs/binfmt_misc/

total 0

drwxr-xr-x 2 root root 0 Nov 12 09:19 .

dr-xr-xr-x 1 root root 0 Nov 12 09:16 ..

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-aarch64

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-arm

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-ppc64le

-rw-r--r-- 1 root root 0 Nov 12 09:25 qemu-s390x

--w------- 1 root root 0 Nov 12 09:19 register

-rw-r--r-- 1 root root 0 Nov 12 09:19 status

然后,验证下指定架构处理程序现已启用,例如:

$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64

enabled

interpreter /usr/bin/qemu-aarch64

flags: OCF

offset 0

magic 7f454c460201010000000000000000000200b7

mask ffffffffffffff00fffffffffffffffffeffff

$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64

enabled

interpreter /usr/bin/qemu-aarch64

flags: OCF

offset 0

magic 7f454c460201010000000000000000000200b7

mask ffffffffffffff00fffffffffffffffffeffff

默许情况下,Docker 会运用旧的构建器,不支撑多架构构建。

为了创立一个新的支撑多架构的构建器,运转:

$ docker buildx create --use --name mybuilder

验证新的构建器现已收效:

$ docker buildx ls

NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS

mybuilder * docker-container

mybuilder0 unix:///var/run/docker.sock inactive

default docker

default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

$ docker buildx ls

NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS

mybuilder * docker-container

mybuilder0 unix:///var/run/docker.sock inactive

default docker

default default running linux/amd64, linux/arm64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6

搞定。现在 Docker 会运用新的构建器,支撑构建多架构镜像。

好了,现在咱们总算能够开端构建一个多架构镜像了。为了演示这个功用,咱们需求一个示例运用。

让咱们创立一个简略的 Go 运用程序,输出其时运转环境的架构信息:

$ cat hello.go

package main


import


func main {

fmt.Printf

}

让咱们创立一个 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 Linux 镜像创立成最小镜像。

现在,让咱们运用 buildx 来构建一个支撑 arm、arm64 和 amd64 架构的多架构镜像,并一次性推送到 Docker Hub:

$ docker buildx build -t mirailabs/hello-arch --platform=linux/arm,linux/arm64,linux/amd64 . --push

是的,便是这样。现在 Docker Hub 上咱们有了 支撑 arm、arm64 和 amd64 架构的多架构 Docker 镜像。当咱们运转 docker pull mirailabs/hello-arch 时,Docker 会依据机器的架构来获取匹配的镜像。

假如读者要问 buildx 是怎么完成这个魔法的?好吧,在指令的背面,buildx 运用 QEMU 和 binfmt_misc 创立了三个 Docker 镜像。当构建完成后,Docker 会创立一个清单,其间包括这三个镜像以及他们对应的架构。换句话说,“多架构镜像”实际上是一个清单,列举了每个架构对应的镜像。

过程 5:测验多架构镜像

好了,现在咱们总算能够开端构建一个多架构镜像了。为了演示这个功用,咱们需求一个示例运用。

让咱们创立一个简略的 Go 运用程序,输出其时运转环境的架构信息:

$ cat hello.go

package main


import


func main {

fmt.Printf

}

$ cat hello.go

package main


import


func main {

fmt.Printf

}

让咱们创立一个 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 ]

$ 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 Linux 镜像创立成最小镜像。

现在,让咱们运用 buildx 来构建一个支撑 arm、arm64 和 amd64 架构的多架构镜像,并一次性推送到 Docker Hub:

$ docker buildx build -t mirailabs/hello-arch --platform=linux/arm,linux/arm64,linux/amd64 . --push

是的,便是这样。现在 Docker Hub 上咱们有了 支撑 arm、arm64 和 amd64 架构的多架构 Docker 镜像。当咱们运转 docker pull mirailabs/hello-arch 时,Docker 会依据机器的架构来获取匹配的镜像。

假如读者要问 buildx 是怎么完成这个魔法的?好吧,在指令的背面,buildx 运用 QEMU 和 binfmt_misc 创立了三个 Docker 镜像。当构建完成后,Docker 会创立一个清单,其间包括这三个镜像以及他们对应的架构。换句话说,“多架构镜像”实际上是一个清单,列举了每个架构对应的镜像。

让咱们来快速测验下多架构镜像,以保证它们都能够正常作业。因为咱们现已设置了 binfmt_misc,因此在开发机器上现已能够履行任何架构的镜像了。

首要,列出每个镜像的散列值:

$ docker buildx imagetools inspect mirailabs/hello-arch

Name: docker.io/mirailabs/hello-arch:latest

MediaType: application/vnd.docker.distribution.manifest.list.v2+json

Digest: sha256:bbb246e520a23e41b0c6d38b933eece68a8407eede054994cff43c9575edce96


Manifests:

Name: docker.io/mirailabs/hello-arch:latest@sha256:5fb57946152d26e64c8303aa4626fe503cd5742dc13a3fabc1a890adfc2683df

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/arm/v7


Name: docker.io/mirailabs/hello-arch:latest@sha256:cc6e91101828fa4e464f7eddec3fa7cdc73089560cfcfe4af16ccc61743ac02b

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/arm64


Name: docker.io/mirailabs/hello-arch:latest@sha256:cd0b32276cdd5af510fb1df5c410f766e273fe63afe3cec5ff7da3f80f27985d

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/amd64

$ docker buildx imagetools inspect mirailabs/hello-arch

Name: docker.io/mirailabs/hello-arch:latest

MediaType: application/vnd.docker.distribution.manifest.list.v2+json

Digest: sha256:bbb246e520a23e41b0c6d38b933eece68a8407eede054994cff43c9575edce96


Manifests:

Name: docker.io/mirailabs/hello-arch:latest@sha256:5fb57946152d26e64c8303aa4626fe503cd5742dc13a3fabc1a890adfc2683df

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/arm/v7


Name: docker.io/mirailabs/hello-arch:latest@sha256:cc6e91101828fa4e464f7eddec3fa7cdc73089560cfcfe4af16ccc61743ac02b

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/arm64


Name: docker.io/mirailabs/hello-arch:latest@sha256:cd0b32276cdd5af510fb1df5c410f766e273fe63afe3cec5ff7da3f80f27985d

MediaType: application/vnd.docker.distribution.manifest.v2+json

Platform: linux/amd64

有了这些散列值的协助,咱们能够逐个运转镜像,并调查其输出:

$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:5fb57946152d26e64c8303aa4626fe503cd5742dc13a3fabc1a890adfc2683df

Hello, arm!


$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:cc6e91101828fa4e464f7eddec3fa7cdc73089560cfcfe4af16ccc61743ac02b

Hello, arm64!


$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:cd0b32276cdd5af510fb1df5c410f766e273fe63afe3cec5ff7da3f80f27985d

Hello, amd64!

$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:5fb57946152d26e64c8303aa4626fe503cd5742dc13a3fabc1a890adfc2683df

Hello, arm!


$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:cc6e91101828fa4e464f7eddec3fa7cdc73089560cfcfe4af16ccc61743ac02b

Hello, arm64!


$ docker run --rm docker.io/mirailabs/hello-arch:latest@sha256:cd0b32276cdd5af510fb1df5c410f766e273fe63afe3cec5ff7da3f80f27985d

Hello, amd64!

看上去很简略,不是么?

归纳一下,本文咱们了解了软件支撑多 CPU 架构带来的应战,以及 Docker 的实验性扩展 buildx 怎么协助咱们处理这些应战。经过运用 buildx,咱们能够快速构建一个多架构 Docker 镜像,支撑 arm、arm64 和 amd64 架构,而不需求修正 Dockerfile。一同这个镜像能够推送到 Docker Hub,任何 Docker 支撑的渠道都能够依据自己的架构拉取对应的镜像。

未来,buildx 才能很有或许成为规范 docker build 指令的一部分,咱们能够不需求为运用这个功用做额定设置。把穿插编译运用程序比作跌入深渊的故事,不就将变成原始时代的鬼故事了。

行进,无惧多架构!

参考文献

归纳一下,本文咱们了解了软件支撑多 CPU 架构带来的应战,以及 Docker 的实验性扩展 buildx 怎么协助咱们处理这些应战。经过运用 buildx,咱们能够快速构建一个多架构 Docker 镜像,支撑 arm、arm64 和 amd64 架构,而不需求修正 Dockerfile。一同这个镜像能够推送到 Docker Hub,任何 Docker 支撑的渠道都能够依据自己的架构拉取对应的镜像。

未来,buildx 才能很有或许成为规范 docker build 指令的一部分,咱们能够不需求为运用这个功用做额定设置。把穿插编译运用程序比作跌入深渊的故事,不就将变成原始时代的鬼故事了。

行进,无惧多架构!

https://engineering.docker.com/2019/04/multi-arch-images/

https://engineering.docker.com/2019/06/getting-started-with-docker-for-arm-on-linux/

https://www.youtube.com/watch?v=nrBYUw1Pz5I

https://docs.docker.com/docker-for-mac/multi-arch/

https://mirailabs.io/blog/multiarch-docker-with-buildx/

热门文章

随机推荐

推荐文章