核心思路是比对内容寻址的只读层而非镜像标签,通过layer ID(SHA-256哈希)判断宿主机或仓库中是否共享;需用docker image inspect或skopeo提取layer digest并统计频次,频次为1者即冗余层;删除标签不释放层,须确保digest无任何manifest引用并触发GC。

核心思路不是直接比“镜像”,而是比“层(layer)”——因为仓库中真正复用和冗余的单位是内容寻址的只读层,不是标签或镜像摘要。
确认哪些层物理上被多个镜像共享
同一台宿主机的 Docker daemon 内,层复用基于 SHA-256 内容哈希。要验证是否共享,需提取各镜像的 layer ID 列表并逐项比对:
- 运行 docker image inspect
-f '{{.RootFS.Layers}}' 获取该镜像所有 layer ID(注意:ID 是完整 sha256 字符串,长度一致且完全相等才算复用) - 对多个目标镜像重复执行,把输出整理成数组,用脚本或人工比对相同字符串出现次数
- 注意:docker system df -v 中的 “Shared Size” 是估算值,不可直接用于精确判断;它反映的是磁盘空间层面的共享推测,不等于 layer ID 级别的严格复用
识别仓库中实际冗余的层(非共享层)
仓库侧无法直接运行 docker 命令,但可通过镜像 digest 和 layer digest 的映射关系反推。关键步骤如下:
- 拉取目标镜像时加 --digests 参数(docker images --digests),记录每个镜像的 manifest digest
- 用 docker manifest inspect
查看该镜像引用的所有 layer digest(即每层的内容哈希) - 收集一批镜像的 layer digest 列表,统计每个 digest 出现频次:频次 = 1 表示该层仅被一个镜像使用,属于潜在冗余层
- 特别关注基础镜像更新后遗留的旧层——例如 ubuntu:20.04 更新为 20.04.6 后,旧版本镜像仍持有已不再被新镜像引用的 layer digest
借助工具做可视化层对比
手动解析 digest 效率低,推荐用以下方式快速定位重复与独占层:
-
dive 工具:本地运行 dive
,可交互式展开每一层,显示文件变更、大小、是否与其他镜像共享(需配合本地已有镜像) -
skopeo + 脚本:用 skopeo inspect --raw docker://
/ 获取远程仓库原始 manifest,提取 layers[].digest 字段,批量聚合分析: - 私有仓库如 Harbor:启用 content trust 或开启审计日志后,可结合 API 查询 layer 引用计数(部分企业版支持)
区分“标签冗余”和“层冗余”
很多人误删标签以为清了空间,其实没动层。要明白:
- 标签(tag)只是指向镜像 manifest 的指针,删除 tag 不删除 layer,只要还有其他 tag 或 manifest 引用它,层就继续占用空间
- 真正释放层存储,必须确保:该 layer digest 不再被任何 manifest 引用,且仓库执行了垃圾回收(如 Harbor 的 GC 任务、Docker Hub 的自动清理)
- 查悬空层:在支持 OCI 的仓库中,可调用 /v2/
/blobs/sha256: 接口返回 404,说明该层已无人引用











