Linux磁盘空间告急的四种真实成因与标准排障链条:从inode、lsof到长期防护
Linux 服务器磁盘空间告急是运维中最常见的一类告警:监控面板上根分区使用率冲到 95%,业务日志刷屏 "No space left on device",而 du -sh 一圈扫下来,各目录加起来却远小于实际占用。这种情况十有八九不是"真的存了这么多数据",而是文件被删除后空间没有释放。本文从 inode 与文件句柄的关系讲起,给出四种生产环境可直接套用的排障手法,最后给出一套能长期生效的防护策略。
一、为什么删了文件空间还在:inode 与硬链接计数
Linux 里一个文件由两部分组成:目录项里的文件名,以及真正的数据实体 inode。inode 上维护一个链接计数 i_nlink,记录有多少个文件名指向它。执行 rm 时,内核只是把文件的目录项摘掉、i_nlink 减一,并不会立刻回收数据块。只有当 i_nlink 归零,同时没有任何进程持有该文件的打开句柄,内核才真正释放空间。
所以只要还有进程拿着句柄,即使文件在目录树里已经看不见,数据块依然占据磁盘。这就是"df 显示满了、du 却统计不到"的根本原因。判断方法很直接:
lsof +L1
lsof | grep deleted
第一行命令专门列出链接计数小于 1 的已打开文件(即已被删除但句柄未释放),第二行是全量过滤。输出里的 SIZE/OFF 列就是被占住的字节数。找到占用最大的那个进程后,正确做法不是给整个进程发信号,而是重启或让它关闭对应文件描述符;日志类场景更推荐在重启前先做一次 truncate:
: > /proc/<pid>/fd/<fd>
注意这里的重定向必须作用在 /proc/<pid>/fd 路径上,直接对已删除的原路径操作是没有意义的。这个动作会立即释放数据块,且不会破坏进程的写入偏移,比重启服务平滑得多。需要注意的是用 -s 参数做 ftruncate 后,进程可能仍以旧的 offset 继续写,产生稀疏文件(默认 4KB 块会留有空洞),因此更稳妥的方案还是在日志框架层面做切割。
二、df 与 du 不一致的另外三种成因
第一种是挂载点遮罩。往一个已经被挂载目录的路径下写数据时,数据实际落在下层文件系统上,但 unmount 后这些数据会"凭空出现"。排查方式是 ls -l /proc/<pid>/root 对比,或者直接 mount | grep 目标路径,确认是否存在重叠挂载。
第二种是稀疏文件与预留块。ext4 默认会给 root 保留 5% 的空间,df 显示 100% 时普通用户其实已经写不进去,但总容量并没有被真正占满。tune2fs -m 1 /dev/sdX 可以降低预留比例(不建议设为 0,会影响碎片整理与写入稳定性)。XFS 则使用 inode64 和不同的分配策略,缩容需要用 xfs_growfs 而不能像 ext4 那样直接 resize2fs。
第三种是 inode 耗尽。空间还有剩余,但目录里新建文件报 No space left on device,df -i 一看 inode 使用率 100%。典型的元凶是邮件队列、缓存目录、容器镜像层里堆积了大量小文件。这时清理要按目录做,find <dir> -type f -mtime +7 -delete 配合 -xdev 参数避免跨文件系统误删。
三、排查的标准动作序列
把上面的知识串起来,形成一条固定的排障链条,能覆盖九成以上的磁盘告警:
1. df -hT 确认是哪个挂载点满了,注意 -T 看文件系统类型
2. df -i 同步看一眼 inode 是否耗尽
3. du -xh --max-depth=1 / 定位到具体目录,-x 禁止跨越挂载点
4. lsof +L1 检查是否有已删除但仍被占用的文件
5. find / -xdev -size +1G 查大文件,避免在 /proc /sys 上浪费时间
如果第 3 步和第 1 步的数字对不上,就重点看第 4 步;如果 du 的求和本身就对得上,那就老老实实按目录逐层下钻。值得注意的是 du 在文件数极多的目录下会跑很久,生产环境建议加 timeout 并限定深度,先从一级目录开始,定位到具体子目录后再加大深度。
四、长期防护:别等到告警才动手
手工排障只能救火,真正减少故障的是把边界前置。以下四条在生产环境验证有效:
第一,日志必须轮转且限总量。logrotate 的配置里同时设置 rotate 与 maxage,并加上 size 阈值触发,避免"按天轮转但单日暴涨"的情况。容器场景要额外注意 stdout 落盘策略,默认的 json-file 驱动不限制大小,长期运行会把宿主机磁盘写满,应在 daemon.json 里配置 max-size 与 max-file。
第二,临时目录定期清理。使用 systemd-tmpfiles 声明式管理 /tmp、/var/tmp 的保留周期,比手写 cron 更可控,也不会因为脚本被误改而失效。
第三,监控要同时盯容量和增长速率。只设置使用率阈值会在突发写入时来不及响应,增加一条"近一小时增长率"的告警能在真正写满之前留出处置时间。
第四,给关键分区留出独立的挂载点。把日志、数据、容器目录分开挂载,任何一个分区写满都不会连带影响系统根分区,故障影响面从"整机不可用"收窄到"某类业务异常"。
五、小结
磁盘告警的处理核心是理解一个事实:磁盘占用等于"目录树里看得见的文件"加上"已被删除但仍被进程持有的句柄"。排障时先用 df 和 du 判断两者是否一致,不一致就直接上 lsof +L1,用 truncate 而非重启来释放空间。长期来看,日志轮转、临时目录策略、增长速率告警和分区隔离这四件事做到位,磁盘类故障的频率能下降一个量级。工具本身都很简单,难的是把这些动作固化成团队的标准流程,让每一次告警都走同一条路径。
- 点赞
- 收藏
- 关注作者
评论(0)