Docker Manifest 发布多架构镜像
有些团队不在一台机器上交叉构建所有架构,而是让 AMD64 与 ARM64 Runner 分别构建、测试并推送镜像。此时,仓库中已有两个架构专用标签,例如 app:1.2.0-amd64 与 app:1.2.0-arm64,但使用者不应该记住 CPU 架构再选择标签。
Docker Manifest 可以把这些已存在的镜像汇总为一个统一标签,例如 app:1.2.0。之后无论在 linux/amd64 还是 linux/arm64 主机执行相同的 docker pull app:1.2.0,Docker 都会根据本机平台自动选择对应镜像。
本篇关注汇总和发布,不重复介绍 Buildx 的一次性多架构构建。若镜像尚未构建,请先阅读 Buildx 多架构镜像构建。
完成本篇后,你将能够:
- 设计可追溯的架构专用镜像标签;
- 用
docker manifest create将多个平台镜像组成统一版本; - 使用
annotate明确声明平台元数据; - 推送 Manifest 并验证仓库中的平台列表;
- 在 CI 中避免“部分架构发布成功”的不完整版本。
1. Manifest 与镜像标签的关系
一个普通镜像标签通常指向单个平台的 manifest,例如:
registry.example.com/team/api:1.2.0-amd64
-> linux/amd64 的镜像配置和层
多架构版本标签则指向一个镜像索引(也常被称为 manifest list),索引再指向各平台镜像:
registry.example.com/team/api:1.2.0
-> linux/amd64 -> api:1.2.0-amd64
-> linux/arm64 -> api:1.2.0-arm64
统一标签不复制镜像层,也不会重新构建镜像;它只保存对各平台 manifest 的引用。因此,先推送所有架构专用镜像,再创建并推送统一标签,是最稳妥的发布顺序。
2. 发布前准备
2.1 规划标签
建议使用不可变版本号加架构后缀作为源标签,再用无后缀版本作为用户拉取的统一标签:
| 用途 | 标签示例 | 谁使用 |
|---|---|---|
| AMD64 源镜像 | api:1.2.0-amd64 | AMD64 Runner、排障和发布任务 |
| ARM64 源镜像 | api:1.2.0-arm64 | ARM64 Runner、排障和发布任务 |
| 多架构版本 | api:1.2.0 | 生产部署、使用者 |
| 多架构滚动标签 | api:stable | 仅在版本发布验证后更新 |
不要把 latest-amd64、latest-arm64 作为唯一发布依据。滚动标签会被后续构建覆盖,无法证明某个统一标签实际引用了哪一版镜像。
2.2 登录仓库并定义变量
以下示例使用私有仓库;Docker Hub 只需将镜像名改为 <用户名>/api。访问令牌通过环境变量传入,避免写进终端历史。
# 版本标签应来自发布版本或 Git 提交,不要在生产发布中只使用 latest
export IMAGE=registry.example.com/platform/api
export VERSION=1.2.0
# 使用最小权限的机器人账号登录目标仓库
printf '%s' "$REGISTRY_PASSWORD" | \
docker login registry.example.com \
--username "$REGISTRY_USER" \
--password-stdin
执行发布的 Docker CLI 必须能访问镜像仓库;各架构镜像也必须已经推送到同一个仓库路径。docker manifest 操作的是远程引用,不能把只存在本地 docker image ls 中的镜像直接汇总为跨机器可用的 manifest。
2.3 确认架构专用镜像已经存在
先检查每个源标签。imagetools inspect 直接读取远程仓库,适合发布前的自动检查:
# 应分别返回 linux/amd64 和 linux/arm64
docker buildx imagetools inspect "$IMAGE:$VERSION-amd64"
docker buildx imagetools inspect "$IMAGE:$VERSION-arm64"
如果团队使用原生 Runner 构建,两个 CI 任务通常分别执行类似命令:
# AMD64 Runner:构建、测试后推送 AMD64 源标签
docker buildx build \
--platform linux/amd64 \
--tag "$IMAGE:$VERSION-amd64" \
--push \
.
# ARM64 Runner:在 ARM64 主机上构建、测试后推送 ARM64 源标签
docker buildx build \
--platform linux/arm64 \
--tag "$IMAGE:$VERSION-arm64" \
--push \
.
docker buildx build --push 需要 Buildx 与 BuildKit。重点是每个源标签都已成功推送,而非由哪一个 CI 产品执行构建。
3. 创建多架构 Manifest
3.1 创建统一版本标签
docker manifest create 只在本机 CLI 中创建待推送的 manifest list,不会立即改变远程仓库。将两个已经存在的源标签加入统一版本:
# 创建本地 manifest list;顺序不影响 Docker 选择平台的结果
docker manifest create "$IMAGE:$VERSION" \
"$IMAGE:$VERSION-amd64" \
"$IMAGE:$VERSION-arm64"
如需在发布脚本中重复执行,可先清理同名的本地 manifest 缓存。该命令不会删除远程镜像或远程标签:
# 仅删除本机 Docker CLI 保存的 manifest 定义;不存在时忽略错误
docker manifest rm "$IMAGE:$VERSION" 2>/dev/null || true
# 再次创建待发布的 manifest list
docker manifest create "$IMAGE:$VERSION" \
"$IMAGE:$VERSION-amd64" \
"$IMAGE:$VERSION-arm64"
3.2 标注平台元数据
当源镜像 manifest 已经正确包含操作系统和 CPU 架构时,Docker 通常能自动识别。生产发布仍建议显式标注,尤其是源镜像来自不同构建系统或私有仓库迁移后:
# 明确标注 AMD64 镜像的平台信息
docker manifest annotate "$IMAGE:$VERSION" \
"$IMAGE:$VERSION-amd64" \
--os linux \
--arch amd64
# 明确标注 ARM64 镜像的平台信息
docker manifest annotate "$IMAGE:$VERSION" \
"$IMAGE:$VERSION-arm64" \
--os linux \
--arch arm64
如需加入 ARM v7、Windows 或其他平台,应额外创建对应源镜像,并在同一份 manifest 中标注完整平台。不要把 Linux 与 Windows 运行时兼容性当作理所当然:它们需要不同的基础镜像、节点系统和部署策略。
3.3 推送到仓库
确认本地定义后再推送统一标签:
# 推送 manifest list;--purge 在成功后清理本地临时定义,不会删除远程源镜像
docker manifest push --purge "$IMAGE:$VERSION"
如果需要同步发布滚动标签,必须重新创建一份 manifest,因为标签是独立引用:
# stable 只在 1.2.0 已完成验证后更新
docker manifest create "$IMAGE:stable" \
"$IMAGE:$VERSION-amd64" \
"$IMAGE:$VERSION-arm64"
# 为 stable 标注两个平台并推送
docker manifest annotate "$IMAGE:stable" "$IMAGE:$VERSION-amd64" --os linux --arch amd64
docker manifest annotate "$IMAGE:stable" "$IMAGE:$VERSION-arm64" --os linux --arch arm64
docker manifest push --purge "$IMAGE:stable"
4. 验证发布结果
4.1 检查远程平台列表
发布完成后使用 Buildx 检查远程统一标签,应同时看到两个平台。即使没有用 Buildx 构建,imagetools inspect 仍是最清楚的验证工具:
# 读取远程统一标签,而不是本机缓存
docker buildx imagetools inspect "$IMAGE:$VERSION"
预期结果包含:
Manifests:
Platform: linux/amd64
Platform: linux/arm64
也可以检查 docker manifest 返回的 JSON。该命令主要用于诊断,日常发布日志优先保留上面的平台清单即可:
# 查看 manifest 的原始描述信息
docker manifest inspect "$IMAGE:$VERSION"
4.2 在真实或指定平台运行
最可靠的验证是在各自的真实 CPU 架构上拉取并运行。以下命令适合检查当前主机自动选中的版本:
# Docker 会根据当前主机平台从统一标签中选择对应镜像
docker run --rm "$IMAGE:$VERSION" uname -m
需要在 Docker Desktop 或已配置 QEMU 的机器上做交叉功能验证时,可以显式指定平台:
# 在支持模拟的环境中验证 ARM64 变体;不用于性能基准
docker run --rm \
--platform linux/arm64 \
"$IMAGE:$VERSION" \
uname -m
输出常见为 x86_64(AMD64)或 aarch64(ARM64)。应用镜像的健康检查、启动参数和关键业务请求仍应在每种真实架构上通过 CI 验证。
5. CI 发布顺序
将 manifest 发布任务设计为依赖两个架构构建任务的收敛步骤:
amd64 构建 + 测试 + 推送 api:<版本>-amd64 ─┐
├─ manifest 汇总 + 推送 api:<版本> ─> 远程验证
arm64 构建 + 测试 + 推送 api:<版本>-arm64 ──┘
发布任务应满足以下约束:
- 两个架构任务都成功后才创建统一标签;
- 两个源镜像使用同一份源码版本、同一依赖锁定文件和同一发布版本;
- 汇总前检查每个源标签的平台元数据,而不是只检查推送命令退出码;
- 将 manifest digest、源镜像 digest 和 CI 任务 ID 写入发布记录;
stable或latest等滚动标签只在不可变版本验证通过后更新。
下面的 Shell 片段可作为汇总任务的核心逻辑:
set -euo pipefail
# 先验证两个远程源镜像都存在且可解析
docker buildx imagetools inspect "$IMAGE:$VERSION-amd64" >/dev/null
docker buildx imagetools inspect "$IMAGE:$VERSION-arm64" >/dev/null
# 清理本地缓存后,创建、标注并发布统一标签
docker manifest rm "$IMAGE:$VERSION" 2>/dev/null || true
docker manifest create "$IMAGE:$VERSION" "$IMAGE:$VERSION-amd64" "$IMAGE:$VERSION-arm64"
docker manifest annotate "$IMAGE:$VERSION" "$IMAGE:$VERSION-amd64" --os linux --arch amd64
docker manifest annotate "$IMAGE:$VERSION" "$IMAGE:$VERSION-arm64" --os linux --arch arm64
docker manifest push --purge "$IMAGE:$VERSION"
# 发布完成后再次检查远程索引
docker buildx imagetools inspect "$IMAGE:$VERSION"
6. 常见问题
no such manifest 或源标签找不到
先确认镜像全名、仓库命名空间和架构后缀一致,再确认发布账号有读取源标签的权限:
# 这两条命令任何一条失败时,都不要创建统一 manifest
docker buildx imagetools inspect "$IMAGE:$VERSION-amd64"
docker buildx imagetools inspect "$IMAGE:$VERSION-arm64"
常见原因是 ARM64 Runner 推送到另一个项目路径,或 CI 变量展开后产生了不同版本标签。
拉取统一标签后提示 no matching manifest for linux/...
统一标签中缺少当前平台,或 annotate 写错了 --os、--arch。重新使用 imagetools inspect 查看真实平台列表;不要只看仓库 UI 中是否存在同名标签。
denied: requested access to the resource is denied
发布 manifest 既需要读取两个源镜像,也需要向统一标签所在仓库写入索引。检查机器人账号是否同时拥有读取和推送权限;跨项目汇总时,目标仓库可能还要求允许引用外部仓库的 manifest。
docker manifest 提示 experimental
部分旧版 Docker CLI 仍将 docker manifest 标记为实验功能。优先升级 Docker CLI;无法升级时,使用 docker buildx imagetools create 也能完成镜像索引汇总,但团队应统一工具与发布脚本,避免两种流程同时维护。
源镜像被仓库清理策略删除
多架构统一标签依赖每个架构源 manifest。若清理策略只保留统一标签却删除了 -amd64 或 -arm64 源标签,某些仓库可能仍保留底层 digest,也可能导致索引引用失效。为架构源标签设置与发布版本相同或更长的保留期,并定期执行拉取验证。
7. 发布检查清单
- 每个目标架构都有不可变源标签,例如
1.2.0-amd64、1.2.0-arm64; - 源标签都已推送到目标仓库,并通过
imagetools inspect验证; - 每个源镜像的操作系统和 CPU 架构元数据正确;
- 统一版本标签已通过
docker manifest create、annotate和push发布; - 远程统一标签同时列出
linux/amd64与linux/arm64; - 至少在一种真实目标架构上完成运行验证;
- 发布记录保留版本、manifest digest、源镜像 digest 与 CI 任务信息。