多架构镜像三部曲(二)构建

本文介绍了如何构建多架构镜像,包括基于目标架构机器的镜像构建和基于交叉编译的方式。通过Docker manifest和Buildx工具,详细讲解了如何在AMD64和ARM64平台上构建、组合及验证多架构镜像,以实现跨平台应用部署。
摘要由CSDN通过智能技术生成

大家好,我是温玉。今天给大家分享的内容是 “多架构镜像构建” 专题,希望给有在不同架构平台上交付应用需求的同学提供一些帮助。

多架构镜像构建(multi-architecture)

在计算机的发展过程中冯诺依曼计算机体系结构是现代计算机组成的基础,在此基础上各组件都有诞生许多优秀的硬件,而计算机硬件的核心之一就是 CPU。计算机由于 CPU 指令集的不同,可以分为精简指令集计算机(RISC)和复杂指令集计算机(CISC),软件在不同的指令集硬件上运行需要编译成对应的可执行文件。典型的有英国 Acorn 公司设计 ARM 精简指令集架构处理器(如 arm64)和 美国 AMD 公司设计的 AMD 复杂指令集架构处理器(如 amd64/x86_64)。

目前常见的精简指令集 CPU 包括 DEC Alpha、ARC(英语:ARC (processor))、ARM、AVR、MIPS、PA-RISC、Power ISA(包括PowerPC、PowerXCell)、RISC-V 和 SPARC等。属于复杂指令集的处理器有 CDC 6600、System/360、VAX、PDP-11、Motorola 68000家族、x86、AMD Opteron等[1]。

计算机软件是一系列按照特定顺序组织的电脑数据和指令,是电脑中的非有形部分。软件类型可以分为编程语言软件、操作系统软件和应用软件;操作系统软件就是最基础的软件,由操作系统来统一管理计算机相关硬件,使其能够协同工作为上层应用软件提供运行环境。操作系统软件目前主流的有 Linux、Window、Unix 以及一些特殊的大型机等特殊设备的操作系统等,其他发行版本大都是基于这些系统衍生而来。

应用软件是运行在计算机硬件和操作系统之上的,为满足用户不同领域、不同问题的应用需求而提供的那部分软件。由于低层的硬件和操作系统的不同,应用软件在不同架构平台上需要重新编译才能执行;容器服务也是应用,在不同的平台运行容器需要编译打包制作成不同类型的镜像,这也是本文核心讲诉的内容。

镜像、manifests 与 manifests list

说到镜像,就离不开对其含义的理解,特别是在多架构场景下有多出了 manifests 和 manifests list 的概念。

镜像是由一组元数据信息(镜像的架构、默认配置、构建镜像的容器配置、所有镜像层layer 等)和 rootfs 组成的只读的文件归档,该归档能够作为容器运行的只读环境基础,结合可读写层提供容器运行环境。

与镜像不同,manifests 是镜像的描述清单,是在镜像的上层的又一种抽象,这个清单文件中也包含了有关于镜像的信息,如 layer 层、大小和摘要信息。而 manifests list 就是在这个描述清单中有多种类型的 manifest 镜像描述信息,这个描述清单列表可以通过镜像 TAG 关联访问。

可以简单的理解一个 manifests list 就是用一个 TAG 来关联相同功能的多个不同架构平台的镜像,就是常说的多架构镜像。这里的多架构一般是指不同种类的 CPU (如 amd64,arm64)和 操作系统(如:linux、windows)。

manifests lists 可以在本地组合创建然后 push 推送到远程 Registry 仓库,也可以直接在远程 Registry 仓库中组合创建,但他不可以直接被作为容器运行的镜像。实际上在 docker pulldocker run 在拉取容器镜像时,客户端会自动根据当前机器的 CPU 和 操作系统从 manifests list 中下载自己需要的相关文件,即只下载符合本机器运行的容器镜像,而不是下载 manifests lists 中所有的内容。

本文中的描述涉及多架构镜像manifests lists时,他们两者的概念时等价的。

多架构镜像构建方法

对于多平台架构镜像的发布主要有两类方式,使用基于目标架构机器的镜像构建结果组合方式和基于交叉编译的镜像构建方式两种方法

  • 基于目标架构机器的镜像构建结果组合方式

如果我们有并且可以访问目标架构机器,同时该机器操作系统支持运行构建所需的各种环境和工具,那么就可以直接分别在目标架构机器上编译应用程序,进行镜像的构建,然后通过拼接组合的方式来发布一个多架构镜像的 manifests list

例如分别构建后组合成多架构镜像:

  • 在 ARM 的服务器上构建 arm64 镜像 : harbor.example-domain.com/library/myapp:v1-arm64
  • 在 AMD 的服务器上构建 amd64 镜像: harbor.example-domain.com/library/myapp:v1-amd64
  • 将 arm64 和 amd64 的镜像组合成多平台架构镜像: harbor.example-domain.com/library/myapp:v1-multi

需要多少种类型的应用,就分别在该类型的机器上编译打包镜像,然后通过一定的方式或工具就可以将多种类型的镜像组合成多架构镜像。

这种方式的优点是不需要特殊改造升级就可以支持;缺点也比较明显,多个不同平台的环境管理维护复杂。

  • 基于交叉编译的镜像构建方式

交叉编译是在一个平台上生成另一个平台上的可执行代码。将交叉编译的可执行代码打包成镜像就是想要的目标架构镜像了,但容器的交叉编译构建镜像依赖 QEMU 的虚拟化技术。

使用 QEMU 虚拟化技术可以虚拟化出服务器相关的硬件设备,常用来在单台高性能物理服务器上虚拟化出多台虚拟机场景,例如 VirtalBox、Vmware、KVM 等虚拟化工具。对于多架构镜像也是可以选择这种方式创建出不同架构平台的虚拟机,然后使用 基于目标架构机器的镜像构建结果组合方式 来发布多架构镜像;但是这种方式除了管理复杂外,虚拟化本身也会有较多的资源损耗浪费。

QEMU 是一种虚拟化技术,传统的 KVM 虚拟机技术就是基于 QEMU 技术,QEMU 支持虚拟化出许多常见的 CPU 架构,包括 ARM、Power-PC 和 RISC-V 等。

目前技术上已经支持在宿主机系统内核支持虚拟化的情况下,可以直接基于目标架构指令的方式来构建镜像。该方式不需要事先虚拟化出各种平台的硬件环境,也不需要安装各种操作系统环境等复杂的操作,同时也不会有大量的虚拟化本身的资源损耗,通过内核支持在编译时直接编译成对应架构的可执行文件,然后构建出目标架构的容器镜像。

这种方式的优点是构建便捷,资源利用率高;缺点是对操作系统内核版本有一定的要求。

基于目标架构机器的镜像构建结果组合方式

本文示例是使用一份 go 代码 get-cpu-os.go 来进行说明,这段代码的功能就是运行时输出当前运行机器的 CPU 架构和操作系统,以此场景来构建和验证多架构镜像的创建过程和使用流程。

代码内容如下:

package main

import (
   "fmt"
   "runtime"
)

func main() {
   fmt.Println("CPU Arch: ", runtime.GOARCH)
   fmt.Println("Operating System:", runtime.GOOS)
}

将源代码编译过程和打包过程放在一个 Dockerfile 中,使用 Dockerfile 的多阶段构建的方式来一次编译和构建镜像。当然,也可以分成两个独立的 Dockerfile,先编译出对应的二进制文件,然后再使用二进制文件构建容器镜像。

构建使用的 Dockerfile 内容如下:

FROM golang AS builder
WORKDIR /gobuild
COPY get-cpu-os.go  .
RUN go build get-cpu-os.go

FROM golang
WORKDIR /gorun
COPY --from=builder /gobuild/get-cpu-os .
CMD ["./get-cpu-os"]

有了源码代码和 Dockerfile ,在构建节点就可以来执行命令构建容器镜像。

构建 amd64 镜像

选择一台 AMD64 机器作为镜像构建机器,来构建可在 AMD64 架构机器上运行的容器镜像。

Intel 的 X86_64 架构 CPU 使用的是 AMD 的技术,因此在大多数情况下 X86_64 架构和 AMD64 架构是等价的。

可以通过 lscpu 命令查看机器的 CPU 硬件相关系信息,如下 CPU 信息表明其是 Architecture: x86_64 架构的机器。

相关的 CPU 和操作系统内核相关信息:

# lscpu 
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              16
On-line CPU(s) list: 0-15
Thread(s) per core:  1
Core(s) per socket:  1
Socket(s):           16
NUMA node(s):        1
Vendor ID:           GenuineIntel
CPU family:          6
Model:               61
Model name:          Intel Core Processor (Broadwell)
Stepping:            2
CPU MHz:             2394.456
BogoMIPS:            4788.91
Hypervisor vendor:   KVM
Virtualization type: full
L1d cache:           32K
L1i cache:           32K
L2 cache:            4096K
L3 cache:            16384K
NUMA node0 CPU(s):   0-15
Flags:               fpu vme de pse tsc......

除了 CPU ,还需要确认其操作系统类型。在 Unix 以及 类 Unix 等系统中,可以通过 uname -a 命令来查看内核相关的信息。

如示例中可看出这是一台 GNU/Linux 的系统,其内核版本是 4.18.0-80

# uname -a
Linux localhost 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

在该机器上通过 docker build 工具来构建镜像

# docker build --no-cache  -t zhaowenyu/get-cpu-os:latest-amd64 .

该命令是构建一个容器镜像,参数说明:

  • --no-cache: 配置在构建过程中不使用缓存,所有步骤都重新构建。
  • -t zhaowenyu/get-cpu-os:latest-amd64: 配置构建的目标镜像的 TAG 名称。
  • .: 这里的点是构建的上下文的初始位置,发送给 Docker Damon 的相关文件都是以此为相对初始路径。

镜像构建过程详细输出

# docker build --no-cache  -t zhaowenyu/get-cpu-os:latest-amd64 .
Sending build context to Docker daemon  6.656kB
Step 1/8 : FROM golang AS builder
 ---> 7d1902a99d63
Step 2/8 : WORKDIR /gobuild
 ---> Running in fb4f6d53c372
Removing intermediate container fb4f6d53c372
 ---> ad6d09ac6b9a
Step 3/8 : COPY  get-cpu-os.go  .
 ---> cb0fc4f43012
Step 4/8 : RUN go build get-cpu-os.go
 ---> Running in 2a623a44a760
Removing intermediate container 2a623a44a760
 ---> a8b499ec1a66
Step 5/8 : FROM golang
 ---> 7d1902a99d63
Step 6/8 : WORKDIR /gorun
 ---> Running in c5f0571f23a2
Removing intermediate container c5f0571f23a2
 ---> ff55b5c620eb
Step 7/8 : COPY --from=builder /gobuild/get-cpu-os .
 ---> d2eb316ce2d8
Step 8/8 : CMD ["./get-cpu-os"]
 ---> Running in 509518ac6084
Removing intermediate container 509518ac6084
 ---> bc682938a232
Successfully built bc682938a232
Successfully tagged zhaowenyu/get-cpu-os:latest-amd64

镜像构建完成后,一般会使用 docker push 推送到远程镜像仓库,便于在不同环境节点上都能快速拉取。

构建 arm64 镜像

选择一台 ARM64 机器作为镜像构建机器,来构建可在 ARM64 架构机器上运行的容器镜像。

可以通过 lscpu 命令查看机器的 CPU 硬件相关系信息,如下 CPU 信息表明其是 Architecture: aarch64 架构的机器。

AArch64 是 ARMv8 架构的一种执行状态,arm64 已经与 aarch64 合并,因为 aarch64 和 arm64 指的是同一件事。

相关的 CPU 和操作系统内核相关信息:

# lscpu 
Architecture:          aarch64
Byte Order:            Little Endian
CPU(s):                96
On-line CPU(s) list:   0-95
Thread(s) per core:    1
Core(s) per socket:    48
座:                 2
NUMA 节点:         4
型号:              0
CPU max MHz:           2600.0000
CPU min MHz:           200.0000
BogoMIPS:            200.00
L1d 缓存:          64K
L1i 缓存:          64K
L2 缓存:           512K
L3 缓存:           49152K
NUMA 节点0 CPU:    0-23
NUMA 节点1 CPU:    24-47
NUMA 节点2 CPU:    48-71
NUMA 节点3 CPU:    72-95
Flags:             fp asimd evtstrm aes......

查看操作系统内核信息。如下说明该构建机器是 GNU/Linux ,内核版本是 4.18.0-80.7.2,也可以看出是 aarch64 的架构信息。

# uname  -a
Linux arm-k8s1 4.18.0-80.7.2.el7.aarch64 #1 SMP Thu Sep 12 16:13:20 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux

在该机器上通过 docker build 工具来构建镜像

# docker build -t zhaowenyu/get-cpu-os:latest-arm64 .
Sending build context to Docker daemon  6.656kB
Step 1/8 : FROM golang AS builder
latest: Pulling from library/golang
3a36574378e6: Pull complete 
a61d3345afba: Pull complete 
3e267d6aa58f: Pull complete 
f647907d26b8: Pull complete 
c64921c44c8b: Pull complete 
4d35a8839961: Pull complete 
fdcf0c916203: Pull complete 
Digest: sha256:02c05351ed076c581854c554fa65cb2eca47b4389fb79a1fc36f21b8df59c24f
Status: Downloaded newer image for golang:latest
 ---> 2c818329d3a1
Step 2/8 : WORKDIR /gobuild
 ---> Running in e5d478535d09
Removing intermediate container e5d478535d09
 ---> c8cebf89db0f
Step 3/8 : COPY  get-cpu-os.go  .
 ---> d3b30edcb2c0
Step 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值