前言
很多老服务器(典型 CentOS7 环境)Docker 默认存储驱动是 devicemapper,而且大多是 loop-lvm 模式。你会遇到一个很诡异的现象: 执行完 docker system prune -a 清理镜像、容器之后,docker system df 显示内部空间已经释放,但宿主上 du -sh /mnt/docker/devicemapper/devicemapper 看到的文件大小依然很高(例如 13G),不会自动缩小。
这不是清理命令没生效,是 devicemapper loop-lvm 本身的设计缺陷。
官方早已标记 devicemapper 为废弃(deprecated),新环境一律推荐 overlay2。
现象
13G /mnt/docker/devicemapper/devicemapper
特征:
docker system df统计的镜像 / 容器占用远小于这个 13G;- 反复 prune 清理,该文件体积不会回落;
- 它是稀疏文件,thin pool 内部空闲空间已经释放,但文件本身不会向操作系统归还磁盘块。
先区分两个概念
- thin pool 内部空间:
docker system df看到的,prune 可以释放,Docker 后续拉镜像可以复用这块空间; - 宿主文件大小:
du看到的/mnt/docker/devicemapper/devicemapper/data,loop-lvm 稀疏文件只膨胀、不自动收缩。
第一步:基础排查
# 确认存储驱动
docker info | grep -A10 "Storage Driver"
# 查看 thin pool 状态
dmsetup status | grep pool
# 查看docker内部资源占用概览
docker system df
# 明细
docker system df -v
第二步:常规清理(只能释放 Docker 内部池空间,不能缩文件)
先把无用资源清理干净,避免池内持续膨胀:
# 清理停止容器、悬空镜像、无用网络、构建缓存(不删volume)
docker system prune
# 清理所有未使用镜像(不仅仅悬空<none>镜像)
docker system prune -a
# 带时间过滤,清理7天未使用资源(推荐定时任务使用)
docker system prune -a --filter "until=168h"
# 分项清理命令
docker image prune -a
docker builder prune
docker container prune
docker network prune
⚠️ 注意:
docker system prune -a --volumes会清理未被挂载的匿名卷,有丢数据风险,谨慎执行;- 命名持久化卷默认不会被 prune 删除;
- 上面所有命令执行完,devicemapper/data 文件大小大概率还是 13G,这是正常现象。
补充:fstrim 只能回收容器文件系统内空闲块,无法缩小外层 devicemapper 稀疏文件,效果有限:
docker ps -q | xargs -I{} sh -c 'docker inspect --format {{.State.Pid}} {} | xargs -rIpid fstrim /proc/pid/root'
第三步:想要把 13G 文件瘦身还给系统(loop-lvm 停机重建方案)
⚠️ 高危操作,必须先备份重要持久化卷数据,会清空所有镜像、容器,需要停机。
# 1. 停止docker服务
systemctl stop docker
systemctl stop docker.socket
# 2. 备份目录(可选,防止翻车)
cp -r /mnt/docker/devicemapper /mnt/docker/devicemapper.bak
# 3. 删除devicemapper整个目录
rm -rf /mnt/docker/devicemapper
# 4. 启动docker,自动重建全新稀疏文件,初始体积很小
systemctl start docker
启动之后再重新拉镜像、启动业务,data 文件会按需缓慢增长,不会卡死在 13G。
第四步:根治方案,切换到 overlay2(强烈推荐)
devicemapper loop-lvm 本身就不适合长期运行,overlay2 不存在稀疏文件只涨不缩的问题,是当前主流稳定驱动。
操作步骤:
- 备份所有业务 volume 数据、导出需要保留的镜像
- 停止 docker
systemctl stop docker
- 修改
/etc/docker/daemon.json,指定存储驱动与数据根目录
{
"storage-driver": "overlay2",
"data-root": "/mnt/docker"
}
- 清理旧 devicemapper 数据(确认备份完成再删)
- 启动 docker
systemctl start docker
- 验证驱动
docker info | grep "Storage Driver"
输出 overlay2 即切换成功。
补充:loop-lvm vs direct-lvm
- loop-lvm(当前场景):用两个稀疏文件模拟块设备,配置简单,缺点就是文件只增不减,适合测试,不建议生产长期使用;
- direct-lvm:使用独立裸盘 / 分区作为 thin pool,性能更好,支持 lvextend 扩容,但同样不支持自动缩容,只是底层不是宿主上的大文件。
运维建议
- 老 CentOS7 机器优先迁移 overlay2,不要在 devicemapper 上反复折腾缩容;
- 可以配置定时 prune 脚本,定期清理无用镜像缓存,延缓膨胀;
- 生产环境禁止使用 loop-lvm;
- 不要随便使用
--volumes参数清理卷,数据库等持久化数据优先使用命名 volume 单独备份。
小结
看到 /mnt/docker/devicemapper/devicemapper 占用十几 G 不要慌: prune 只能释放 Docker 内部 thin pool 空间;想要归还磁盘空间给宿主机,loop-lvm 只能停机重建目录;一劳永逸的办法是迁移到 overlay2 存储驱动。