摘要
在多架构镜像场景下,同样的 docker pull 命令,在不同 Docker 版本或不同本地 image store 后端上,可能表现出完全不同的结果:有的环境下镜像会直接体现为单架构镜像,可以正常查看 Architecture 并执行 docker save;有的环境下则只会看到一个 manifest list,本地 inspect 无法直接看到架构信息,save 还可能报错。
本文结合实际排查过程,说明新旧版 Docker 在多架构镜像处理上的差异,并给出两类可落地方案:
- 如何恢复旧版 Docker 的使用体验
- 如何在新版本 Docker 下正确保存单架构镜像
- 如何恢复旧版 Docker 的使用体验
- 如何在新版本 Docker 下正确保存单架构镜像
一、问题现象
排查过程中,遇到了如下现象:
- 两台机器执行相同的
docker pull命令 - 一台机器可以正常
pull、inspect、save - 另一台机器虽然
pull成功,但:docker image inspect看不到Architecturedocker save报错- 本地看到的是
manifest list,而不是单架构镜像
典型表现如下:
docker image inspect quay.io/ascend/vllm-ascend:releases-v0.13.0
输出中可能出现:
"Architecture": "",
"Os": "",
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json"
这说明当前本地看到的不是某个具体平台镜像,而是一个多架构入口。
二、根因分析
1. 镜像本身是多架构镜像
先看远端 manifest:
docker manifest inspect quay.io/ascend/vllm-ascend:releases-v0.13.0
例如输出:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"digest": "sha256:0f8f22023543fe3cb4414192d8f3e4b098b8489c27a559befa108f115478bfd6",
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"digest": "sha256:69ec114c9ba3ac151b93f8183304d409ee015f4d917e4e655883d6b877493464",
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
]
}
说明该 tag 同时支持:
linux/arm64linux/amd64
也就是说,这个 tag 指向的是 manifest list,不是某一个单独架构镜像。
2. 新旧版 Docker 的 image store 行为不同
核心差异通常不在镜像仓库,而在本地 Docker。
旧体验
旧版 Docker,或者仍在使用 classic image store 的环境,往往会把多架构 tag 落成更接近“单架构镜像”的本地表现:
docker image inspect能直接看到架构docker save更容易成功- 用户感觉上“一个 tag 就是一个镜像”
新体验
新版本 Docker,特别是启用了 containerd image store 的环境,会更完整地保留 manifest list / image index 语义:
- 本地 tag 可能对应的是 manifest list
docker image inspect顶层未必能直接看到架构docker save可能在处理多架构对象时失败
三、为什么 docker save 会失败
典型报错如下:
docker save -o vllm-ascend.tar quay.io/ascend/vllm-ascend:releases-v0.13.0
报错:
Error response from daemon: unable to create manifests file: NotFound: content digest sha256:69ec114c9ba3ac151b93f8183304d409ee015f4d917e4e655883d6b877493464: not found
这个报错的含义通常是:
- 本地 tag 指向的是一个多架构 manifest list
- 但是本地并没有完整持有该 list 下所有子内容
docker save在整理导出内容时,发现某个 digest 不在本地,于是失败
如果远端 manifest 里:
sha256:0f8f22...是arm64sha256:69ec11...是amd64
而报错缺的是 sha256:69ec11...
那通常可以反推出:
本地已经拿到的是 arm64 子镜像,而缺失的是 amd64 子镜像。
四、docker pull 不指定 --platform 是否两边都能跑
答案是:
不是同一份本地镜像同时能在 amd64 和 arm64 上运行,而是 Docker 会根据当前机器平台自动选择对应架构的子镜像。
例如:
docker pull quay.io/ascend/vllm-ascend:releases-v0.13.0
- 在
amd64主机上,通常会选择amd64 - 在
arm64主机上,通常会选择arm64
这意味着:
- 同一个 tag 可以在不同机器上分别
pull - 但不同机器拉下来的实际内容并不是同一份单架构镜像
docker save导出的 tar 包仍然是单架构内容,不会天然跨架构通用
五、如何确认本地到底是 arm64 还是 amd64
方法 1:先看远端支持哪些架构
docker manifest inspect quay.io/ascend/vllm-ascend:releases-v0.13.0
方法 2:看自己拉取时有没有指定平台
docker pull --platform linux/arm64 quay.io/ascend/vllm-ascend:releases-v0.13.0
如果明确执行过这条,那么该次拉取目标就是 arm64。
方法 3:根据 save 报错反推
例如:
NotFound: content digest sha256:69ec114c...
而已知:
sha256:69ec114c...=amd64sha256:0f8f2202...=arm64
那就说明:
- 本地已经有的是
arm64 - 缺的是
amd64
因此可以判断,本地拉下来的具体镜像内容是 arm64。
六、如何恢复旧版 Docker 的使用体验
如果希望新机器表现得更像旧机器,最直接的方法是:
切回 classic image store,不使用 containerd image store。
1. 先看当前 Docker 情况
docker version
docker info
docker info -f '{{ .DriverStatus }}'
如果输出中出现类似:
driver-type io.containerd.snapshotter.v1
通常说明当前启用了 containerd image store 相关能力。
2. Linux 上修改 /etc/docker/daemon.json
查看当前配置:
cat /etc/docker/daemon.json
如果当前启用了 containerd snapshotter,可改成如下:
{
"features": {
"containerd-snapshotter": false
},
"storage-driver": "overlay2"
}
重启 Docker:
sudo systemctl restart docker
再次确认:
docker info
3. Docker Desktop 上关闭 containerd image store
如果是 Docker Desktop,可在设置中关闭:
Use containerd for pulling and storing images
关闭后重新应用配置即可。
4. 注意事项
切换 image store 后:
- 原来另一套 store 中的镜像和容器会暂时“看不见”
- 通常不是丢失,而是当前 Docker daemon 换了另一套后端视图
因此切换前建议先备份:
docker images
docker ps -a
必要时先导出能导出的镜像。
七、新版本 Docker 下如何正确保存镜像
在新版本 Docker 下,不要直接对多架构 tag 执行 docker save,而应当保存具体平台的子镜像。
方法一:先找出具体平台的 digest
先查看 manifest:
docker manifest inspect quay.io/ascend/vllm-ascend:releases-v0.13.0
假设得到:
- arm64:
sha256:0f8f22023543fe3cb4414192d8f3e4b098b8489c27a559befa108f115478bfd6 - amd64:
sha256:69ec114c9ba3ac151b93f8183304d409ee015f4d917e4e655883d6b877493464
方法二:按具体 digest 拉取
拉取 arm64:
docker pull quay.io/ascend/vllm-ascend@sha256:0f8f22023543fe3cb4414192d8f3e4b098b8489c27a559befa108f115478bfd6
拉取 amd64:
docker pull quay.io/ascend/vllm-ascend@sha256:69ec114c9ba3ac151b93f8183304d409ee015f4d917e4e655883d6b877493464
方法三:给 digest 打本地 tag
保存 arm64 时:
docker tag quay.io/ascend/vllm-ascend@sha256:0f8f22023543fe3cb4414192d8f3e4b098b8489c27a559befa108f115478bfd6 vllm-ascend:arm64
保存 amd64 时:
docker tag quay.io/ascend/vllm-ascend@sha256:69ec114c9ba3ac151b93f8183304d409ee015f4d917e4e655883d6b877493464 vllm-ascend:amd64
方法四:再执行 docker save
保存 arm64:
docker save -o vllm-ascend-arm64.tar vllm-ascend:arm64
保存 amd64:
docker save -o vllm-ascend-amd64.tar vllm-ascend:amd64
这种做法的优点是:
- 避开多架构 tag 的 manifest list 问题
- 导出的就是明确的单架构镜像
- 后续导入和管理更清晰
八、推荐操作方式
场景 1:希望维持旧机器体验
建议:
docker version
docker info
cat /etc/docker/daemon.json
确认后切回 classic image store。
适合场景:
- 习惯旧 Docker 行为
- 需要直接
inspect看到架构 - 需要对 tag 直接
save - 依赖旧脚本、旧流程
场景 2:接受新版本 Docker 行为,但希望稳定导出镜像
建议统一采用以下流程:
docker manifest inspect <image:tag>
docker pull <image@sha256:platform_digest>
docker tag <image@sha256:platform_digest> local-name:platform
docker save -o xxx.tar local-name:platform
适合场景:
- 不改宿主机 Docker 配置
- 接受 manifest list / image index 语义
- 只要求明确导出单架构镜像
九、结论
这次问题的关键不在镜像仓库,而在于:
- 镜像本身是多架构镜像
- 新旧版 Docker 对多架构对象的本地处理方式不同
- 新版本 Docker 更可能把 tag 落成 manifest list
- 因而带来
inspect看不到架构、save失败等现象
如果希望恢复旧体验,可以切回 classic image store。 如果希望保留新版本 Docker,则应当按具体平台 digest 拉取并保存单架构镜像。
从运维实践角度看,团队内部最好统一:
- Docker 主版本
- image store 类型
- 镜像导出流程
这样才能避免同样命令在不同机器上出现不一致结果。