登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux 日志轮转后磁盘空间没释放:用 失联文件句柄定位并恢复

来源:17golang原创

时间:2026-08-11 15:07:01 469浏览 收藏

凌晨值班运维最常碰到这种完全不合预期的磁盘告警:df -h /var 显示使用率已经冲到92%,但切到日志目录敲完 du -sh /var/log 算出来的总容量才几十GB。明明刚跑完logrotate,旧日志文件早就不在目录里了,占用的磁盘空间就是没释放出来。

这种情况别急着随手删别的文件腾空间。Linux的机制里,只要进程还握着对应文件的描述符,就算文件已经从文件夹里删掉了,进程还是能正常读写它,文件占的磁盘块就完全不会被回收。标准排查路径很清晰:先核对文件系统的统计差额,再找出带 失联 标记的持句柄进程,最后按照业务特性选 reload、受控重启,或者等journald完成自身轮转就行。

碰到这种日志删完空间不释放的场景,不用额外找冷门工具,顺着文件描述符链路走,10分钟内就能准确定位到占空间的失联文件,不会误操作影响线上业务。
要点速览
  • df 的统计结果明显比日志目录的 du 大,就是要排查失联文件句柄的明确信号,别上来就直接删整个目录。
  • lsof +L1 可以优先筛选出硬链接数小于1的已删除文件,重点关注输出里的COMMAND、PID、FD和SIZE/OFF几列。
  • 释放空间的核心是让持有句柄的进程主动关闭文件;对着已经失联的文件路径再删多少次都没用。
  • 服务管理器 journal要区分开活跃日志和归档日志,必要时先 journalctl --rotate 再执行vacuum清理操作。

先把 df、du 的差额拆出来

第一步只做观察操作,完全不动线上服务。先确认告警对应的挂载点,别把根分区、容器overlay存储或者独立数据盘的统计搞混:df -hT /vardu -xhd1 /var 2>/dev/null | sort -hdu -xhd1 /var/log 2>/dev/null | sort -h

df 统计的是整个文件系统已经分配出去的磁盘块,du 是遍历目录树统计当前还能直接看到的文件总大小。两者差值很大的时候,除了失联文件之外,还要排查挂载点遮挡、未销毁的容器层,还有文件系统本身预留的空间。要是 /var/log 统计的目录总大小只有18G,而 df 显示已用空间比预期多了30G,接着查未释放的文件句柄,比反复跑清理命令要高效得多。

Linux df 与 du 差额从文件系统占用指向 失联 日志句柄的排查流程

看统计数字时别忽略文件系统类型

df -hT 的类型列很关键。XFS、ext4还有容器的overlay存储,后续的排查细节差别不小;如果目标目录落在别的独立挂载点上,加 du -x 参数能避免把其他文件系统的占用算进来。这个阶段的唯一目标,就是确认「可见目录总大小」和「文件系统已用总量」确实存在明确差值。

用 lsof 找到仍在写入的失联文件

在Linux主机上,先用最精准的筛选条件定位已删除但仍被进程打开的文件:sudo lsof +L1,定位到大致范围后再用 sudo lsof -nP +L1 | grep -E '/var/log|/run/log|失联' 进一步缩小排查范围。

输出结果里重点看四列:COMMAND 是进程名,PID 是进程ID,FD 是对应的文件描述符,SIZE/OFF 可以快速判断哪个持有的失联文件占用空间最大。典型的输出结果类似 nginx 1842 www-data 7w REG 253,0 4294967296 ... /var/log/nginx/access.log (失联)

这里统计出来的4GiB空间根本不会出现在 ls /var/log/nginx 的结果里,因为对应的目录项已经被删掉了;但nginx的1842号进程还把它当成合法的写入目标。别直接对着文件描述符做删除操作,也不要上来就直接杀PID看效果。先确认这个进程归属于哪个服务管理器单元、当前有没有业务流量,还有日志轮转配置是不是已经把新日志指向了正确的存储路径。

把 PID 还原成对应的服务和启动方式

依次查看 ps -fp 1842sudo readlink -f /proc/1842/exesudo service status nginx --no-pagersudo ls -l /proc/1842/fd/7。如果是业务自己打包的守护进程,进程名大概率不会和文件名一致;这种情况就顺着父进程、启动参数和部署脚本往回溯源就行。

从失联句柄回到可控的恢复动作

恢复动作按照影响面从小到大排序。对支持重新打开日志文件的nginx、HAProxy或者自研服务,优先用官方指定的信号或者管理接口操作;对服务管理器托管、可以快速拉起的无状态服务,再考虑受控重启。每一步操作前先把PID、服务状态和当前磁盘的使用率数据留存下来,操作完立刻复查状态。

比如先执行 sudo service reload nginx,等两秒之后再执行 sudo lsof -nP -p 1842 | grep 失联 || truedf -hT /var 核验状态。如果失联条目还在,就说明reload操作没有关闭旧的日志句柄,或者reload之后worker进程的PID已经全部更新。这时候去翻对应服务的官方文档或者部署脚本,确认正确的reopen操作方式;生产环境里重启前,要提前确认健康检查、连接摘除和回滚窗口都准备妥当。

恢复检查清单
  • 操作前记录PID、服务状态和 df -hT
  • 优先执行reload或者reopen操作,确认新日志文件已经在正常写入增长。
  • 操作后再次执行 lsof +L1 核验,不能只等监控告警消失就完事。
  • 如果磁盘使用率没有下降,检查是不是还有其他PID持有对应的失联文件句柄。

服务管理器 journal 为什么清理后仍有空间占用

如果大空间占用的来源是 /var/log/journal,别直接套普通文本日志的轮转清理经验。journalctl --disk-usage 统计的时候会把活跃journal文件也算进去,而 --vacuum-size 只处理归档状态的journal文件,所以执行完清理之后,已用空间大概率不会直接降到你预期的数值。

可以先执行 sudo journalctl --disk-usage,再依次执行 sudo journalctl --rotatesudo journalctl --vacuum-time=14days,最后重新查看磁盘使用情况。如果团队要求按固定容量控制journal大小,也可以配置 --vacuum-size=2G;操作前先确认日志保留周期、审计要求和故障回溯窗口符合内部规范。

碰到journal空间持续异常增长的问题,要去检查 /etc/服务管理器/journald.conf 里的 日志容量上限运行日志容量上限 等相关配置,修改完之后用 service restart 服务管理器-journald 或者对应发行版文档指定的方式重新加载配置。

Linux 从 失联 日志句柄确认进程后通过 reload 或 journal rotate 恢复磁盘空间

常见问题:误区与边界状态

为什么 rm 删完文件,df 统计的使用率完全没变?

文件已经从文件夹里删掉之后,你再执行多少次rm都没有新的可操作目标。空间真正被回收,要等最后一个打开这个文件的文件描述符被关闭,一般对应服务执行reload、主动reopen日志或者受控重启的动作。

lsof +L1 没有输出,差值还可能来自哪里?

继续排查挂载点遮挡、容器存储层、文件系统快照、系统预留块,还有不同用户权限导致的文件可见性差异。也可以对比 findmnt -T /vardu -x 的统计覆盖范围,找出漏算的部分。

journalctl --vacuum-size 为什么没清到指定的大小?

这个命令只会处理符合条件的归档journal文件,当前正在写入的active文件还是会被 --disk-usage 统计进已用空间。先手动执行一次轮转再跑vacuum操作,最终的统计结果就完全符合预期了。

能不能直接重启持有失联文件的进程?

可以当成最后的兜底恢复手段,但操作前必须先确认服务可用性、现有连接的处理逻辑、健康检查规则和回滚路径。无状态服务和有状态服务不能直接用同一套重启流程。

把排查动作做成值班习惯

这类告警最容易踩的坑就是「看起来清理成功了,但是根因完全没解决」。把 df -hT、目录级 du -xlsof +L1 和服务状态校验放进同一份值班检查单,操作完成后要核验新日志正常写入、失联句柄完全清零、文件系统使用率回落这三个状态。日志轮转本身只是切换了日志文件的名字,真正释放磁盘空间的节点,永远取决于最后一个持有句柄的进程什么时候关闭文件。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>