Docker 磁盘占用 187GB 怎么清理?我对比了 Linux、WSL2、Docker Desktop 的 6 个坑和参数

🔑 关键词:Docker磁盘清理,overlay2,BuildKit缓存,json-file日志,Docker Desktop

📖 摘要:从 /var/lib/docker 187GB 说起,实测 docker system df -v、builder prune、日志 max-size 和 WSL2 VHDX 压缩,附步骤和参数。

Docker 磁盘占用 187GB 怎么清理?我对比了 Linux、WSL2、Docker Desktop 的 6 个坑

图片

先说结论:如果你在开发机上只会 docker system prune -a,大概率会删掉不该删的卷,还不一定把磁盘真正降下来。我上周三在 Ubuntu 22.04 上遇到一次:1TB NVMe 根分区只剩 31GB,/var/lib/docker 占 187GB。docker system df 显示 Images 42.3GB、Containers 1.2GB、Local Volumes 9.8GB、Build Cache 133.7GB。最离谱的是 BuildKit 缓存,不是镜像。后来我按“先诊断,再限日志,再清缓存,最后动卷”的顺序,降到了 46GB。下面不是标准教程,是我踩过的坑和对比。

1. 先别 prune,先看这 4 个数字

第一步别急着敲 docker system prune -a --volumes。那个命令会删掉所有停止容器、未使用网络、dangling 镜像,--volumes 还会把未使用卷一起删。我的测试 PostgreSQL 卷当时只是容器停了,差点没了。先跑:

docker system df -v
du -sh /var/lib/docker/overlay2 /var/lib/docker/containers /var/lib/docker/volumes
docker ps -s

图片

docker system df -v 能拆到每个镜像、容器、卷、构建缓存。docker ps -s 里的 SIZE 是容器可写层,不包括卷。overlay2 目录大,可能是可写层、日志,也可能是已删除但被进程占用的文件。真正要看的还有日志:docker inspect --format='{{.LogPath}}' 容器名。默认 json-file 驱动没有上限,一个疯狂打日志的 Node 服务能写到 20GB 以上。查找大日志:find /var/lib/docker/containers -name '*-json.log' -size +100M -exec ls -lh {} \;

2. 日志驱动对比:json-file、local、journald,别小看 max-size

Docker 默认日志驱动通常是 json-file。它最好懂,但默认不限制。local 驱动默认每个容器保留 100MB 左右,由 5 个 20MB 文件组成,日常更省心,但 docker logs 之外的工具链要确认兼容。journald 把日志交给 systemd,不占 /var/lib/docker/containers,但会占 /var/log/journal,而且 docker logs 仍可用。none 完全不记,适合纯计算任务,出问题你会哭。

我最后在 /etc/docker/daemon.json 用了:

图片

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

然后 sudo systemctl restart docker。注意:这不会自动改已有容器,已有容器要重建,或者在 Compose 里写 logging.options.max-size=10mmax-file=3。单个容器 10MB×3,100 个容器最多 3GB,比无上限好太多。如果你用 docker run,记得加 --log-opt max-size=10m --log-opt max-file=3

3. 清理顺序和参数:镜像 14 天,构建缓存 7 天,卷要备份

我的清理顺序是这样的,参数可以直接抄,但先确认业务能接受:

图片

  1. 停止容器:docker container prune 只删已停止容器。
  2. 清 dangling 镜像:docker image prune
  3. 清构建缓存:docker builder prune --filter until=168h,即 7 天前。Docker 23.0+ 默认 BuildKit,多 builder 用 docker buildx ls 看,再 docker buildx prune --all --filter until=168h
  4. 清未使用镜像:docker image prune -a --filter until=336h,即 14 天前。
  5. 卷:docker volume ls,先 docker run --rm -v 卷名:/data -v $PWD:/backup alpine tar czf /backup/卷名.tgz -C /data . 备份,再 docker volume prune

对比一下:docker system prune 默认不删卷,但会删停止容器、未使用网络、dangling 镜像和构建缓存;docker system prune -a 会删所有未被容器使用的镜像;docker system prune -a --volumes 最狠,生产上别随手敲。--filter until=24h 适合 CI,until=336h 适合开发机。我实测清掉 133.7GB Build Cache 后,/var/lib/docker 从 187GB 到 51GB,docker image prune -a --filter until=336h 又降了 5GB 左右。

4. Linux 原生 vs WSL2/Docker Desktop:一个立即释放,一个要压缩虚拟盘

图片

这是很多人忽略的对比。Linux 原生 Docker Engine 用 overlay2,删除镜像、容器、缓存后,ext4/XFS 空间通常很快回来。Docker Desktop on Windows 的 WSL2 后端不一样:容器里删了 50GB,宿主机 docker_data.vhdxext4.vhdx 可能还是 120GB。因为虚拟盘只增不减。你要先在里面清理,再关 WSL,再压缩虚拟盘。

Windows 步骤我实操过:先在 Docker 里 docker system prune -adocker builder prune --all,然后 PowerShell 执行 wsl --shutdown。找到虚拟盘,新版常见路径是 C:/Users/<你的用户名>/AppData/Local/Docker/wsl/disk/docker_data.vhdx,旧版可能是 .../Docker/wsl/data/ext4.vhdx。用管理员 CMD 进 diskpart

select vdisk file="C:/Users/你的用户名/AppData/Local/Docker/wsl/disk/docker_data.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit

压缩前最好确认没有容器在跑。Mac 的 Docker Desktop 也有类似问题,磁盘文件通常在 ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw,Docker Desktop 4.30 以后可在 Settings > Resources 里调 Disk image size,但已经膨胀的 RAW 不一定自动缩,最稳的是清理后做一次 Reset/Purge,代价是全删。

图片

5. 我的独立观点:Docker 磁盘治理不是“定期 prune”,而是分层预算

我不建议把 docker system prune -a --volumes 写成 cron。那相当于把日志、镜像、缓存、卷四种不同生命周期的东西一把梭。更合理的是分层:日志当数据库,设硬上限,max-size=10mmax-file=3;镜像当发布产物,保留 14 天或最近 5 个 tag;BuildKit 缓存当 CI 资产,保留 7 天;卷当业务数据,只能备份后人工或脚本按标签清理。

如果你只记一个命令,先跑 docker system df -v;如果只改一个配置,先改 daemon.json 的日志上限;如果只清一项,先清 BuildKit 缓存,因为它在开发机上最容易涨到几十 GB。Docker 本身不背锅,默认配置是通用值,开发机需要自己的容量预算。

最后补一个坑:用 rm 删正在写入的 *-json.log,空间可能不释放,因为 Docker 进程还开着文件句柄。更安全的做法是 truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log,或者干脆重启容器。生产环境改日志驱动和重建容器前,先确认 docker logs 采集链路是否依赖 json-file。

🏷️ 标签: