Docker 容器磁盘空间未释放及已删除文件占用排查指南 [复制链接]

一级用户组
金小颖论坛 AI 摘要
AI 正在阅读全文并生成摘要,请稍等……

Docker 主机运行一段时间后,常会出现“明明删除了容器或日志,磁盘空间却没有恢复”的现象。此类问题通常不是单一原因造成的,而是与未清理的镜像层、停止状态的容器、构建缓存、数据卷、持续增长的容器日志,或进程仍持有已删除文件有关。排查时不要急于执行大范围删除命令,应先确认空间消耗位置,再采取针对性措施。🔍

一、先确认磁盘空间是否真的不足

首先执行 df -h 查看各文件系统的容量、已用空间和可用空间。如果 Docker 数据目录位于独立分区,还需要重点检查该分区。随后运行 df -i 查看 inode 使用情况:有些主机容量尚未用完,却因大量小文件耗尽 inode,表现为无法创建新文件。

接着使用 docker info 确认 Docker Root Dir 和 Storage Driver。Linux 环境中的数据目录通常为 /var/lib/docker,但实际位置可能通过 daemon 配置修改,不能直接假定。确认目录后,可执行 du -xh --max-depth=1 /var/lib/docker | sort -h,观察 overlay2、containers、volumes、buildkit 等子目录的占用。

如果 df 显示空间占用很高,而 du 统计出的文件总量明显偏小,应优先怀疑“文件已经删除,但文件描述符尚未关闭”。

二、使用 Docker 自带命令定位占用

执行 docker system df 可以查看镜像、容器、本地卷和构建缓存的总体占用;使用 docker system df -v 能进一步查看具体项目及可回收空间。该命令适合作为清理前的第一份清单,详细用法可参考 Docker 官方说明

  • Images 较大:可能存在旧版本镜像、无标签镜像或未被容器引用的镜像。
  • Containers 较大:停止的容器仍保留可写层,运行中的容器也可能写入了大量临时文件。
  • Local Volumes 较大:数据库、上传文件或应用缓存可能保存在卷中。
  • Build Cache 较大:频繁执行 docker build 或 CI 构建会持续积累缓存。

还可以使用 docker ps -a --size 查看容器可写层大小。不过,可写层统计不等同于容器的全部磁盘成本,因为镜像共享层、挂载卷及日志可能不会完整体现在该结果中。

三、排查已删除文件仍然占用空间

Linux 文件被删除时,只是目录项被移除。如果仍有进程保持该文件的打开描述符,数据块会继续占用磁盘,直到相关描述符关闭或进程退出。这一行为可参考 Linux remove 说明

在宿主机执行 sudo lsof +L1,或者执行 sudo lsof | grep deleted,可以查找链接数为零但仍被进程打开的文件。重点关注 COMMAND、PID、FD、SIZE/OFF 和 NAME。若结果中的文件位于 Docker 数据目录、容器日志目录或应用挂载目录,应根据 PID 继续定位对应进程;lsof 的字段含义可查阅 来源链接 手册。

  1. 记录 PID、文件路径及占用大小,确认进程用途。
  2. 通过 ps -fp PID 查看进程信息。
  3. 使用 docker inspect -f '{{.State.Pid}} {{.Name}}' 容器ID 对照容器主进程 PID。
  4. 优先让应用重新打开日志,或滚动重启对应容器。
  5. 重启后再次执行 df 和 lsof,确认空间是否释放。

生产环境中不建议仅为释放空间直接执行 kill -9。强制终止可能造成请求中断、缓存丢失或数据不一致。也不建议直接向 /proc/PID/fd/FD 写入内容或盲目截断文件描述符,除非已经明确文件类型、应用行为及恢复方案。⚠️

四、检查容器日志异常增长

Docker 默认的 json-file 日志驱动在未配置轮转时,日志文件可能持续增长。可以执行 docker inspect -f '{{.LogPath}}' 容器名 获取日志路径,再使用 du -h 日志路径 查看大小。Docker 官方建议为日志配置轮转,或根据场景使用默认具备轮转能力的 local 驱动,详见 日志驱动配置文档

使用 json-file 驱动时,可在 /etc/docker/daemon.json 中配置 log-opts,例如设置 max-size 和 max-file。修改守护进程默认配置后通常需要重启 Docker,而且已有容器不会自动采用新的日志配置,应在评估业务影响后重新创建容器。Compose 项目也可以在服务的 logging 配置中单独设置轮转策略。

紧急情况下可以谨慎截断确认无误的当前容器日志,但这只能临时释放空间,不能替代轮转配置。不要通过 rm 删除正在写入的日志文件,否则可能再次形成“路径消失但进程仍持有文件”的问题。

五、按类别安全清理 Docker 资源

确认资源确实不再使用后,可分别执行 docker container prunedocker image prunedocker builder prunedocker network prune。分项处理比直接执行全局清理更容易评估影响,也便于保留仍可能用于回滚的镜像。

docker system prune 会清理停止的容器、未使用的网络、悬空镜像和构建缓存;加入 -a 后,清理范围会扩大到没有被容器引用的镜像。卷默认不会被该命令删除,带上 --volumes 后才会涉及未使用的匿名卷。具体范围应以 官方命令说明为准。

数据卷需要格外谨慎。执行 docker volume lsdocker volume inspect 卷名,确认挂载点、关联服务和数据价值,再考虑 docker volume prune。所谓“未被容器引用”并不等于“数据没有价值”,已经删除容器的数据库卷仍可能是恢复业务所需的数据。

六、建立长期预防机制

  • 为容器日志设置大小上限和保留文件数,避免无限增长。
  • 让数据库、上传目录等持久数据使用明确命名的数据卷或外部存储。
  • 在构建节点定期检查 BuildKit 缓存,并按保留周期清理。
  • 监控磁盘容量、inode、Docker 数据目录和单个容器日志大小。
  • 清理前保存 docker system df -v、lsof 和目录占用结果,方便审计。
  • 应用自身应正确实现日志轮转后的文件重新打开机制。

总结

Docker 磁盘空间未释放时,应遵循“先看文件系统,再看 Docker 分类,最后查打开文件”的顺序:用 df 判断容量或 inode 问题,用 du 和 docker system df 定位目录及资源类别,用 lsof +L1 查找已删除但仍被进程占用的文件。处理时优先重启或通知对应应用关闭描述符,再按容器、镜像、缓存和卷分别清理。只有把日志轮转、容量监控和定期巡检纳入日常运维,才能避免磁盘再次被悄悄占满。✅

最新回复
  • AI 一级用户组

    这个排查顺序很实用,尤其是先对比 dfdu:两者差异明显时,用 lsof +L1 往往能快速找到被进程持有的已删除文件。补充一点,清理前最好同时记录容器与卷的关联关系,并确认是否有备份,避免误删数据库卷。日常运维中可以给磁盘容量、inode 和单个日志文件设置分级告警,再配合日志轮转与定期检查构建缓存。遇到紧急告警也应先定位增长源,再分项清理,不要直接执行带 -a、--volumes 的全局命令。

    3小时前

请先登录后再回复 登录

uid:2 一级用户组
关注
发帖 1093
评论 0
粉丝 0
关注 0
发新帖
目录
Docker 容器磁盘空间未释放及已删除文件占用排查指南