场景概述
报 No space left on device 时,第一反应通常是查磁盘空间,但 ext4、xfs 这类文件系统的 inode 数量在创建时就已经确定,用尽之后即使还有大把空闲块,也无法再创建任何新文件——日志写不进去、会话文件建不出来、程序报错方式与磁盘满完全一样。这类问题的特征是容量指标正常而 inode 使用率接近 100%,且元凶几乎总是某几个目录里堆积了海量小文件。
场景信息
| 触发条件 | 有可用空间但创建文件失败,应用报 No space left on device |
|---|---|
| 排查目标 | 确认是否为 inode 耗尽,定位消耗源并清理 |
| 工具链 | df -i 定文件系统 → du --inodes 定目录 → find 定文件 → 分批清理 |
| 关键区别 | 块空间与 inode 是两套独立资源,各自的告警需要各自的处置方式 |
常见消耗源
- PHP、Java 等应用的会话文件目录:sessions、tmp 下累积几十万到上百万个小文件
- 邮件队列与 spool 目录:maildrop、queue 堆积大量小文件
- 缓存与临时文件:应用把每个请求的结果落成一个小文件且没有清理策略
- 日志目录:应用按请求或按分钟切分日志,产生海量碎片文件
- 容器与镜像层:overlay2 与 buildkit 缓存中的元数据文件数量巨大
- 版本控制与依赖目录:node_modules、.git 里的对象文件动辄数十万
四步排查命令
四步排查命令
# 第 1 步:确认是块空间还是 inode 用尽
df -i; df -i /var | awk 'NR==1 || $5+0>80'
# 第 2 步:按目录统计文件数,逐层下钻
du --inodes -x -d1 /var | sort -n | tail
du --inodes -x -d1 /var/lib/php/sessions | sort -n | tail
# 第 3 步:找出小文件重灾区(按目录计数)
find /var -xdev -type f -mtime +7 -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head
# 第 4 步:分批清理(先确认业务影响,再动手)
find /var/lib/php/sessions -type f -mtime +2 -delete
find /var/spool/postfix/maildrop -type f -delete
# 清理量很大时降低优先级,避免拖垮业务 IO
ionice -c3 nice -n 19 find /var/tmp/cache -type f -delete信号判读
| 现象 | 结论 | 下一步 |
|---|---|---|
| df -h 有空间,df -i 使用率 100% | inode 耗尽 | 按文件数而非容量找目标 |
| df -i 正常,df -h 已满 | 块空间不足 | 转场景「磁盘满」的流程 |
| 某个目录 du --inodes 数值异常大 | 小文件堆积点 | 确认业务归属后制定清理或轮转策略 |
| 删除后 inode 不下降 | 文件仍被进程持有或删除未生效 | 检查删除是否成功、进程是否仍在写 |
ext4 的 inode 总数在 mkfs 时就已确定,事后无法增加;xfs 支持动态分配 inode,因此 xfs 上很少出现这个场景。长期运行海量小文件业务时,建文件系统应显式提高 inode 密度(mke2fs 的 -i 指定每个 inode 对应多少字节,-N 直接指定总数),否则只能靠定期清理或迁移到对象存储。
清理海量文件时直接 rm 可能报 Argument list too long,或让 IO 长时间打满。用 find ... -delete 分批删除,配合 ionice -c3 与 nice 降低优先级;删除前先把目标目录缩小到确认过的范围,避免误删业务数据。
根治要改行为而不是反复救火:会话目录设定期清理任务、日志改用轮转而不是无限切分、临时文件加 TTL 策略。只清一次不调整策略,几天后同一目录会再次堆满。
监控要同时采集两套指标:块空间使用率与 inode 使用率。很多基础监控只采集 df 的块使用率,导致 inode 悄悄涨到 100% 才被发现。对小文件密集的业务目录,还应对目录内文件数量做趋势采集,提前看到增长而不是等告警。清理动作也要记入变更台账,便于事后复盘是哪个业务模块在持续产生小文件。