服务器突然报 “No space left on device”,第一反应多半是磁盘满了。可你敲 df -h 一看,明明还躺着十几个 G 空闲——这就矛盾了。真正的元凶往往是 inode 耗尽:文件系统里的索引节点被海量小文件占光,磁盘块还剩一大堆,内核却拒绝新建任何文件或目录。下面用四条命令把问题看清、定位、清理、根治。
一、先确认:是块满还是 inode 满
df -h 看的是“数据块”用量,df -i 看的是“索引节点”用量,两者根本不是一回事。一张表分清:
| 命令 | 看什么 | 满了报什么 |
|---|---|---|
df -h |
数据块(容量) | 磁盘空间不足 |
df -i |
索引节点(文件数) | inode 耗尽、建不了文件 |
$ df -h /var
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 18G 22G 45% /
$ df -i /var
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 2621440 2621440 0 100% /var
只要 df -i 的 IUse% 到 100%、而 df -h 还有空闲,就坐实是 inode 问题。注意 inode 总数在 mkfs 时按“每多少字节分配一个”固定下来,ext4 上不能在线调整,只能重建文件系统才能改变密度。
二、三步定位谁吃掉了 inode
inode 被占满,通常是某个目录塞了几百万个小文件。按顺序排查:
查总量
du –inodes 看各区占用
找目录
find 数文件定位元凶
绕开
TMPDIR 指向 /dev/shm
# 1) 全系统按目录统计 inode 占用(GNU coreutils 8.22+)
$ du --inodes /var/* 2>/dev/null | sort -n | tail -20
# 2) 锁定某目录后,数普通文件数
$ find /var/spool -xdev -type f | wc -l
# 3) /tmp 也满时,临时绕到内存盘
$ export TMPDIR=/dev/shm
常见元凶:PHP 的 /tmp session 文件、邮件队列 /var/spool/postfix/maildrop、没跑 logrotate 的日志目录、Docker 容器产生的零散层文件。
踩坑实录
上周一台生产机告警,我 rm -rf /tmp/sess_* 清掉两百多万个 session 文件,满以为 inode 能回落。结果 df -i 还是 100%。后来用 lsof +L1 | grep deleted 才看到:一个早被我删掉日志的进程还攥着文件句柄,inode 一直不释放。重启该进程后,IUse% 立刻从 100% 掉到 9%。教训很硬——删文件不算释放,得先让持有句柄的进程退出。
三、清理与临时绕开
确认元凶目录后直接清理,注意别误删正在写的文件:
# 只删 7 天前的 session,留当天的
$ find /tmp -name 'sess_*' -type f -mtime +7 -delete
# 邮件队列爆了
$ find /var/spool/postfix/maildrop -type f -delete
若 /tmp 自身也满、连临时文件都建不了,先把 TMPDIR 指到 /dev/shm(内存盘)再操作,避免清理解除不了死锁。
四、根治:别让小文件无限增长
清一次只是止血,要治本得从生成端压住:
logrotate
在 /etc/logrotate.d/ 设 rotate 与 maxage,别让日志无限堆
systemd-tmpfiles
用 /etc/tmpfiles.d/ 定期清 /tmp,多数发行版默认开启
应用层
PHP session 改存 Redis,缓存加 TTL、分层目录
重建调密度
新盘 mkfs.ext4 -i 16384,1TB 约 6400 万 inode
常见问题
Q:inode 满了能像磁盘满那样直接扩容吗?
A:不能。inode 数在格式化时定死,ext4 无法在线增减,只能清理文件,或备份后重建文件系统并调小 bytes-per-inode。
Q:为什么 df -h 有空间还报 “No space left on device”?
A:这条报错不只看块,也看 inode,任一项耗尽都会触发,所以一定要 df -i 和 df -h 一起看。
Q:XFS 会好一点吗?
A:XFS 的 inode 是动态分配的,几乎不会整体耗尽,适合小文件极多的场景,可优先考虑。
如果你在运维里也踩过 inode 或磁盘相关的坑,欢迎到 fenij.com 留言,一起把排障笔记补全。