Docker 磁盘占用排查与清理
Docker 主机磁盘打满排查实战:从 47% 到告警只差一次镜像构建
摘要:Docker 主机磁盘使用率告警是最常见的容器运维故障之一,但“清理”远不止一条
docker system prune。本文从一次真实告警切入,拆解 Docker 磁盘占用的四大元凶(镜像、容器可写层、日志、卷),给出精准定位每个元凶的命令链,并说明不同清理策略的风险边界,避免“清完之后服务起不来”的二次事故。
一、场景引入
周一早晨收到告警:某台 Docker 主机磁盘使用率 91%。登上去:
$ df -h /
/dev/vda1 100G 91G 4.2G 96% /
这台机器上跑着十几个容器,其中有两个是数据库。此时最危险的动作是直接执行网上搜来的:
docker system prune -a --volumes # 千万别急着跑
--volumes 会连命名卷一起删——如果数据库数据在卷里没做外部备份,这一敲下去就是删库。正确姿势是先定位、再按风险等级逐项清理。
二、先看大盘:Docker 空间都花在哪
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 45 12 38.2GB 31.5GB (82%)
Containers 18 15 2.1GB 1.2GB (57%)
Local Volumes 9 7 22.4GB 3.1GB (13%)
Build Cache 63 0 4.8GB 4.8GB (100%)
关键看 RECLAIMABLE 列——可回收空间及占比。本例中镜像占 38GB、其中 31.5GB 可回收,是绝对大头;构建缓存 4.8GB 全部可回收。再看详细分布:
$ docker system df -v
逐镜像列出体积、逐容器列出可写层大小、逐卷列出占用,是精确定位的第一手数据。
三、四大元凶逐个排查
元凶一:悬空镜像与未使用镜像(通常是大头)
CI/CD 环境每天构建新版本,旧镜像不断堆积。定位:
# 悬空镜像(<none>:<none>,构建失败或被新版本顶替的残留)
$ docker images -f dangling=true
# 按体积排序看全部镜像
$ docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' | sort -h
清理(安全,不影响运行中容器):
$ docker image prune # 只删悬空镜像
$ docker image prune -a # 删除所有未被容器使用的镜像
注意 -a 的边界:有容器正在运行的镜像不会删,但“停止状态的容器”其镜像同样受保护——如果有些容器是临时停着的,它们会连带保护一批旧镜像不被回收。
元凶二:构建缓存
多阶段构建、频繁 docker build 会积累大量中间层缓存:
$ docker builder prune # 清理全部构建缓存
$ docker builder prune --filter until=48h # 只清 48 小时前的
构建缓存对下次构建速度有影响,清理后首次构建会变慢,属于“可接受代价”。
元凶三:容器日志(最容易被忽略)
docker system df 的 Containers 统计通常不含 json 日志文件,日志爆盘时 df 明明显示的是容器目录在涨。直接定位:
# 各容器日志文件实际大小(默认路径)
$ du -sh /var/lib/docker/containers/*/*-json.log | sort -h
单个容器的日志动辄几个 GB 很常见。立即止血(truncate 不会影响运行中的容器):
$ truncate -s 0 /var/lib/docker/containers/<容器ID完整串>/<容器ID完整串>-json.log
注意不能直接 rm——文件被 dockerd 持有句柄,rm 后空间不释放,且可能引起句柄异常。根治靠日志轮转配置,/etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
改完 systemctl restart docker(会重启所有容器,安排在维护窗口),此后每个容器日志上限 300MB 封顶。
元凶四:命名卷
$ docker volume ls
$ docker volume prune # 只删"无容器引用"的卷
绝对不要加 -a 以外的激进参数去删正在使用的卷。数据库卷即使显示“可回收”,回收前也必须确认数据已备份或有外部存储。
四、删了容器但磁盘没释放?
常见“灵异现象”:容器删了、镜像删了,df 还是不降。按顺序查:
# 1. 悬空卷(容器已删,卷还在)
$ docker volume ls -f dangling=true
# 2. 已停止容器的可写层(停止≠删除,可写层还在)
$ docker ps -a --filter status=exited
# 3. overlay2 目录残留
$ du -sh /var/lib/docker/overlay2/* | sort -h | tail -5
第 2 项是最常见原因——docker stop 只是停止,可写层(包括日志配置、临时文件)原封不动,要 docker rm 才释放。
五、安全清理决策表
| 命令 | 释放对象 | 风险等级 |
|---|---|---|
docker builder prune |
构建缓存 | 低(仅影响构建速度) |
docker image prune |
悬空镜像 | 低 |
truncate 日志文件 |
容器日志 | 低(不影响运行) |
docker image prune -a |
未使用镜像 | 中(回滚版本需重新拉取) |
docker container prune |
停止的容器 | 中高(确认容器不再需要) |
docker volume prune |
无引用卷 | 高(先确认无数据) |
docker system prune --volumes |
以上 + 卷 | 极高(删库级) |
六、长效机制:别让磁盘再打满
清理只是治标,治本靠三件事:
- 日志轮转:部署时就在 daemon.json 配置 max-size,一次配置永久生效;
- CI/CD 侧控制镜像数量:构建流水线里加定时清理任务,保留最近 N 个版本;
- 监控前置:对
/var/lib/docker目录做独立告警,85% 触发处理,别等到 95% 才动。
七、总结
Docker 磁盘排查的完整路径:docker system df 看大盘 → df -v 定位元凶 → 按决策表逐项清理 → 日志轮转根治。核心原则一句话:先定位再动手,volumes 永远最后碰,且碰之前先备份。
- 点赞
- 收藏
- 关注作者
评论(0)