前阵子在一台开发服务器上跑 docker build,构建到一半直接报 no space left on device。我以为就是镜像太多,赶紧 docker image prune -f,顺手把没用的容器也清了一遍,结果第二天磁盘又飙回 90%。后来用 du -sh /var/lib/docker/* 拆开看才发现,真正占空间的根本不是我能看到的镜像列表,而是构建缓存、日志文件,以及一堆删除容器之后留下的匿名卷。
这也是 Docker 最让人头疼的地方——它不像普通软件,装了就占一块,删了就还回来。镜像层、容器可写层、构建缓存、数据卷、日志文件各自为政,你不把账捋清楚,光靠一两条 prune 命令永远是在打地鼠。这篇文章我就从基础到进阶,把 Docker 磁盘清理这件事完整梳理一遍,同时把我踩过的坑和验证过的方案一并放出来。
1. 磁盘又满了:先弄清 Docker 的空间到底花在哪
1.1 Docker 的“磁盘账本”:镜像层、容器层、构建缓存和数据卷
Docker 不是一块铁板,/var/lib/docker 下面同时住着好几类东西,每一类占的空间性质都不一样。不了解这个,后面所有清理命令都只是碰运气。
第一类是镜像层。你执行 docker pull 拉下来的镜像不是单个大文件,而是由很多只读层叠加而成。多个镜像之间还会共享相同的层,所以“删除镜像”并不意味着把所有空间都还给你,只有该镜像独享的层才会被真正释放。这也是为什么有时候你删了好几个镜像,df -h 看起来没啥变化。
第二类是容器可写层。每个容器启动后会在镜像层之上新建一个可写层,容器运行时产生的文件改动都写在这里。容器停止之后,这个可写层依然存在,除非你把容器删除。如果你只是 docker stop 而不 docker rm,空间会一直吊着不放。
第三类是构建缓存。用 Dockerfile 执行 docker build 时,每一层指令的产物都会进入 BuildKit 缓存。这些缓存的设计初衷是加速后续构建,但它们只增不减,而且增长速度远超很多人的预期。一个频繁构建的项目,缓存占几个 GB 是很正常的事。
第四类是数据卷和日志文件。卷分命名卷和匿名卷,用来持久化数据,比如数据库文件、上传目录。日志文件则藏在 /var/lib/docker/containers/<容器ID>/ 下,由容器日志驱动写入。这两类是最容易被人忽略、也最容易撑爆磁盘的部分。
把这个账本在脑子里建立起来,后面每一步清理才能知道自己在删什么。
1.2 动手前先用三行命令看清账目
不管磁盘是不是已经报警,我建议先跑这三条命令,把现状摸清楚。
bash复制df -h
这个看磁盘整体使用情况,重点看挂载点对应的分区,尤其是 / 或 /var 所在的分区剩余量。
bash复制sudo du -sh /var/lib/docker/* 2>/dev/null | sort -rh
这条命令直接列出 Docker 数据目录下每个子目录的占用大小,从大到小排列。哪块是大头一目了然。比如 containers 目录特别大,说明是容器日志;buildkit 特别大,说明是构建缓存。
bash复制docker system df
这条是 Docker 自己提供的统计视图,会按 Images、Containers、Local Volumes、Build Cache 分类,列出总数、活动数、占用大小和可回收大小。输出大致长这样:
code复制TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 18 6 11.2GB 8.4GB (75%)
Containers 9 2 2.1GB 1.9GB (90%)
Local Volumes 11 3 5.6GB 4.2GB (75%)
Build Cache 23 0 6.8GB 6.8GB (100%)
看到没,Build Cache 的 RECLAIMABLE 经常是 100%,因为只要没有正在进行的构建,所有缓存理论上都可以清掉。Volumes 回收率也很高,但卷不能乱清,这一点后面专门讲。
如果你还想看得更细,可以加 -v:
bash复制docker system df -v
它会列出每个镜像被哪些容器引用、共享层占了多少、独享层占了多少。这在定位“为什么删了镜像但空间没释放”时特别有用。
1.3 一个常见误区:删除镜像不等于释放磁盘
很多人删完镜像后磁盘没变,就开始怀疑 Docker 有问题。其实大部分原因是下面几种。
一是容器还在引用镜像。docker image rm 删一个被停止容器引用的镜像时,Docker 往往不会真正删除,而是把镜像标签改成 <none>:<none>,变成 dangling 状态。镜像本身和它占用的层还留在磁盘上,等容器删除之后才会释放。如果你有一堆长期停止不删的容器,镜像清理效果会大打折扣。
二是overlay2 共享层机制。两个镜像共享的层,只要其中一个还被引用,共享层就不能删除。所以回收空间的大小并不等于你删除镜像列表中看到的那几个 GB 相加。
三是构建缓存和日志被忽略了。你删了镜像,但 build cache 依然健在,日志也一分没少。特别是日志,很多人从来没想过要清它。
所以正确的排查思路永远是:先 du 找出大头,再针对大头处理,而不是一上来就盲目跑 docker system prune -a。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一刀:system prune 组合拳怎么打才安全
2.1 prune 家族参数逐个拆解
docker system prune 是 Docker 自带的聚合清理命令,但它不是万能的,参数不同,清理范围和风险完全不同。
最基础的 docker system prune 会清理三类资源:已停止的容器、未被使用的网络、悬空镜像和悬空构建缓存。这里的“悬空”指的是没有标签、没有
