KernelRanch
返回文章列表

Docker 占满磁盘:image、container、volume、build cache 各占多少,按什么顺序清

2026-11-10 #运维

Docker 的磁盘占用分四类,计量口径完全不同,混在一起看只会得出"镜像很大"这种没用的结论。正确做法是先 docker system df 拿到四类的总量与可回收量,再用 -v 展开到具体对象,最后按"删除风险从低到高"的顺序清理。我本机这份盘面很典型:镜像 2.912 GB、容器 100.7 MB、本地卷 14.52 GB、构建缓存 0 B——真正的大头是没人引用的卷,不是镜像。

先看总量,再看明细

docker system df          # 四类总量
docker system df -v       # 展开到每个镜像/容器/卷

本机实测(Docker Desktop 29.4.3):

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          11        3         2.912GB   1.808GB (62%)
Containers      5         5         100.7MB   0B (0%)
Local Volumes   35        0         14.52GB   14.52GB (100%)
Build Cache     0         0         0B        0B

四个数字的读法不一样:

类别SIZE 的含义清理命令风险
Images镜像虚拟大小(SHARED + UNIQUE)docker image prune(悬空) / -a(未被引用)-a 会删掉所有没有容器引用的镜像
Containers容器可写层docker container prune只删已停止的容器,运行中的不受影响
Local Volumes卷占用docker volume rm <name>卷里是数据,删了不可恢复
Build Cache构建中间层docker builder prune下次构建要重跑未命中缓存的层

清理顺序:从最安全到最危险

docker container prune                  # 1. 已停止的容器,通常最安全
docker image prune                      # 2. 悬空镜像(<none>:<none>)
docker builder prune                    # 3. 构建缓存,可用 --filter until=24h
docker image prune -a                   # 4. 所有未被容器引用的镜像
docker volume rm <name>                 # 5. 卷,逐个确认再删

每一步之后重新跑一次 docker system df,用回收量核对判断是否正确。第 4 步之前先用 docker image ls 把将被删除的列表打出来存档,因为 docker image prune -a 的确认提示不会列出具体对象,--filter 还支持 until=、label= 等筛选条件。

三个读数陷阱

第一,删掉一个 tag 不代表能回收 SIZE 那么多。-v 里 postgres:14 是 629 MB,其中 SHARED SIZE 87.71 MB、UNIQUE SIZE 540.9 MB;postgres:18 的共享部分也是 87.71 MB。这两个镜像共享同一批基础层,删掉 14 只会回收它独占的 540.9 MB。判断能回收多少,看 UNIQUE SIZE,不要看 SIZE。

同样地,awd-player 是 840 MB,其中 633.5 MB 与 awd-player-ai-controller 共享,UNIQUE SIZE 只有 206.8 MB,而且它被 3 个容器引用(CONTAINERS 列为 3)——这一条根本不在可回收范围内。

第二,RECLAIMABLE 的百分比是相对 SIZE 算的,不是相对磁盘。本机镜像一栏 62%,指的是 2.912 GB 里有 1.808 GB 可以回收;如果你只盯着百分比,会误以为还有 38% 的"浪费"没法处理,实际那是正在被容器使用的部分。

第三,卷的 ACTIVE 0 不等于可以删。35 个卷全部显示无人引用、100% 可回收,合计 14.52 GB,看上去删起来很爽;但"没有容器引用"和"数据没用"是两件事,卷常常是数据库、上传目录、证书的落盘位置,容器临时停掉时 ACTIVE 也会是 0。删之前用 docker volume inspect 看 Labels 和创建时间,能定位到是哪个 compose 项目留下的。

构建缓存单独算

Build Cache 是独立的一类,既不算在镜像里,也不受 docker image prune 影响。本机这一栏是 0 B,因为这台机器上没有跑过 docker build——这也说明"构建缓存占满磁盘"只发生在真正构建过的机器上。清理用 docker builder prune,加 --filter until=24h 可以只清一天前的缓存,保留最近构建的热层。

清理之后仍不降,去文件系统层看

Docker 的数据目录默认在 /var/lib/docker,overlay2 的每层数据、每个容器的可写层、卷都在这里。docker system df 统计的是 Docker 自己认识的对象,不包括容器内部写出的文件被记在可写层之外的部分,也不包括日志驱动写到宿主文件系统的内容。所以"清完镜像和卷,磁盘还是满"的情况下,直接对数据目录做一次:

du -xhd1 /var/lib/docker | sort -h        # 看 overlay2 / volumes / containers 哪块大
journalctl --disk-usage                    # 若日志驱动是 journald
du -sh /var/lib/docker/containers/*/*-json.log   # json-file 驱动的容器日志

json-file 驱动的容器日志默认没有大小上限,一个刷屏的容器写到几十 GB 很常见,而它在 docker system df 里只体现为容器的可写层大小,很容易被忽略。对策是在 daemon.json 里配 log-opts 的 max-size 与 max-file,或者改用其他日志驱动。

定期清理怎么配

docker system df 只回答"现在占了多少",不负责回收。要让它长期不失控,可以给几类清理各配一条定时命令,配合 --filter until= 把"最近还在用"的对象排除掉:

docker container prune -f --filter until=24h
docker image prune -f --filter until=168h        # 一周前的悬空镜像
docker builder prune -f --filter until=168h

until 接受 Go 的时长写法(24h、168h)和 RFC3339 时间戳,语义是"只处理创建时间早于该值之前的对象"。用时间过滤而不是直接 -a,能避免把刚构建好、还没来得及起容器的镜像删掉——这一点在 CI 机器上尤其重要,构建与部署之间的几分钟空窗期正好会被"无容器引用"的判定命中。

小结

四类占用要分开计量:镜像看 UNIQUE SIZE,容器看可写层,卷看引用数与业务含义,构建缓存单独清。顺序上先做无风险的三步(停止的容器、悬空镜像、构建缓存),再处理未被引用的镜像,最后才动卷。每步之后用 docker system df 复核,是这套流程里唯一能防止"清了 2 GB 但磁盘没变"的环节。

如果清完之后空间仍然紧张,问题往往不在 Docker 本身:/var/lib/docker 所在分区可能被日志、容器内写的文件、或者 overlay 的 upperdir 占满了。这时候要回到文件系统层面用 df/du 定位,而不是继续在 Docker 里删东西。

数据来源与检索时间

来源URL对应数据
docs.docker.com · docker system dfhttps://docs.docker.com/reference/cli/docker/system/df/四类占用的定义、-v 的 SHARED SIZE/UNIQUE SIZE/SIZE 关系、RECLAIMABLE 列含义
docs.docker.com · docker image prunehttps://docs.docker.com/reference/cli/docker/image/prune/默认只删悬空镜像、-a 删除所有未被引用镜像、--filter until=/label= 语法、确认提示不列出具体对象

检索时间:2026-11-10(Asia/Shanghai)。

核验说明

  • 真跑:docker system df 与 docker system df -v 在本机(Windows 11 + Docker Desktop 29.4.3)执行,文中所有数值(11 个镜像 / 2.912 GB / 35 个卷 / 14.52 GB / 构建缓存 0 B、postgres 两版的 SHARED 与 UNIQUE)均为原始输出。
  • 真跑:awd-player 的 CONTAINERS 列为 3 与 UNIQUE SIZE 206.8 MB 来自同一次 -v 输出。
  • 未执行:文中未运行任何 prune 命令。本机 35 个卷与 11 个镜像中有一部分属于同机其他项目(ai-controller、awd-player1..3 等运行中的容器),执行 prune -a 或 --volumes 会影响它们,因此清理动作只给出命令与顺序,未实际回收空间。
  • 仅依据文档:docker builder prune 的 --filter until= 行为、docker image prune -a --filter label= 的组合语义,按官方页面记录,本机无构建缓存可验证。