Docker 日志轮转后磁盘没释放?一次句柄泄漏排查

举报
shijiu_dev 发表于 2026/10/30 09:53:34 2026/10/30
【摘要】 磁盘已满,du 却只找到少量文件?本文从 df/du 差异入手,用 lsof 和 /proc 定位被删除但仍打开的容器日志句柄,并给出 Docker 日志轮转的修复与验证步骤。

Docker 日志轮转后磁盘没释放?一次句柄泄漏排查

一台运行大量容器的构建节点突然提示磁盘空间不足,但用 du 扫描 /var/lib/docker 时,只统计到约 2 GB 文件,df 却显示已使用 48 GB。这个差异通常意味着:文件已经被删除,但某个进程仍然持有它的文件描述符,磁盘空间因此没有真正释放。

先确认不是统计口径问题

先对比文件系统使用量和目录实际占用:

df -h /var/lib/docker
du -xhd1 /var/lib/docker | sort -h

-x 参数可以避免 du 跨越挂载点。如果两者差距持续很大,就继续检查已删除但仍打开的文件:

lsof +L1

更直接的方式是遍历进程的文件描述符:

find /proc/*/fd -lname "*deleted*" -printf "%p -> %l\n" 2>/dev/null

本次找到的目标都指向 GitHub Actions runner 的 docker stdout 日志,路径已经显示为 deleted,但文件大小仍在增长。

根因:轮转和写入方没有同步

该节点在宿主机上额外配置了 logrotate,对容器日志执行 rename。容器进程仍然通过旧文件描述符写入,重命名只是改变了目录项,inode 没有变化;随后旧文件被删除,写入方没有重新打开新文件,空间就变成“看得见占用、找不到路径”的悬挂状态。

这类问题不等同于普通日志过大。直接 rm 新日志没有作用,因为占空间的是另一个已删除 inode;直接删除整个日志目录还可能影响正在运行的容器。

处理与长期修复

应急处理时,先确认持有句柄的进程。如果句柄属于单个容器,可以重启该容器,让写入方重新打开日志;如果句柄属于 dockerd 或 runner 服务,应重启对应服务。重启前确认没有正在执行的构建任务,避免中断业务。

长期应交给 Docker 自己轮转,不再用宿主机 logrotate 重命名容器日志。创建容器时配置:

--log-driver json-file
--log-opt max-size=100m
--log-opt max-file=3

已有容器需要重建才能应用日志驱动参数。Compose 中可写成 logging.options.max-size 和 max-file;Kubernetes 环境则应使用 kubelet 的 containerLogMaxSize、containerLogMaxFiles,并确认 runtime 使用的是 CRI 日志路径。

验证是否真正释放

修复后重复执行 df 和 du,观察两者是否重新接近;再用 lsof +L1 检查是否还有 deleted 日志。最后触发一次实际日志写入,确认新日志文件会按大小轮转,而不是继续增长。

这次故障的关键不是“删日志”,而是找到谁还持有旧 inode。先看 df/du 差异,再用文件描述符定位,通常比盲目清空目录更安全,也更容易找到可复现的根因。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。