遇到一个Linux系统文件被删除后仍占用磁盘的问题

在使用Linux系统中,有时候发现明明已经删除了大量的文件(特别是日志文件),但用du查看磁盘,空间依然没有减少,这种非常奇怪的问题,最近遇到了。本文描述一下前因后果,并给出一些个人看法。

起因

最近项目准备上线,同事查了服务器磁盘使用情况,发现隔了几天磁盘空间就减少几百兆,叫我查一下。
环境描述如下:
1、nodejs应用服务,使用pm2管理,每天会产生大量日志,并且要保留。(这是必要的,为了与厂家扯皮保留证据)
2、每天半夜,自动压缩日志。(必须的,否则日志太多了)
3、服务器上还有一期遗留的产品,包括源码,日志。已不使用。

经过

首先使用du -h --max-depth=1查看空间。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0       ./dev
0 ./proc
16K ./lost+found
961M ./root
337M ./var
2.5G ./usr
33M ./etc
8.0K ./opt
147G ./home
9.4M ./tmp
4.0K ./media
226M ./run
103M ./boot
4.0K ./srv
298G

注:有心的朋友如果计算各个目录的空间总和,则会发现其远远小于最后一行显示的空间大小。笔者在最后时刻才意识到。
/home目录是外挂的硬盘,此处暂不提。从上面信息看到,有几个目录可疑:

1
2
3
4
961M    ./root
337M ./var
2.5G ./usr
226M ./run

发现/usr/local/share/.cache/yarn/v2/var/cache/yum有缓存数据,可以删除。另外发现/root/root/path存在一些旧代码以及不再使用的日志文件、sql文件,/root/.pm2/有pm2产生的日志。

接着,查找最近1天修改过的文件,命令如下:

1
find ./* -mtime -1

得到的文件体积均不大,可能不是此方面原因。

到网上搜索,发现有文章介绍相关的内容,使用lsof |grep delete来查询已经删除的文件。结果出现大量文件列表,数量很多:

1
2
lsof |grep delete | wc -l
7344

示例如下:

1
2
3
4
5
node  786  root 15w   REG  202,64     1941641 5354776 /root/logs/log39.2019-07-26.txt (deleted)
node 786 root 16w REG 202,64 437345392 5354830 /root/logs/logtf.2019-07-26.txt (deleted)
node 786 root 17w REG 202,64 471811 5354836 /root/logs/logtt.2019-07-26.txt (deleted)
node 786 root 18w REG 202,64 1189231954 5354838 /root/logs/logt3.2019-07-26.txt (deleted)
node 786 root 19w REG 202,64 2003838 5354840 /root/logs/logit.2019-07-26.txt (deleted)

分别为命令、PID、拥有者、FD、类型、设备、大小、节点、文件名称。只关注命令、PID、大小和文件名即可。

随便找一个进程,查看一下占用的文件句柄fd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ls -al /proc/74408/fd
total 0
dr-x------ 2 root root 0 Jul 31 12:19 .
dr-xr-xr-x 9 root root 0 Jun 27 12:27 ..
lrwx------ 1 root root 64 Jul 31 12:19 0 -> socket:[1673886202]
l-wx------ 1 root root 64 Jul 31 12:19 1 -> /root/.pm2/pm2.log
lr-x------ 1 root root 64 Jul 31 12:19 10 -> /dev/null
lrwx------ 1 root root 64 Jul 31 12:19 100 -> socket:[3240337185]
l-wx------ 1 root root 64 Jul 31 12:19 13 -> /root/.pm2/logs/1024-error-28.log (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 130 -> socket:[3207913087]
l-wx------ 1 root root 64 Jul 31 12:19 29 -> /root/logs/log34.2019-06-27.txt (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 3 -> socket:[2024014357]
l-wx------ 1 root root 64 Jul 31 12:19 30 -> /root/logs/log51.2019-06-27.txt (deleted)
l-wx------ 1 root root 64 Jul 31 12:19 31 -> /root/logs/logfa.2019-06-27.txt
l-wx------ 1 root root 64 Jul 31 12:19 32 -> /root/logs/log3f.2019-06-27.txt
l-wx------ 1 root root 64 Jul 31 12:19 33 -> /root/logs/log35.2019-06-27.txt (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 41 -> socket:[2024031696]
lrwx------ 1 root root 64 Jul 31 12:19 42 -> socket:[2024031697]

至此,能定位到原因了:nodejs服务使用log4js作为日志管理模块,每天定时备份,将旧日志文件压缩为新文件,再删除旧日志文件,但程序一直在运行,并未退出,即占用的文件句柄并未释放,所以要积累大量的已删除文件,占用磁盘空间。

解决

将nodejs服务手动重启一次,释放了很多空间,下面是重启前的磁盘空间:

1
2
3
4
5
6
7
8
Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/xvda2 37024320 27152180 7984756 78% /
devtmpfs 3983648 0 3983648 0% /dev
tmpfs 3981888 0 3981888 0% /dev/shm
tmpfs 3981888 25672 3956216 1% /run
tmpfs 3981888 0 3981888 0% /sys/fs/cgroup
tmpfs 747024 0 747024 0% /run/user/0
/dev/xvde 515930552 189452476 300263676 39% /home

重启后,空间列表如下:

1
2
3
4
5
6
7
8
Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/xvda2 37024320 9699008 25437928 28% /
devtmpfs 3983648 0 3983648 0% /dev
tmpfs 3981888 0 3981888 0% /dev/shm
tmpfs 3981888 25616 3956272 1% /run
tmpfs 3981888 0 3981888 0% /sys/fs/cgroup
tmpfs 747024 0 747024 0% /run/user/0
/dev/xvde 515930552 181515448 308200704 38% /home

其实笔者也有疑惑,按理说,log4js应该在备份日志后,会自动释放句柄,搜索资料未果。按观察之结果,重启一次nodejs即可解决问题。看来,后面要在一定时间间隔内,重启所有的nodejs进程才行。

指导

1、对于旧资料,该删除的要删除,不应保留,除了占用空间,没有其它用。本次排查,统计了旧资料,至少有1GB以上。囿于权限,本次不敢清理。
2、系统缓存(不管是npm、yarn还是apt、yum),建议隔一段时间删除。
3、对使用的程序模块要深入研究,不放过细节。(在追求速度时代,笔者还无法做到)
4、日志规范化,该写的信息要写,不该写的,不写。
5、程序安装目录,尽量不要选择系统目录,如/bin/sbin/usr/bin/usr/sbin,如果一定要安装,仅将必须的二进制安装在系统目录,配置文件、日志文件、数据文件,放到其它分区。本次发现,mysql和redis的数据目录与安装目录是在一起的,这样做不太合理。

拓展

yarn相关命令:
列出cache目录:yarn cache dir
列出详细文件:yarn cache list
清除cache:yarn cache clean
设置cache目录:yarn config set cache-folder <目录>

查找指定时间修改过的文件命令:
查找N天之内:find ./* -mtime -<N天>
查找N天之前:find ./* -mtime +<N天>
恰好N天:find ./* -mtime <N天>
-mtime以天为单位,类似的,-mmin以分钟为单位。

资料



PS:本文为笔者参考网络资料,结合实验经历得到的一些记录,并不权威,仅为个人观点而已。