1, 错误现象
运维的监控系统发来通知, 报告一台服务器空间满了, 登录服务器查看, 根分区确实没有空间了, 如图 1 所示.
图 1 查看服务器磁盘空间
这里首先说明一下服务器的一些删除策略, 由于 Linux 没有回收站功能, 所以线上服务器上所有要删除的文件都会先移动到系统 / tmp 目录下, 然后定期清除 / tmp 目录下的数据. 这个策略本身没有问题, 但是通过检查发现这台服务器的系统分区中并没有单独划分 / tmp 分区, 这样 / tmp 下的数据其实占用了根分区的空间. 既然找到了问题, 那么删除 / tmp 目录下一些占空间较大的数据文件即可, 检查 / tmp 下最大的三个数据文件, 如图 2 所示.
图 2 查看 / tmp 下最大的前三个数据文件
通过命令输出发现在 / tmp 目录下有个 66GB 大小的文件 access_log, 这个文件应该是 Apache 产生的访问日志文件, 从日志大小来看, 应该是很久没有清理 Apache 日志文件了, 基本判定是这个文件导致的根空间爆满, 在确认此文件可以删除后, 执行如下删除操作:
[root@localhost ~]# rm /tmp/access_log
接着查看系统根分区空间是否释放, 如图 3 所示.
图 3 查看磁盘空间是否释放
从输出可以看到, 根分区空间仍然没有释放, 这是怎么回事?
2, 解决思路
一般来说不会出现删除文件后空间不释放的情况, 但是也存在例外, 比如文件被进程锁定, 或者有进程一直在向这个文件写数据等, 要理解这个问题, 就需要知道 Linux 下文件的存储机制和存储结构.
一个文件在文件系统中的存放分为两个部分: 数据部分和指针部分, 指针位于文件系统的 meta-data 中, 在将数据删除后, 这个指针就从 meta-data 中清除了, 而数据部分存储在磁盘中. 在将数据对应的指针从 meta-data 中清除后, 文件数据部分占用的空间就可以被覆盖并写入新的内容, 之所以在出现删除 access_log 文件后, 空间还没释放, 就是因为 httpd 进程还在一直向这个文件写入内容, 导致虽然删除了 access_log 文件, 但是由于进程锁定, 文件对应的指针部分并未从 meta-data 中清除, 而由于指针并未删除, 系统内核就认为文件并未删除, 因此通过 df 命令查询空间并未释放也就不足为奇了.
3, 问题排查
既然有了解决问题的思路, 那么接下来看看是否有进程一直在向 access_log 文件中写数据, 这里需要用到 Linux 下的 lsof 命令, 通过这个命令可以获取一个仍然被应用程序占用的已删除文件列表, 命令执行如图 4 所示.
图 4 查看被应用程序锁定的已删除文件列表
从输出结果可以看到,/tmp/access_log 文件被进程 httpd 锁定, 而 httpd 进程还一直向这个文件写入日志数据. 从第 7 列可知, 这个日志文件大小约 70GB, 而系统根分区总大小才 100GB, 由此可知, 这个文件就是导致系统根分区空间耗尽的罪魁祸首. 最后一列的 "deleted" 状态说明这个日志文件已经被删除, 但由于进程还在一直向此文件写入数据, 因此空间并未释放.
4, 解决问题
到这里问题就基本排查清楚了, 解决这一类问题的方法有很多种, 最简单的方法是关闭或重启 httpd 进程, 当然也可以重启操作系统, 不过这些并不是最好的方法. 对待这种进程不停对文件写日志的操作, 要释放文件占用的磁盘空间, 最好的方法是在线清空这个文件, 具体可以通过如下命令完成:
[root@localhost ~]# echo " " >/tmp/acess.log
通过这种方法, 磁盘空间不但可以马上释放, 也可保障进程继续向文件写入日志, 这种方法经常用于在线清理 Apache,Tomcat,Nginx 等 web 服务产生的日志文件.
来源: http://os.51cto.com/art/201912/608713.htm