Docker pull镜像save后仅导出几KB:镜像 Layer 缺失问题排查与解决
Docker 29.2 + containerd 环境下 docker save 仅导出 17KB:镜像 Layer 缺失问题排查与解决
一、问题背景
在需要将 Docker 镜像从联网环境迁移到离线环境时,一般会使用:
docker pull <image>
docker save -o image.tar <image>
然后将 image.tar 拷贝到离线环境:
docker load -i image.tar
本次在迁移:
vllm/vllm-openai:latest
镜像时,却遇到了一个非常异常的问题:
- 本地镜像大小约 30GB;
docker save导出的 tar 文件只有约 17KB;- tar 文件可以正常解包,并且是 OCI Image Layout;
- 归档中只有少量 manifest / metadata;
- 真正的 filesystem layer 并没有被导出;
- 将 tar 文件带到离线环境后无法正常使用;
- 指定
--platform=linux/amd64导出时,Docker 又提示该镜像不提供 amd64 平台。
最终确认,该现象与 Docker 29.2 + containerd image store 下的一个 Moby 官方 Bug 高度一致。
最终通过一次“空 Build”成功解决,而且不需要重新下载约 30GB 的 vLLM 镜像。
二、系统环境
本次出现问题的实际环境如下。
Docker Client
Version: 29.2.0
API version: 1.53
Go version: go1.25.6
Git commit: 0b9d198
OS/Arch: linux/amd64
Context: default
Docker Server
Docker Desktop: 4.59.0
Engine Version: 29.2.0
API version: 1.53
OS/Arch: linux/amd64
Experimental: false
containerd
Version: v2.2.1
GitCommit: dea7da592f5d1d2b7755e3a161be07f43fad8f75
runc
Version: 1.3.4
目标镜像:
vllm/vllm-openai:latest
本地镜像大小约:
30GB
三、异常现象
正常情况下,一个约 30GB 的 Docker 镜像执行:
docker save \
-o openaivllmlatest.tar \
vllm/vllm-openai:latest
得到的 tar 至少应该是 GB 级。
但实际生成的文件只有:
约 17KB
这显然不可能是正常压缩。
进一步查看归档内容:
tar -tf openaivllmlatest.tar
得到:
blobs/
blobs/sha256/
blobs/sha256/07ea4e292adf3a26b05ac97114b28849cf4551a26beb1fbe7decd3842d752ed7
blobs/sha256/5b03c7de2951cda2d5fd087457902625eabbe9188478e439561ef34f325b0fd5
index.json
manifest.json
oci-layout
这里已经能够发现明显异常。
一个约 30GB 的大型 CUDA/vLLM 镜像,正常情况下:
blobs/sha256/
下面应该存在大量:
config
manifest
filesystem layer
而这里实际只有两个 blob。
也就是说:
30GB 并不是被压缩成了 17KB,而是几十 GB 的镜像 Layer 根本没有进入
docker save生成的 tar。
四、进一步分析 Manifest
继续读取其中一个 blob:
tar -xOf openaivllmlatest.tar \
blobs/sha256/07ea4e292adf3a26b05ac97114b28849cf4551a26beb1fbe7decd3842d752ed7 \
| jq .
得到:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 7238,
"digest": "sha256:7441a0579b7974fe00eec2c41a5710bcd0dc3dafc03edcdc5663158dd68286cf",
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 7239,
"digest": "sha256:5b03c7de2951cda2d5fd087457902625eabbe9188478e439561ef34f325b0fd5",
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
]
}
Manifest List 中明确存在:
linux/arm64
linux/amd64
于是继续尝试:
docker image save \
--platform=linux/amd64 \
-o openaivllm-amd64.tar \
vllm/vllm-openai:latest
Docker 却返回:
Error response from daemon:
no suitable export target found:
image with reference vllm/vllm-openai:latest was found
but does not provide the specified platform (linux/amd64)
这时出现了一个非常有意思的矛盾:
Manifest List
│
├── linux/arm64
│
└── linux/amd64
│
▼
docker save --platform=linux/amd64
│
▼
Docker:没有 linux/amd64
因此问题并不是:
vllm/vllm-openai 不支持 amd64
而是:
Docker 本地 image store 中虽然存在对应的 Manifest 信息,但对应平台的 Layer/Blob 没有处于
docker save能够正确导出的状态。
五、官方 Issue
这个问题与 Moby 官方 Issue #52193 高度一致。
Issue 标题:
c8d storage issue with `docker save` after `docker pull`
官方地址:
https://github.com/moby/moby/issues/52193
该 Issue 创建于:
2026-03-19
并带有:
containerd-integration
kind/bug
version/29.2
等标签。
官方 Issue 的复现也是先执行:
docker pull --platform ...
然后执行:
docker save --platform ...
最终得到:
no suitable export target found:
image ... was found but does not provide the specified platform
Issue 中出现问题的环境包括:
Docker Engine 29.2.1
containerd 2.2.1
Docker Desktop
而我的环境是:
Docker Engine 29.2.0
containerd 2.2.1
Docker Desktop 4.59.0
两者高度接近。
更加关键的是,该 Issue 还明确说明:
Blobs that are expected to be present are missing
也就是说,本应存在的 Blob 实际无法被 exporter 获取,从而可能进一步产生:
missing layers
no such file or directory
等异常。
六、真正有效的解决方案
Moby #52193 中同时给出了一个非常有效的 workaround:
对准备进行
docker save的镜像执行一次 Build。
本次实际测试同样有效。
执行:
docker build \
--platform=linux/amd64 \
-t vllm/vllm-openai:offline \
- <<'EOF'
FROM vllm/vllm-openai:latest
EOF
这里实际上相当于创建了一个只有一行内容的 Dockerfile:
FROM vllm/vllm-openai:latest
没有:
RUN
COPY
ADD
也没有修改原始 vLLM 镜像里的任何内容。
它只是重新基于原镜像生成一个本地 Build 结果:
vllm/vllm-openai:latest
│
│ FROM
▼
vllm/vllm-openai:offline
完成以后重新导出:
docker image save \
--platform=linux/amd64 \
-o openaivllm-amd64.tar \
vllm/vllm-openai:offline
这一次:
docker save
恢复正常。
生成的 tar 文件重新变成 GB 级,并且包含完整镜像 Layer。
之后即可复制到离线环境:
docker load -i openaivllm-amd64.tar
镜像可以正常使用。
七、为什么一次“空 Build”能够解决问题?
这部分需要注意,目前更适合作为根据官方 Issue 和实际测试结果进行的机制分析,而不是把它描述成 Docker 官方已经完全确认的底层根因。
问题发生时,可以简单理解成:
docker pull
│
▼
containerd image store
│
├── OCI Index 存在
├── Manifest 存在
└── Blob / Layer 导出状态异常
│
▼
docker save
│
▼
只能导出少量 Metadata
│
▼
17KB
而执行:
FROM vllm/vllm-openai:latest
进行一次 Build 后,Docker/BuildKit 需要重新解析:
镜像 Manifest
平台
Config
基础镜像 Layer
并生成新的本地构建结果。
可以将这个过程理解成:
原镜像
│
│ docker pull
▼
containerd image store
│
└── Layer/Blob 对 save 不可正常导出
│
▼
docker build
│
▼
BuildKit 重新解析基础镜像
│
▼
生成新的本地 Image Result
│
▼
Layer/Blob 可以被 exporter 获取
│
▼
docker save 正常
因此,与其说:
镜像损坏了
不如说:
Docker 29.2 + containerd image store 场景下,
docker pull后的镜像在docker save导出过程中出现了 Blob/Layer 的可见性或导出状态异常;重新执行 Build 可以触发 Docker/BuildKit 重新处理这些 Layer,从而使其恢复为可正常导出的状态。
八、如何确认自己遇到的是同一个问题?
如果出现以下组合现象,就非常值得怀疑是相同问题。
首先:
docker images
镜像显示:
10GB
20GB
30GB
甚至更大。
但是:
docker save -o image.tar image:tag
得到的文件却只有:
几 KB
几十 KB
查看:
tar -tf image.tar
发现只有:
blobs/
blobs/sha256/
index.json
manifest.json
oci-layout
并且:
blobs/sha256/
下面只有极少量文件。
同时再执行:
docker save \
--platform=linux/amd64 \
-o image.tar \
image:tag
出现:
no suitable export target found
或者:
image ... was found but does not provide the specified platform
而 Manifest List 中明明存在:
linux/amd64
那么这个问题与 Moby #52193 就非常接近了。
九、导出以后一定要进行验证
问题解决以后,不建议看到 docker save 没有报错就直接把文件带进离线环境。
至少做下面几个检查。
1. 检查文件大小
du -h openaivllm-amd64.tar
如果原镜像是几十 GB:
17KB
显然不正常。
恢复以后应该是:
GB 级
2. 检查 Blob 数量
tar -tf openaivllm-amd64.tar \
| grep '^blobs/sha256/' \
| wc -l
原来的异常镜像只有极少量 Blob。
正常的大型 vLLM 镜像应该存在大量:
blobs/sha256/xxxxxxxx
3. 查看归档内容
tar -tf openaivllm-amd64.tar | head -50
应该能够看到大量:
blobs/sha256/xxxx
blobs/sha256/xxxx
blobs/sha256/xxxx
...
4. 计算 SHA256
在线机器:
sha256sum openaivllm-amd64.tar
复制到离线环境以后:
sha256sum openaivllm-amd64.tar
两边结果应该完全一致。
这样可以排除移动硬盘、U盘、网络传输等过程导致的大文件损坏。
十、备用方案:关闭 containerd image store
Moby #52193 中还提到:
关闭 Docker Desktop 中的:
Use containerd for pulling and storing images
以后,docker save 可以恢复正常。
Docker Desktop 可以进入:
Settings
→ General
→ Use containerd for pulling and storing images
取消该选项。
Docker 官方关于 containerd image store 的说明:
https://docs.docker.com/desktop/features/containerd/
需要注意的是:
classic image store 与 containerd image store 是两套独立的镜像存储。
因此切换以后,之前的镜像可能暂时不会出现在:
docker images
中。
这并不代表镜像被删除。
重新切回原来的 image store 后又可以看到。
如果镜像有:
20GB
30GB
40GB
那么切换 image store 后重新 docker pull 的成本比较高。
所以对于这种大型 AI/CUDA 镜像,我更推荐优先尝试:
空 Build
成功以后直接:
docker save
十一、推荐排查流程
以后遇到类似问题,可以按照:
docker save 文件异常小
│
▼
tar -tf 检查内容
│
▼
是否只有 Manifest / Metadata?
│
Yes
│
▼
检查 docker version
检查 containerd version
│
▼
docker save --platform
│
▼
是否提示 platform 不存在?
│
Yes
│
▼
执行一次空 Build
│
▼
docker save 新镜像
│
┌───┴────┐
│ │
成功 失败
│ │
▼ ▼
完成 关闭 containerd
image store
│
▼
重新 pull
│
▼
docker save
十二、最终可直接使用的命令
如果以后再次遇到类似情况,可以直接尝试:
1. 重新生成本地镜像
docker build \
--platform=linux/amd64 \
-t vllm/vllm-openai:offline \
- <<'EOF'
FROM vllm/vllm-openai:latest
EOF
2. 导出 amd64 镜像
docker image save \
--platform=linux/amd64 \
-o openaivllm-amd64.tar \
vllm/vllm-openai:offline
3. 检查文件大小
du -h openaivllm-amd64.tar
4. 检查 Blob
tar -tf openaivllm-amd64.tar \
| grep '^blobs/sha256/' \
| wc -l
5. 生成 SHA256
sha256sum openaivllm-amd64.tar
6. 离线机器加载
docker load -i openaivllm-amd64.tar
十三、总结
这次问题最具有迷惑性的地方在于:
docker images
明明显示:
约 30GB
但:
docker save
却只生成:
约 17KB
进一步检查 OCI archive 后发现,17KB 文件基本只有:
OCI Index
Manifest
Metadata
真正几十 GB 的 filesystem layer 并没有进入 tar。
同时:
docker save --platform=linux/amd64
还出现:
image ... was found but does not provide the specified platform
但是 Manifest List 明明声明存在:
linux/amd64
结合:
Docker Engine 29.2.0
Docker Desktop 4.59.0
containerd 2.2.1
以及 Moby 官方 Issue #52193,可以判断该问题与 Docker 29.2 下 containerd image store 的 docker save Bug 高度一致。
最终解决方法非常简单:
docker build \
--platform=linux/amd64 \
-t vllm/vllm-openai:offline \
- <<'EOF'
FROM vllm/vllm-openai:latest
EOF
然后重新:
docker image save \
--platform=linux/amd64 \
-o openaivllm-amd64.tar \
vllm/vllm-openai:offline
即可恢复正常。
对于几十 GB 的 vLLM、PyTorch、CUDA 等大型 AI 镜像,这种方案最大的优点是:
通常不需要重新下载整个镜像,就能解决
docker save缺少 Layer 的问题。
十四、参考资料
Moby 官方 Issue
#52193 - c8d storage issue with docker save after docker pull
https://github.com/moby/moby/issues/52193
Moby 相关历史 Issue
#47065 - [v25] docker save produces an OCI manifest with no layers
https://github.com/moby/moby/issues/47065
Docker 官方文档:containerd image store
https://docs.docker.com/desktop/features/containerd/
Docker 官方文档:docker image save
https://docs.docker.com/reference/cli/docker/image/save/
Docker 官方文档:docker image load
本文来自博客园,作者:日报初级开发工程师,转载请注明原文链接:https://www.cnblogs.com/april-code/p/22390407

浙公网安备 33010602011771号