介绍
在过去的几年里,我们看到了 ARM64 架构的出现,该架构因其能源效率和性能优势而越来越受欢迎。 我们经常在智能手机和平板电脑等移动设备中看到这些处理器。 我们还在智能手表和智能电视等物联网设备中看到了它们。 现在,我们开始看到服务器和个人计算机中越来越多地采用 ARM64 处理器。
ARM64 架构的出现意味着我们在构建和部署容器化应用程序时需要考虑两种主要的处理器架构,以最大限度地扩大应用程序的覆盖范围。
在本文中,我们将探讨多架构 (multi-arch) 容器映像的重要性、如何构建它们以及如何将它们部署在 Azure Kubernetes Service (AKS) 上。
为什么多架构容器镜像很重要
分发容器化应用程序时,跨不同平台的广泛兼容性对于广泛采用至关重要。 无论您是与团队或更广泛的社区共享容器映像,确保其在各种架构上无缝运行都至关重要。
在云计算方面,云提供商提供了多种处理器架构,每种架构都有自己的优点和缺点。 例如,我们知道 ARM64 处理器比 x86_64 处理器更节能。 这意味着在 ARM64 处理器上运行容器化应用程序比在 x86_64 处理器上运行容器化应用程序消耗的电量更少。 这可以为您的组织节省大量成本。
通过创建多架构容器映像,您可以创建一个包含应用程序的多个版本的单个容器映像,每个版本都针对特定的操作系统架构。 这允许客户端操作系统确定要使用的适当架构。
缩略词很多
在我们深入构建多架构容器映像之前,了解一些用于识别操作系统处理器架构的术语非常重要。 当涉及到识别操作系统处理器架构时,它充满了首字母缩略词,解读起来可能有点令人眼花缭乱,并且跟踪所有不同的处理器架构可能具有挑战性。
这是我们需要知道的:
x86 是基于 Intel 8086 CPU 的指令集架构系列。 这是使用最广泛的体系结构,并且大多数操作系统都支持。 x86_64 是 x86 架构的 64 位版本。
ARM 是基于 ARM CPU 的指令集架构系列。 这是一种较新的架构,由于其能源效率和性能优势而越来越受欢迎。 ARM64 是 ARM 架构的 64 位版本,您可能还会看到它被标识为 aarch64。
所以如果你看到了,x86_64就知道它是AMD64的代名词,而aarch64是ARM64的代名词。
构建多架构容器镜像的方法
有几种构建支持多种架构的容器映像的策略。 第一个是为每个架构构建单独的映像。 这种方法有一个巨大的缺点: 您需要维护多个图像并使它们保持同步。 从最终用户的角度来看,这意味着他们需要知道哪个映像用于其特定的主机操作系统架构。
另一种方法是单独构建每个容器映像,然后使用 Docker 清单命令将它们组合成单个映像。 这最终为您提供了一个多架构容器镜像; 然而,这种方法很麻烦并且容易出错。
而且,您真的想在每次构建新映像时更新清单文件吗?!? 可能不是……有更好的方法。
更好的方法是使用 Docker Buildx 等工具来构建可以在多种架构上运行的单个映像。 Docker Buildx 是一个简化构建多架构容器镜像过程的工具。 使用此工具,您可以使用单个命令构建多个容器映像,每个容器映像都针对特定架构,并将这些映像组合成单个多架构映像。
多架构容器镜像的实际应用
为了演示如何构建多架构容器映像,我们将使用我编写的一个简单的 Rust 应用程序。 您可以在此处找到该应用程序的源代码。 该应用程序是一个简单的命令行工具,可以打印有关其运行的操作系统的信息。
本地测试应用程序
我将首先克隆存储库并在本地运行应用程序以查看它的作用:
git clone https://github.com/pauldotyu/osinfo.git
cd osinfo
cargo run
您需要在计算机上安装 Rust 才能运行此应用程序。 您可以按照此处的说明安装 Rust。
我正在运行这台 M1 Mac(Apple Silicon),这就是我所看到的:
Type: Mac OS
Version: 13.5.1
Edition: None
Codename:
Bitness: 64-bit
Architecture: arm64
在 GitHub Codespaces 上测试应用程序
如果您有权访问 GitHub Codespaces,请在 Codespace 中打开存储库并运行应用程序。
您应该看到以下输出,该输出与我在 M1 Mac 上看到的输出不同:
Type: Debian
Version: 11
Edition: None
Codename: bullseye
Bitness: 64-bit
Architecture: x86_64
在 AKS 上运行应用程序
如果我从 M1 Mac 构建容器,将其推送到 Azure 容器注册表 (ACR),并尝试在 AKS 群集上运行它,会发生什么情况?
让我们来了解一下...
我需要一个 AKS 群集,因此我将使用 Azure CLI 创建一个:
# Create a resource group
az group create \
-n rg-multiarch \
-l eastus
# Create an Azure Container Registry
ACR_NAME=$(az acr create -n acrmultiarch${RANDOM} \
-g rg-multiarch \
--sku Standard \
--admin-enabled \
--query name -o tsv)
# Create an AKS cluster
az aks create \
-g rg-multiarch \
-n aks-multiarch \
--attach-acr $ACR_NAME
# Connect to the AKS cluster
az aks get-credentials \
-g rg-multiarch \
-n aks-multiarch
我正在我的 M1 Mac 上运行这些命令。
运行基于ARM64架构的容器镜像
存储库中有一个 Dockerfile,我可以使用它来构建容器映像。 我将使用 docker build 命令从我的 M1 Mac 构建并推送映像:
docker build -t $ACR_NAME.azurecr.io/osinfo:v0.1-arm64 .
docker push $ACR_NAME.azurecr.io/osinfo:v0.1-arm64
创建用于构建多架构容器映像的 Dockerfile 时,您需要确保基础映像支持您的目标架构。 例如,我使用的是 rust:1.72-bullseye 和 rust:1.72-slim-bullseye 图像,它们都支持 linux/amd64 和 linux/arm64 平台。 您可以在此处和此处找到有关这些图像的更多信息
将容器推送到 ACR 后,在 AKS 群集上运行该容器:
kubectl run osinfo1 --image=$ACR_NAME.azurecr.io/osinfo:v0.1-arm64 --restart=Never
如果运行命令 kubectl get po osinfo1,您将看到 pod 处于错误状态。
NAME READY STATUS RESTARTS AGE
osinfo1 0/1 Error 0 7s
运行命令 kubectl logs osinfo1,您将看到以下错误消息:
exec ./osinfo: exec format error
这意味着容器映像与AKS节点的架构不兼容。 该节点运行在 x86_64 架构上,容器镜像是针对 ARM64 架构构建的。 这是预料之中的,因为我在我的 M1 Mac 上构建了容器映像,该映像在 ARM64 架构上运行。
运行基于AMD64架构构建的容器镜像
让我们尝试在 x86_64 机器上构建容器映像,看看会发生什么。 这次,我将使用 GitHub Codespaces 通过运行 docker build 和 docker Push 命令来构建和推送容器映像,就像在 M1 Mac 上所做的那样:
# Login to Azure
az login
# Get ACR name
ACR_NAME=$(az acr list -g rg-multiarch --query "[].name" -o tsv)
# Build and push container image
docker build -t $ACR_NAME.azurecr.io/osinfo:v0.1-amd64 .
docker push $ACR_NAME.azurecr.io/osinfo:v0.1-amd64
在 AKS 上运行容器映像:
# Get AKS credentials
az aks get-credentials --resource-group rg-multiarch --name aks-multiarch
# Run container image on AKS
kubectl run osinfo2 --image=$ACR_NAME.azurecr.io/osinfo:v0.1-amd64 --restart=Never
如果运行 kubectl get po osinfo2 命令,您应该会看到 pod 处于“已完成”状态。
NAME READY STATUS RESTARTS AGE
osinfo2 0/1 Completed 0 20s
如果运行 kubectl logs osinfo2,您将看到以下输出:
Type: Debian
Version: 11.0.0
Edition: None
Codename:
Bitness: 64-bit
Architecture: x86_64
所以我们可以看到容器镜像的构建位置很重要。 如果您在 x86_64 计算机上构建容器映像,它将在 x86_64 计算机上运行。 如果您在 ARM64 计算机上构建容器映像,它将在 ARM64 计算机上运行。
构建并运行多架构容器映像
现在,让我们使用 Docker Buildx 来构建多架构容器映像。 我将使用 docker buildx 命令从安装了 Docker Desktop 的 M1 Mac 构建并推送映像。 Buildx 使用模拟来构建不同架构的容器映像。 这意味着您可以为不同于主机的架构构建容器映像。
如果您使用 Docker Desktop,则默认情况下应启用 Buildx。 您可以通过运行以下命令来验证这一点:
docker buildx version
但要使 Buildx 能够针对多个平台进行构建,您需要运行以下命令:
docker buildx create --use
使用 Buildx 构建容器映像时,您需要知道容器映像的目标“平台”。 Docker 使用 os/arch 的组合来标识镜像可以运行的平台。 例如,linux/amd64是在x86_64架构的Linux上运行的平台,linux/arm64是在ARM64架构的Linux上运行的平台。
运行以下命令构建多架构容器镜像:
docker buildx build --platform linux/amd64,linux/arm64 --push -t $ACR_NAME.azurecr.io/osinfo:v0.1-multi .
运行以下命令确认容器镜像支持多种架构:
docker manifest inspect $ACR_NAME.azurecr.io/osinfo:v0.1-multi
您应该看到类似于以下内容的输出,并且您会注意到输出中包含 AMD64 和 ARM64。 这意味着您的容器映像可以在任一架构上运行。
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 1055,
"digest": "sha256:76781cd09ce46d5c0cf13b0c834c3d39e649c6c392c42343284ec851125f819b",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 1055,
"digest": "sha256:26ebcb5ac14907804552477bf01f2892cdaebac06035e7561df4c5c61e6d8c30",
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 566,
"digest": "sha256:875f61309b8199a0582faa4ebe8aaca9412e9982774a3bf9d9d24f5a61d7f020",
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 566,
"digest": "sha256:76e5d0269adffa14b470b22856c653f466644b213b53d5fac0461fff88942362",
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}
AKS 支持具有不同主机操作系统体系结构的多个节点池。 我们可以使用 nodeSelector 告诉 Kubernetes 在特定类型的节点上运行 pod,以确保它在具有适当架构的节点上运行。
执行以下命令,在x86_64架构的节点上运行容器镜像:
kubectl run osinfo3 --image=$ACR_NAME.azurecr.io/osinfo:v0.1-multi --restart=Never --overrides='{"apiVersion":"v1","spec":{"nodeSelector":{"kubernetes.io/arch":"amd64"}}}'
运行 kubectl get po osinfo3 和 kubectl logs osinfo3 命令,您将看到输出与前面的示例相同。 它在x86_64节点上成功运行。
现在,让我们向 AKS 集群添加一个新的 ARM64 节点池,看看在新节点池上运行容器映像时会发生什么。
az aks nodepool add \
-g rg-multiarch \
--cluster-name aks-multiarch \
--name armpool \
--node-vm-size Standard_B2ps_v2
运行容器镜像并再次使用nodeSelector以确保它在ARM64节点上运行:
kubectl run osinfo4 --image=$ACR_NAME.azurecr.io/osinfo:v0.1-multi --restart=Never --overrides='{"apiVersion":"v1","spec":{"nodeSelector":{"kubernetes.io/arch":"arm64"}}}'
如果运行 kubectl logs osinfo4 命令,您应该看到以下输出:
Type: Debian
Version: 11.0.0
Edition: None
Codename:
Bitness: 64-bit
Architecture: aarch64
这意味着我们已经成功构建了一个可以在 x86_64 和 ARM64 架构上运行的多架构容器镜像
结论
在本文中,我们探讨了多架构容器映像的重要性以及如何使用 Docker Buildx 工具构建它们。 Docker Buildx 是一个有价值的工具,可以简化构建多架构容器镜像的过程。 通过创建支持多种架构的单个容器映像,您可以确保您的应用程序可以在不同的主机上无缝运行。 这使您能够覆盖更广泛的受众,提高应用程序的采用率,并有可能在云中节省一些资金。
我们在这里获得的有关如何使用 Docker Buildx 的知识将帮助我们进一步迈向自动化。 因为,在本地运行这些 docker buildx 构建命令也不理想。