docker system df 显示的镜像大小不准,因其统计的是所有层未去重的逻辑体积,同一基础层被多个镜像共用却重复计算,实际磁盘占用取决于 overlay2 中引用计数为1的层的 diff 目录大小。

docker system df 显示的镜像大小为什么不准
因为 docker system df 默认显示的是镜像的“逻辑大小”——即所有层加起来的未去重体积,不是实际磁盘占用。同一基础层被多个镜像共用,但这里会重复计算。
真正影响磁盘的是 overlay2(或 zfs/btrfs)底层存储中实际写入的块。尤其在频繁构建、打标签、删旧镜像后,逻辑大小和物理占用差距可能差 2–5 倍。
- 只看
docker images -s或docker system df的SIZE列,会误判空间压力 -
docker system df --verbose会列出各层 SHA256 和是否被引用,但不给出物理路径大小 - 真正要查磁盘占用,得直接进
/var/lib/docker/overlay2看硬链接计数和实际文件大小
查 overlay2 下某镜像真实磁盘占用的步骤
核心思路:找到该镜像最顶层的 layer 对应的 diff 目录,再顺着它的 lowerdir 链路,统计所有被它独占(引用计数为 1)的层目录大小。
实操建议分三步走:
- 用
docker image inspect <IMAGE_ID>找到RootFS.Layers数组,顺序就是从底到顶的 layer digest 列表 - 进
/var/lib/docker/overlay2,对每个 digest 找对应目录(可能带-init后缀),检查其link文件内容,再读diff目录下的merged和work可忽略,重点是diff - 用
find <diff_dir> -printf "%s\n" | awk '{sum += $1} END {print sum}'算单层物理大小;再结合ls -l | grep "hard link count == 1"过滤出仅被当前镜像引用的层,累加它们的diff大小——这才是该镜像“净增”的磁盘消耗
用 docker-slim 或 dive 工具快速验证层冗余
手动扒 overlay2 容易漏掉 hard link 计数变化,尤其在 docker build --squash 或 buildx 多阶段构建后。这时候用工具辅助更稳。
dive 是最贴近需求的轻量工具:
- 运行
dive <IMAGE_NAME>后,它会自动解压镜像层并统计每层文件数、大小、重复率 - 右侧栏明确标出 “% of Image” 和 “Re-used from base image”,比
docker history更直观反映冗余程度 - 注意:它默认不分析硬链接,所以对已存在的镜像,仍需配合
stat -c "%h" <diff_dir>看引用计数,否则高估“可删空间”
删除镜像后磁盘没释放?检查容器、构建缓存和 dangling 层
执行 docker rmi 后 df -h /var/lib/docker 没变化,大概率不是镜像本身问题,而是这些残留还在占着 overlay2 的物理块:
- 已停止但未
rm的容器:它们的overlay2/<ID>/diff仍被保留,运行docker ps -a -q | xargs docker rm清一遍 - 构建缓存(
docker builder prune):特别是用buildx构建的镜像,缓存层独立存在,docker system prune -a才能清掉 - dangling 层(
<none>:<none>):这些是中间层,没人引用但没被自动回收,docker images -f "dangling=true" -q | xargs docker rmi必做
最隐蔽的点:某些发行版(如 Ubuntu 22.04 的 snap 版 Docker)会把 /var/lib/docker 挂在单独子卷上,df 看的是宿主根分区,得用 btrfs filesystem usage /var/lib/docker 或 lsblk 确认挂载点真实设备。









