前阵子在一台开发服务器上跑 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 会清理三类资源:已停止的容器、未被使用的网络、悬空镜像和悬空构建缓存。这里的“悬空”指的是没有标签、没有任何容器引用的资源。这个命令默认会要求你输入 y 确认,加上 -f 可以跳过确认。
如果我加上 -a,清理范围会从“悬空”扩大到“所有未被容器使用的镜像”,也就是说,你本地拉了很多镜像但当前没有容器在跑,这些都会被删掉。风险随之上来——下次再用就得重新拉取。在镜像下载很快或者有私有仓库兜底的场景下没问题,但如果带宽紧张,建议慎用。
加上 --volumes,就会把未使用的卷也一并清理。这是最危险的一个参数,因为卷几乎都是数据,一旦删了基本找不回来。我个人的习惯是:永远不要把 --volumes 放进默认清理脚本,需要清理卷时单独处理。
下面这张表可以帮你快速看明白:
| 命令 | 清理范围 | 风险 |
|---|---|---|
| docker system prune -f | 停止的容器、无用网络、悬空镜像、悬空构建缓存 | 低 |
| docker system prune -af | 上面全部再加所有未被容器引用的镜像 | 中 |
| docker system prune -af --volumes | 上面全部再加未使用卷 | 高 |
| docker system prune -af --filter "until=24h" | 只清 24 小时前创建的资源 | 低 |
--filter until=24h 这个参数很实用,适合在频繁构建的环境中做定时清理,只删旧的,不影响刚产生的资源。注意它过滤的是资源的创建时间或完成时间,不是最后使用时间。
2.2 一套稳妥的清理顺序
如果你不想背命令参数,记住这个操作顺序就够了,这也是我每次清理都会走的流程。
第一步,把不需要的容器删掉,尤其是那些已经停止、纯属占坑的:
bash复制docker ps -a
docker rm <container_id>
想一次清掉所有停止的容器,用:
bash复制docker container prune -f
第二步,清理悬空镜像。这条命令只删 <none> 标签的老镜像,不动那些还在用的:
bash复制docker image prune -f
如果确定本地镜像仓库可以随时拉取,再考虑加 -a:
bash复制docker image prune -af
第三步,清理构建缓存:
bash复制docker builder prune -f
想全清就加 -a:
bash复制docker builder prune -af
第四步,审视卷。执行:
bash复制docker volume ls -f dangling=true
看看有没有不再被任何容器引用的卷。确认无误后可以删,但这个动作一定要人工确认,千万不要写进自动脚本里。关于卷的安全边界,后面第五章会有更详细的分析。
这么一套走下来,空间基本能释放掉一半以上。
2.3 用 filter 按时间删,别在生产库上乱来
生产环境踩过一次坑之后,我再也不敢在业务机器上随手 docker system prune -af 了。那次是凌晨跑的清理,正好把团队临时用来排查问题的一个中间镜像给删了,第二天谁都没法复现那个构建流程,最后重新拉基础镜像、重跑构建,白白浪费了一个上午。
所以生产环境的清理一定要带时间过滤,比如只清理创建或停止超过 24 小时的容器:
bash复制docker container prune -f --filter "until=24h"
悬空镜像也类似:
bash复制docker image prune -f --filter "until=24h"
清理构建缓存时可以保留最近一小部分:
bash复制docker builder prune -f --filter "until=48h"
还可以用 --keep-storage 让缓存清理后至少保留指定大小的空间给未来构建用,比如:
bash复制docker builder prune -f --keep-storage 20GB
这个参数的意思是清理到缓存小于 20GB 就停手,适合不想彻底清空缓存的场景。实际使用中,我的建议是:开发机可以全清,生产机务必带时间窗口。
3. 进阶抠空间:镜像和构建缓存里的门道
3.1 悬空镜像 vs 未使用镜像,清理范围别搞错
这两个概念很多教程混着讲,但实际清理时差别巨大。
悬空镜像,也就是 dangling image,指的是没有任何仓库标签、也没有被任何容器使用的镜像。它的典型表现是 docker images 列表里出现一堆 <none>:<none>。产生原因很常见:你用同一个 tag 重新构建或重新拉取后,旧镜像的 tag 被新镜像顶掉,旧版本就变成了悬空状态。
清理悬空镜像用:
bash复制docker image prune -f
未使用镜像,指的是仓库标签还在,但当前没有容器引用它。docker image prune -a 才会删到这一层。两者一对比,一个是“没有身份也没人用”,一个是“有身份但暂时没人用”。
我曾经在一个项目里见过团队把所有构建产物都用 tag 固定下来,比如 myapp:20250101、myapp:20250102 这样一天一个。过不了两个月本地就有三四十个版本镜像,那都是典型的未使用镜像。如果全部保留,空间压力很大;如果直接 -a 梭哈,又怕误删。折中方案是按时间删:
bash复制docker image prune -af --filter "until=720h"
只删 30 天之前构建且未被使用的镜像,给回滚留了一点余地。
3.2 BuildKit 构建缓存:一个容易被忽略的大头
如果你跑过几次多阶段构建或者前端镜像构建,你会发现 du -sh /var/lib/docker/buildkit 有时候比整个镜像目录还大。这是很多人在清理时最容易漏掉的一块。
BuildKit 缓存的原理可以理解为:Dockerfile 里每一条指令执行完后,文件系统的变化都会被快照保存。下一次构建时,如果基础层和指令没有变化,BuildKit 会直接复用快照,从而大幅缩短构建时间。听起来很美好,但代价是每个快照都要占磁盘。
要清它,直接:
bash复制docker builder prune -af
这条命令会清掉所有未使用的 build cache。如果你的构建容器正在跑,正在使用的 cache 会被保留。
再进一步,可以从 Dockerfile 层面减少缓存体积。最常见的手段是多阶段构建,最终镜像只拷贝运行时需要的东西,把编译工具和中间产物留在 builder 阶段。比如一个 Go 项目,第一阶段用 golang:1.22 编译,第二阶段用 alpine 运行,第二阶段只复制编译好的二进制文件。这样最终镜像体积能少几个数量级,构建缓存里的中间快照也不会全部塞进最终产物里。
如果你用的是 Docker Compose 管理构建,可以给构建过程设置 BuildKit 缓存挂载,比如在 RUN 指令里加 --mount=type=cache,target=/go/pkg/mod,让依赖包缓存和镜像层解耦,清理起来也更容易。不过这是构建优化的话题了,这里不展开。
3.3 按仓库批量清理,从根上减少镜像冗余
有时候你不需要全盘清理,只针对某一个仓库的镜像做批量删除会更精准。比如你本地有一堆测试时随手构建的 test-* 镜像,可以用:
bash复制docker image ls -f reference='test-*'
先确认列表,再批量删:
bash复制docker image rm $(docker image ls -q -f reference='test-*')
这里 -q 只输出镜像 ID,$(...) 把这些 ID 作为参数传给 docker image rm。执行前务必看清楚列表,尤其是 reference 过滤条件写错的时候,很容易误伤其他镜像。
另一种常见情况是单独处理一个镜像名下的多个 tag,比如:
bash复制docker image rm myapp:20250101 myapp:20250102 myapp:20250103
手动指定 tag 删除,干净利落,不容易误删。
4. 日志才是沉默的磁盘杀手:容器日志清理与轮转
4.1 一个真实案例:两周没看日志,磁盘就爆了
有一次我帮朋友排查一台跑着 Nginx 容器和若干 Java 服务的服务器,df -h 显示根分区用了 98%,但 docker system df 里 Images、Containers、Volumes 各项都很小。当时我就怀疑是日志,跑了一下:
bash复制sudo du -sh /var/lib/docker/containers/*/
结果发现几个容器的日志文件动辄几个 GB,最大的一个 Java 服务容器的 *-json.log 已经超过 20GB。
Docker 默认的日志驱动是 json-file,它在容器启动后会把标准输出和标准错误按 JSON 格式写进宿主机文件。如果没有配置轮转策略,这个文件会一直增长,直到撑爆磁盘。很多应用自己不打日志文件,全靠 stdout 输出,日志全被 Docker 接走了,你从应用层面根本看不到。
4.2 已经爆了怎么救:truncate 而不是 rm
遇到日志文件已经很大的情况,很多人的第一反应是直接删文件:
bash复制sudo rm /var/lib/docker/containers/<id>/*-json.log
这是个非常经典的错误。容器进程还持有这个文件句柄,删除之后空间并不会立刻释放,反而可能出现“文件已删除但空间被占用”的现象,直到容器重启才还回来。
正确做法是把文件截断为 0,而不是删除文件:
bash复制sudo truncate -s 0 "$(docker inspect --format='{{.LogPath}}' <container_name>)"
docker inspect --format='{{.LogPath}}' 可以获取该容器的日志文件路径,truncate -s 0 把文件大小清零。这样做的好处是文件句柄不变,容器不用重启,空间立刻释放。
如果你有多个容器要处理,可以先列出所有容器的日志路径和大小:
bash复制for c in $(docker ps -q); do
echo "=== $c ==="
ls -lh "$(docker inspect --format='{{.LogPath}}' $c)"
done
然后对每个日志文件做同样的截断操作。建议操作前先确认一下哪些容器正在输出高频日志,免得刚清完又涨起来。
4.3 从源头治理:在 daemon.json 里配置日志轮转
截断只是救火,真正治本的是给日志加上轮转配置。
修改 Docker 守护进程配置文件 /etc/docker/daemon.json,加上日志大小和文件数量限制:
json复制{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
max-size 表示单个日志文件超过 10MB 就轮转,max-file 表示最多保留 3 个文件。这样单个容器最多占用 30MB 日志空间,再也不用担心某个容器悄悄把磁盘写满。
修改后重启 Docker 服务使配置生效:
bash复制sudo systemctl restart docker
注意,这个配置只对之后创建的容器生效,已经存在的容器不会自动套用新配置,需要重建容器。生产环境重建容器如果涉及数据卷,务必先确认卷的挂载方式。
单个容器启动时也可以临时指定:
bash复制docker run --log-opt max-size=10m --log-opt max-file=3 nginx
或者在 docker-compose.yml 里为具体服务单独配置:
yaml复制services:
web:
image: nginx:latest
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
我个人的经验是:daemon.json 里的全局配置必须做,这样才能覆盖以后所有新起的容器;具体的服务如果日志量特别大,再用 compose 里的 options 单独调大或调小。
5. 卷清理的边界感:哪些能删,哪些碰都不能碰
5.1 匿名卷为什么越堆越多
Docker 里有两种卷:命名卷和匿名卷。命名卷是你手动指定的,比如 -v mydata:/var/lib/mysql 里的 mydata;匿名卷是你在 -v /var/lib/mysql 这种格式下,Docker 替你随机起了一个名字的卷。
匿名卷特别容易积灰,因为删除容器时,默认情况下匿名卷并不会被自动删除。我用 Dockerfile 里的 VOLUME 指令或者 compose 文件里只写容器路径不写宿主机路径时,每次 docker compose up 都可能产生一个全新的匿名卷。项目跑几个月后,docker volume ls 里能看到一长串随机字符串名字的卷,就是这些沉积下来的匿名卷。
查看悬空卷:
bash复制docker volume ls -f dangling=true
这个 dangling=true 过滤出没有被任何容器引用的卷。注意,这里说的是“没有被引用”,不等于“无用”,里面可能存着某个历史容器的数据。所以下面这行命令虽然网上到处都是,但我不建议你无脑执行:
bash复制docker volume rm $(docker volume ls -q -f dangling=true)
5.2 删卷前如何确认它是否还被使用
判断一个卷能不能删,最稳妥的办法是看容器挂载信息。
先查这个卷被哪个容器使用:
bash复制docker ps -a --filter volume=<volume_name>
如果没有任何容器输出,说明当前没有活动容器引用它。但仅凭这一点还不够,因为你可能在三天前手动删除了一个容器,那个容器的数据还没来得及备份。所以更保险的做法是,删除前先查看卷的内容大小,必要时打包留底。
查看某个卷占用了多少空间:
bash复制docker run --rm -v <volume_name>:/data alpine du -sh /data
确认确实不需要之后,再逐个删除:
bash复制docker volume rm <volume_name>
如果你只用命令行的交互式操作,还有一个办法:执行 docker volume rm 时如果不小心误删了,可以尝试从恢复备份中找回。但前提是你得有备份,所以下面单独聊一下备份的问题。
5.3 小心重建容器时两个误删数据的参数
卷相关的误删事故,绝大多数发生在重建容器的时候。
第一个坑是 docker-compose down -v。很多人只知道 docker-compose down 会停止并移除容器和网络,但不知道加上 -v 之后会连所有卷一起删除。如果这些卷里存的是 MySQL 数据、ES 索引、Redis 持久化文件,那基本等于删库跑路。
第二个坑是 docker run -v 的匿名卷干系。执行:
bash复制docker run -v /var/lib/mysql mysql
这里没有指定宿主机目录,Docker 会创建一个匿名卷挂载到容器的 /var/lib/mysql。这个容器删除后,匿名卷依然存在。如果之后你再用 --rm 启动临时容器,又创建了新的匿名卷,数据根本不互通,也就容易把人绕晕。更危险的是 docker-compose up --force-recreate --renew-anon-volumes 这个组合,它会在容器重建时删除匿名卷并创建新的匿名卷,如果你的服务依赖匿名卷里的数据,重建完数据就没了。
下面这张表可以帮你区分常见命令的卷行为:
| 命令 | 命名卷 | 匿名卷 |
|---|---|---|
| docker compose down | 保留 | 保留 |
| docker compose down -v | 删除 | 删除 |
| docker compose up --force-recreate --renew-anon-volumes | 保留 | 删除 |
| docker rm <容器> | 保留 | 保留 |
| docker rm -v <容器> | 保留 | 删除(如果无其他容器引用) |
所以我的建议始终是:生产环境的重要数据挂载,一律使用命名卷,并且在 docker-compose.yml 里显式声明 volumes。这样至少你知道数据挂在哪个名字下,删不删自己心里有数。
5.4 数据库类卷:备份一步都不能省
如果你要清理的卷和数据库相关,无论它看起来多么无用,我都建议先备份再动手。
以命名卷 mysql_data 为例,停止依赖该卷的容器后,可以临时起一个工具容器,把卷内容打成 tar 包放到宿主机当前目录:
bash复制docker run --rm -v mysql_data:/data -v $(pwd):/backup alpine tar czf /backup/mysql_data_$(date +%Y%m%d).tar.gz -C /data .
恢复时也很方便:
bash复制docker run --rm -v mysql_data:/data -v $(pwd):/backup alpine tar xzf /backup/mysql_data_20250101.tar.gz -C /data
如果你不想用工具容器,也可以直接用 cp -a 拷贝 /var/lib/docker/volumes/<volume_name>/_data 目录。不过拷贝时要注意数据库的一致性,最稳妥的做法是先停容器再拷贝,避免拷到一半写入的数据导致文件损坏。
备份这事平时看着多余,真到误删的时候就知道值了。我现在对任何带 volumes 的操作,都会先问一句:这个数据丢了能不能重建?不能重建就先备份。
6. 把压力扼杀在源头:配置、脚本与长期维护习惯
6.1 daemon.json 一次配好,省掉以后很多事
前面提到的日志轮转,是 daemon.json 里最值得配的一项。除此之外,还有几个参数能帮你从源头控制磁盘用量。
一个是构建缓存的保留策略。在 Docker 较新的版本中,可以在 daemon.json 里对 BuildKit 的 GC 行为做配置,比如设置默认保留的缓存上限:
json复制{
"builder": {
"gc": {
"enabled": true,
"defaultKeepStorage": "20GB"
}
}
}
这个配置的含义是,当 BuildKit 缓存超过 20GB 时,Docker 会按自己的策略自动清理一部分旧缓存。这样一来,即使你忘记手动执行 docker builder prune,缓存也不会无限制增长。不同 Docker 版本对这段配置的字段支持有差异,配置前最好先看下当前引擎的文档,或者在测试环境验证一下。
另一个建议是检查当前的存储驱动。执行:
bash复制docker info --format '{{.Driver}}'
Linux 环境一般会输出 overlay2,这是目前推荐的驱动,空间释放逻辑比较成熟。如果你用的是老旧的 vfs 驱动,层与层之间完全不共享,磁盘占用会成倍增加,清理方式也很受限。存储驱动的切换会涉及已有镜像的重建,不要在生产环境随意变更,但要心里有数。
6.2 Docker Desktop 的虚拟磁盘膨胀与回收
如果你用的是 Windows 或 macOS 上的 Docker Desktop,还会遇到一个 Linux 服务器上不存在的问题:虚拟机磁盘文件只增不减。
Docker Desktop 本质上是在本机虚拟机里跑 Docker,镜像、卷、缓存都写在一个虚拟磁盘文件里。在 Windows 上通常是 WSL2 的 ext4.vhdx,macOS 上则是 Docker 虚拟机磁盘。你在 Docker Desktop 里删除大量镜像之后,可能会发现宿主机的磁盘空间并没有明显回升,这是因为虚拟磁盘不会自动缩小,已经申请到的空间仍然被虚拟机占着。
回收这部分空间,一般有两个途径。
一是通过 Docker Desktop 的 Troubleshoot 页面执行 “Clean / Purge data” 之类的清理操作,不同版本叫法略有不同,但效果类似:彻底重置 Docker 数据,会清空镜像、容器、卷,操作前务必确认这些数据不需要了。
二是针对 WSL2 的 vhdx 文件做压缩。大致思路是先执行 wsl --shutdown 关闭 WSL 发行版,然后用 diskpart 选择 vhdx 文件并执行 compact vdisk。这个操作有一定门槛,而且 Docker Desktop 正在运行时不能做,建议按官方文档或社区教程逐步操作。我自己的经验是:先把 Docker 内的资源用 docker system prune -af 清理一遍,再重启 Docker Desktop,然后再考虑压缩虚拟磁盘。顺序反了效果会差很多。
6.3 一份能直接用起来的定时清理脚本
手动清理适合偶尔处理一次,但真正解决磁盘焦虑,还得靠定期的自动化清理。下面这个脚本是我在测试服务器上用的版本,兼顾了安全和实用性:
bash复制#!/usr/bin/env bash
set -euo pipefail
LOG_FILE="/var/log/docker-clean.log"
echo "========== $(date) ==========" >> "$LOG_FILE"
# 清理已停止超过 24 小时的容器
docker container prune -f --filter "until=24h" >> "$LOG_FILE" 2>&1
# 清理悬空镜像,保留 48 小时内的构建产物
docker image prune -f --filter "until=48h" >> "$LOG_FILE" 2>&1
# 清理构建缓存,保留 48 小时内的缓存
docker builder prune -f --filter "until=48h" >> "$LOG_FILE" 2>&1
# 清理未使用的网络
docker network prune -f --filter "until=48h" >> "$LOG_FILE" 2>&1
# 卷清理不放入定时任务,仅打印提醒
echo "Dangling volumes:" >> "$LOG_FILE"
docker volume ls -f dangling=true >> "$LOG_FILE" 2>&1 || true
这里刻意把卷的清理排除在自动任务之外,只打印出来提醒你人工检查。生产环境上如果连卷都自动删,一旦误判后果会很严重。
脚本放到 /usr/local/bin/docker-clean.sh,赋执行权限:
bash复制sudo chmod +x /usr/local/bin/docker-clean.sh
然后编辑 crontab:
bash复制sudo crontab -e
加入一行,每周日凌晨 3 点执行:
cron复制0 3 * * 0 /usr/local/bin/docker-clean.sh
保存后生效。下次磁盘告警之前,它已经帮你处理掉大部分可回收空间了。
有一点要提醒:如果你在同一台机器上同时运行多个不常维护的旧项目,最好挨个看一眼这些项目有没有使用匿名卷。把 docker volume ls 里那些随机命名的卷的创建时间、容器归属查清楚,能删的删掉,该转命名卷的转成名卷。这一步做完,以后每次清理都会轻松很多。
我个人的习惯是每两周执行一次 docker system df -v 检查,不只是看总量,而是看是否有某个容器或项目在悄悄膨胀。Docker 清理这件事没有什么特别深奥的原理,核心就是分清数据类型、按风险分层处理、把重复动作自动化。捋顺了之后,磁盘空间其实一直在你手里。
