空间耗尽是服务器在毫无恶意参与的情况下停止工作的最常见原因。站点返回 500,数据库写不进去,邮件发不出去——而与此同时 df -h 报告还有好几个 G 空闲。我们来把会出现这种情况的三种情形,以及关于 SMART 值得知道的东西,都过一遍。
情形一:inode 用完了
文件系统里有两种有限的资源:存放内容的空间,以及关于文件的记录。第二种是独立于第一种耗尽的:
df -h
df -i
如果第二条命令里的 IUse% 是 100,那么问题就是 inode。空间是有的,但连一个文件都创建不了,哪怕是空文件。
罪魁祸首总是那几个:几百万个小文件。/var/lib/php/sessions 里垃圾回收坏掉的 PHP 会话。从不清理的应用缓存。卡住的邮件队列。缩略图目录。可以这样找出它们:
sudo find / -xdev -type f -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20
这条命令会列出文件最多的目录。在大系统上它要跑好几分钟——这是正常的。
情形二:文件删掉了而空间没回来
更狡猾的情形。有人用 rm 删掉了一个膨胀的日志,但往里写的那个进程仍然把它开着。文件在目录里已经没有了,空间却仍被占着,而且会一直如此,直到那个进程被重启。
sudo lsof +L1
这条命令会显示在文件系统里链接数为零、却仍被某个进程持有的文件。治法是重启或轻柔地重载那个进程,而不是去追查「那几个 G 跑哪去了」。
由此得出一条规则:膨胀的日志不是删掉而是截断为空,这样打开着的描述符仍然可用:
sudo truncate -s 0 /var/log/huge.log
而且紧接着就把轮转配好——否则这一切会在一周内重演。
情形三:日志把它吃掉了
空间究竟去了哪里,靠一层一层往下走就能看到:
sudo du -x -h --max-depth=1 / | sort -h
sudo du -x -h --max-depth=1 /var | sort -h
-x 这个标志能让它不游荡到别的文件系统上去;没有它,统计会爬进 /proc 和网络挂载点。这一切用 ncdu 做起来更舒服,如果有的话。
常见的吞噬者:
- journald。一条命令就能了结:
journalctl --disk-usage。用/etc/systemd/journald.conf里的SystemMaxUse=500M一行给它设限,并用journalctl --vacuum-size=200M清理一次; - 安全工具的日志。Suricata 在默认配置下会写三十来种事件,而且不给自己配轮转——在一台真实服务器上它两天造出了 15 GB。audit.log 和 nginx 的调试日志也是同样的情况;
- 备份,写在同一块磁盘上而且从不删除;
- apt 的缓存——
apt clean偶尔能还回来一两个 G。
关于 /boot 的两句话
一个被旧内核填满的小分区。它本身不会让站点倒下,但它会把更新彻底卡死:新内核装不上,整个软件包队列都堵在它后面。用 apt autoremove --purge 治,用在自动更新设置里打开自动删除未使用内核来防。
SMART:哪些属性重要
先说一句提醒:在虚拟服务器上 SMART 通常是用不了的——磁盘是虚拟的,你看不到底下那块物理盘。这不是故障,只是根本没有数据。下面这一切说的是独立服务器和你自己的硬件。
sudo smartctl -a /dev/sda
SMART overall-health self-assessment test result: PASSED 这一行不是放心的理由:它几乎会一直绿到硬盘死掉为止。该看的是具体的计数器:
- 5,Reallocated_Sector_Ct——被重映射的扇区。不为零就说明硬盘已经在劣化;随时间增长则说明别拖了,换掉;
- 197,Current_Pending_Sector——读不出来、等待处置的扇区。其中最令人不安的一个:它通常意味着一部分数据已经无法恢复;
- 198,Offline_Uncorrectable——同样的东西,经检查确认过;
- SSD:Percentage Used / Media_Wearout_Indicator——已消耗的写入寿命。一个可预测的数字,也是据以规划更换的那个。
反过来说,温度和通电小时数本身什么也说明不了:一块跑了五年而错误计数器全为零的硬盘,比一块属性 197 上有个 1 的新盘更可靠。
盯着看的意义
上面这一切都是对已经发生的事情的反应。而磁盘被填满和 SSD 磨损两者都是缓慢且完全可预测的过程:两周的占用曲线,早在空间耗尽之前就能给出那个日期。「站点从凌晨三点起就挂了」和「周四得清一下日志」之间的区别,无非就是那个数字有没有摆在你眼前。汇总起来是什么样子,见下面的演示页面。