## 10.3 使用 buildx 构建多种系统架构支持的 Docker 镜像

Docker 镜像可以支持多种系统架构，这意味着你可以在 `x86_64`、`arm64` 等不同架构的机器上运行同一个镜像。这是通过一个名为 “manifest list” (或称为 “fat manifest”) 的文件来实现的。

### 10.3.1 Manifest List 是什么？

为了理解多架构镜像的原理，我们需要先了解 Manifest List 的概念。

Manifest list 是一个包含了多个指向不同架构镜像的 manifest 的文件。当你拉取一个支持多架构的镜像时，Docker 会自动根据你当前的系统架构选择并拉取对应的镜像。

例如，官方的 `hello-world` 镜像就支持多种架构。你可以使用 `docker manifest inspect` 命令来查看它的 manifest list：

```bash
$ docker manifest inspect hello-world
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 525,
         "digest": "sha256:80852a401a974d9e923719a948cc5335a0a4435be8778b475844a7153a2382e5",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 525,
         "digest": "sha256:3adea81344be1724b383d501736c3852939b33b3903d02474373700b25e5d6e3",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v5"
         }
      },
      // ... more architectures
   ]
}
```

### 10.3.2 使用 `docker buildx` 构建多架构镜像

`docker buildx` 是构建多架构镜像的最佳实践工具，它屏蔽了底层的复杂性，提供了一键构建多架构镜像的能力。

在 Docker 19.03+ 版本中，`docker buildx` 是推荐的用于构建多架构镜像的工具。它使用 `BuildKit` 作为后端，可以大大简化构建过程（Docker 23+ 默认启用 BuildKit）。

#### 新建 `builder` 实例

首先，你需要创建一个新的 `builder` 实例，因为它支持同时为多个平台构建。

```bash
$ docker buildx create --name mybuilder --use
$ docker buildx inspect --bootstrap
```

#### 构建和推送

使用 `docker buildx build` 命令并指定 `--platform` 参数，可以同时构建支持多种架构的镜像。`--push` 参数会将构建好的镜像和 manifest list 推送到 Docker 仓库。

```dockerfile
## Dockerfile

FROM --platform=$TARGETPLATFORM alpine

RUN uname -a > /os.txt

CMD cat /os.txt
```
```bash
$ docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t your-username/multi-arch-image . --push
```
构建完成后，你就可以在不同架构的机器上拉取并运行 `your-username/multi-arch-image` 这个镜像了。

#### 架构相关的构建参数

在 `Dockerfile` 中，你可以使用一些预定义的构建参数来根据目标平台定制构建过程：

*   `TARGETPLATFORM`：构建镜像的目标平台，例如 `linux/amd64`。
*   `TARGETOS`：目标平台的操作系统，例如 `linux`。
*   `TARGETARCH`：目标平台的架构，例如 `amd64`。
*   `TARGETVARIANT`：目标平台的变种，例如 `v7`。
*   `BUILDPLATFORM`：构建环境的平台。
*   `BUILDOS`：构建环境的操作系统。
*   `BUILDARCH`：构建环境的架构。
*   `BUILDVARIANT`：构建环境的变种。

例如，你可以这样编写 `Dockerfile` 来拷贝特定架构的二进制文件：

```dockerfile
FROM scratch

ARG TARGETOS
ARG TARGETARCH

COPY bin/dist-${TARGETOS}-${TARGETARCH} /dist

ENTRYPOINT ["/dist"]
```

### 10.3.3 使用 `docker manifest`：底层工具

除了 `docker buildx`，我们也可以直接操作 Manifest List 来手动组合不同架构的镜像。

`docker manifest` 是一个更底层的命令，可以用来创建、检查和推送 manifest list。虽然 `docker buildx` 在大多数情况下更方便，但了解 `docker manifest` 仍然有助于理解其工作原理。

#### 创建 manifest list

```bash
## 首先，为每个架构构建并推送镜像

$ docker buildx build --platform linux/amd64 -t your-username/my-app:amd64 . --push
$ docker buildx build --platform linux/arm64 -t your-username/my-app:arm64 . --push

## 然后，创建一个 manifest list，将它们组合在一起

$ docker manifest create your-username/my-app:latest \
    --amend your-username/my-app:amd64 \
    --amend your-username/my-app:arm64

## 最后，推送 manifest list

$ docker manifest push your-username/my-app:latest
```

#### 检查 manifest list

你可以使用 `docker manifest inspect` 来查看一个 manifest list 的详细信息，如上文所示。
