主机磁盘空间丢失问题排查

2024/08/21

某天午后,钉钉上突然上报了一条告警信息,显示一台主机上的根分区磁盘空间100%了。原以为是一个简单的问题,登录到环境上找到大日志,清除以释放空间就行。

登录到环境后,发现这个问题没那么简单,这个主机上基本上每个目录都有单独的挂载点,比如 /var,/usr/ 甚至连/opt都单独挂载出来。这对于根分区的磁盘空间排查十分友好,可以直接跳过/var这种里面有无数小文件的目录,因为它和根分区无关。排除了这些单独挂载点之后,系统上只有少量几个目录是在根分区下面,比如/etc/, /xxxdata等。对于这些目录单独执行du指令,发现总使用空间不到1G,而通过df 指令查看系统根分区上有20G的空间,并且这些空间都已经被占用,也就是说至少有19G以上的空间丢失了。

df和du统计出的磁盘容量有差异,这种问题并不罕见,常见的情形就是有一个进程持续不断地输出一个超大的日志,而用户错把这个日志文件直接删除了,这时候,由于该进程还在执行,这个日志文件的状态就会变成deleted。删除操作只是把文件名这个引用信息删除了,原进程还持有该文件句柄,可以继续写日志,文件实际还在存在系统上,只是在执行du时,无法通过文件名找到它,但是df统计里可以真实反应到这个文件的占用。这种情况下,我们可以通过lsof |grep deleted这个指令找到被误删除的文件。lsof可以取得进程的id,根据这个进程id,我们可以有两种处理选择:

  1. 重启对应的进程,释放文件占用
  2. 如果进程暂时不便被重启,我们可以cd /proc/进程id/fd 目录,执行echo "">文件句柄 ,清空被误删除的大文件

然后根据这个思路,执行lsof找deleted文件时,我们并没有发现被误删的大文件。lsof确实找到不少处于deleted状态的文件,经过检查这些文件都不在根分区上。

由于空间的占用是逐渐增长起来的,因此有理由认为这些文件目前还是在读写状态,通过lsof是可以看到这些文件的。我们可以通过如下语句将所有的lsof输出保存到文本文件中:

lsof 2>/dev/null |tee alllsofinfo.txt

lsof的输出如下所示

COMMAND      PID    TID TASKCMD             USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
systemd        1                            root  cwd       DIR              252,0      4096        128 /
systemd        1                            root  rtd       DIR              252,0      4096        128 /
systemd        1                            root  txt       REG              252,0   1609504   17053296 /usr/lib/systemd/systemd

每个文件可以看到Size和Device信息。通过走查这个文件,我们发现/var/下某个目录有部分文件很大,而且它归属的Device并不是/var/ 所挂载的文件系统,而是根分区的Device。 自此真相大白,这个文件空间丢失的原因是应用在文件系统挂载前启动,导致日志文件输出到根分区,而文件系统挂载上来后,这个日志文件被隐藏了,但是应用还是可以继续写这个文件,从而导致空间丢失的现象。