Docker 磁盘清理实战:prune 命令的安全删除姿势

Docker 磁盘清理实战:prune 命令的安全删除姿势

Docker磁盘清理Ubuntu运维
📶 入门 🧩 Docker

/var/lib/docker 默认吃掉整个系统盘。镜像叠镜像、退出后躺着的容器、构建缓存一层层堆上去,磁盘告警通常是第一信号。清理这件事的难点在于:Docker 的 prune 家族命令长得像,动手范围差得很远,删错一次,数据库卷就没了。本文把五类对象的删除行为逐条列清,哪些能无脑删、哪些要加过滤、哪些默认根本不碰,看完就能安全回收空间。

清理前先看谁在吃空间

docker system df 是唯一正确的起点,直接动手删等于蒙着眼排雷:

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          18        4         6.8GB     4.2GB (61%)
Containers      12        3         1.1GB     890MB (80%)
Local Volumes   9         3         12.4GB    2.1GB (16%)
Build Cache     47        0         3.3GB     3.3GB (100%)

RECLAIMABLE 列就是可回收上限。上表这个典型状态里,大头在镜像和构建缓存;卷的 RECLAIMABLE 看着小,但那 16% 可能正是某个已停应用的数据库——回收它之前值得多想一秒。想看细节,加 -v 可以列出每个镜像、容器、卷的实际占用。

镜像:dangling 与未使用,两个概念别混

docker image prune 默认只删 dangling 镜像——没有标签、也没有任何容器引用的悬空层,多数来自重新构建后留下的旧层,删掉零风险:

docker image prune

加 -a 之后范围扩大到所有未被容器使用的镜像。注意判断标准是”当前有没有容器在用”,不是”以后还用不用”——停掉的备份脚本镜像、下周要用的镜像,都在被删之列:

docker image prune -a

想保留最近还可能用到的,用时间过滤。下面这条只删创建超过 24 小时且无容器引用的镜像:

docker image prune -a --filter "until=24h"
命令删什么风险
docker image prune仅 dangling 镜像几乎为零
docker image prune -a所有无容器引用的镜像停用应用镜像会丢,需重新 pull
加 --filter "until=24h"上述范围内创建超 24h 的范围更窄,风险更低

容器:退出状态躺久了也是负担

容器停止后不会自动消失(除非启动时带了 --rm),它的可写层继续占磁盘。先看一眼有多少退出容器:

docker ps -a --filter "status=exited"

然后统一清理。要更保守,可以只处理退出超过 24 小时的:

docker container prune
docker container prune --filter "until=24h"

动手前确认一遍:有没有需要 docker logs 回查故障现场的旧容器?排障周期长的服务,把日志先导出来再删。

卷:删错一次,数据就没了

卷是数据库文件、上传附件真正存数据的地方。Docker 的策略很保守——卷永远不会被自动清理,哪怕容器已经删掉。

docker volume prune 默认只删无主匿名卷(没有名字、当前无容器挂载)。但匿名卷正是 bind mount 用惯的人最容易忽视的角落:MySQL 这类镜像不声明卷名时,数据就落在匿名卷里。容器一删,匿名卷就成了孤儿,prune 一到,数据跟着走。

docker volume prune            # 只删无主匿名卷
docker volume prune -a         # 连命名但无容器挂载的卷一起删,高危

如果确实要跑,先给要保的卷打标签做白名单,再过滤掉它们:

docker volume ls -q | xargs docker volume label 2>/dev/null
docker volume prune --filter "label!=keep"

生产机上的实用动作是逐个核对:

docker volume ls
docker volume inspect <卷名> | grep Mountpoint
ls -lh /var/lib/docker/volumes/<卷名>/_data

能对上文件再决定删不删。这一步多花两分钟,比事后从快照恢复快得多。

网络与构建缓存:副作用各不相同

网络本身不占多少磁盘,但残留的自定义网络会留下 iptables 规则和网桥设备。删掉无容器使用的网络是低风险的:

docker network prune

构建缓存是低配机的隐形大户,反复构建的服务器上积累几个 GB 很常见。注意它会拖慢下一次构建:

docker buildx prune

一步到位全家桶与定时任务

docker system prune 是全家桶:已停止容器、无引用网络、dangling 镜像、构建缓存一次清完,但默认不含卷。加 --volumes 才会把无主匿名卷纳入范围——加不加这个参数,是安全与风险的分界线:

docker system prune -f                 # 不碰卷,可日常使用
docker system prune -f --volumes       # 含无主匿名卷,生产机慎用

-f 跳过确认提示。习惯的做法是挂 systemd timer 每周跑一次不碰卷的版本,再在业务低峰期手动处理卷。

清完之后:让下一次膨胀慢一点

回收空间只解决存量,增量要靠习惯:镜像用 精确版本 tag,少用 latest——拉取更新后旧镜像会立刻变 dangling,这就是悬空层的主要来源;多阶段构建控制镜像层数;数据库一律显式命名卷并配合定期备份(参考 auto-backup-to-cos-oss)。这样每周一条 docker system prune -f 就够,不用碰任何带数据的对象。

常见问题

Q:docker system prune 会把我的数据库数据删掉吗?

默认不会——它不处理任何卷。只有显式加 --volumes 时才会删无容器挂载的匿名卷。生产机上建议固定只用不带 --volumes 的版本。

Q:镜像删多了,下次部署会怎样?

本机镜像被删后,docker compose up 或 docker run 会重新从镜像仓库拉取。代价是时间和流量,国内服务器拉境外仓库可能很慢,重要镜像留 tag 少跑 -a。

Q:until=24h 里的 24h 是什么时间?

相对当前时间的时长,24h 表示创建时间在 24 小时前。也支持 10m、2h30m 等写法。

Q:清理后 docker system df 的数字没变小?

SIZE 统计的层可能被运行中容器共享,删除动作只影响 RECLAIMABLE 的部分。另外构建缓存如果启用了远端 builder,本地 df 看不到它,用 docker buildx du 单独查。

Q:被删的卷还能找回来吗?

不能。卷的删除没有回收站。唯一依靠是删除前的备份:数据库类卷定期 mysqldump/pg_dump 到宿主机或对象存储,匿名卷在清理前先 docker volume inspect 确认 _data 里的内容。

以 docker system df 开头、以分层清理收尾,磁盘告警就能从救火变成例行维护。记住一条就够:清理顺序从镜像和缓存开始,卷永远最后碰——前者最坏重拉一次,后者最坏丢的是数据。