华为云 ECS 上 inode 耗尽排查:和磁盘空间已满的区别、定位占用与清理预防

举报
yd_252351073 发表于 2026/11/12 10:03:49 2026/11/12
【摘要】 在华为云 ECS 上,程序突然无法创建文件、包管理器报错、网站写不了会话或临时文件,日志里却经常只看到一句 No space left on device。很多人第一反应是看 df -h,结果发现磁盘空间明明还剩不少。这类情况往往不是空间用尽,而是文件系统的 inode 耗尽。inode 是文件系统给每个文件、目录、符号链接分配的索引节点,数量在部分文件系统上是有上限的;上限用尽后,即使还有...

在华为云 ECS 上,程序突然无法创建文件、包管理器报错、网站写不了会话或临时文件,日志里却经常只看到一句 No space left on device。很多人第一反应是看 df -h,结果发现磁盘空间明明还剩不少。这类情况往往不是空间用尽,而是文件系统的 inode 耗尽。inode 是文件系统给每个文件、目录、符号链接分配的索引节点,数量在部分文件系统上是有上限的;上限用尽后,即使还有空闲块,也无法再创建新文件。

下面以华为云 ECS 上常见的 Linux 镜像(Huawei Cloud EulerOS、CentOS、Ubuntu)为例,先把 inode 耗尽和磁盘空间已满分开,再按“确认哪一块分区、找出谁在占用、安全清理、做好预防”的顺序处理。文中的路径都是常见默认位置,动手前先确认本机实际路径,不要直接整目录删除。

一、先分清:空间不足还是 inode 不足

No space left on device 对应错误码 ENOSPC,磁盘块用尽和 inode 用尽都会返回它,单看这句无法判断原因。先同时看空间和 inode:

df -hT
df -iT

两份输出里都要看文件系统类型(Type)和挂载点,不要只看根分区。典型对照如下:

  • df -h 的 Use% 接近 100%,df -i 的 IUse% 不高:这是空间问题,应找大文件,或排查已删除但仍被进程打开的文件。那是另一类处理方式,本文不展开。
  • df -h 仍有空闲空间,df -i 的 IUse% 已经 100% 或非常接近:这就是 inode 耗尽。后面按这个方向查。
  • 两项都很高:两边都要处理,但创建新文件会立刻失败,可以先把 inode 降下来。

可以再做一个最小验证(请换一个确认可写、且不影响业务的目录):

touch /tmp/inode_probe_$$ && rm -f /tmp/inode_probe_$$

如果这里失败,而 df -h / 仍显示有空闲,先排除目录权限问题,再把它当作该挂载点 inode 不足来排查。若 /tmp 是独立挂载,请对实际报错的那个挂载点做同样测试。

顺便看一眼内核日志,但不要把希望全放在上面:inode 用尽时,内核不一定会打出一条非常醒目的专用记录,仍以 df -i 为准。

sudo dmesg -T | tail -n 50

有两点容易误判,需要先记住:

  1. inode 是按文件系统统计的,不是整台机器合计。系统盘用尽时,数据盘可能仍正常;反之亦然。必须对着出问题的挂载点看。
  2. XFS 的 inode 是按需从空闲空间里动态分配的,df -i 里的可用数量是估算值,含义和 ext4 不完全一样。EulerOS、CentOS 的部分镜像会用 XFS,Ubuntu 系统盘多为 ext4。先看 df -T,再解释 IUse%。XFS 上更常见的是空间先不够;ext4 上“空间还多、inode 先满”更典型。

二、找出哪些目录在消耗 inode

du 默认按磁盘占用(字节)排序,海量小文件往往占不了多少空间,却会吃掉大量 inode,所以不要用 du -sh 来定位这个问题。GNU du 提供了按 inode 计数的参数,EulerOS、CentOS 7 及以后、Ubuntu 上一般都可用:

# -x 表示不跨越文件系统,避免把数据盘、容器挂载或远程盘算进系统盘
sudo du --inodes -x -d 1 / 2>/dev/null | sort -n

哪个顶级目录数量突出,就再往下钻一层,例如:

sudo du --inodes -x -d 1 /var 2>/dev/null | sort -n
sudo du --inodes -x -d 1 /var/lib 2>/dev/null | sort -n

du --inodes 给出的是目录树合计,适合先圈定范围。若要找出“单个目录里堆了多少文件”(会话目录、缓存目录常常是这一种),可以用:

# 按“父目录里有多少文件”排序,只统计文件,目录本身另计
# 先把范围限定在上一步圈出的目录,避免对整盘做全量 find
sudo find /var -xdev -type f -printf '%h\n' 2>/dev/null \
  | sort | uniq -c | sort -nr | head -n 20

这条命令会遍历文件元数据,在文件非常多的盘上会有明显 I/O。建议先用 du --inodes 缩小范围,再对可疑目录执行;生产环境尽量选业务低峰,也可以加上 ionice -c3 降低 I/O 优先级:

sudo ionice -c3 find /var/lib -xdev -type f -printf '%h\n' 2>/dev/null \
  | sort | uniq -c | sort -nr | head -n 20

如果 du --inodes 不可用,可以用下面的备选方式统计某个挂载点下一层目录的条目数(同样不要漏掉 -xdev):

for d in /var /var/log /var/lib /tmp /home /usr; do
  [ -d "$d" ] || continue
  printf '%s ' "$d"
  sudo find "$d" -xdev \( -type f -o -type d -o -type l \) 2>/dev/null | wc -l
done

常见占用 inode 的位置包括:

  • PHP 会话文件:Ubuntu / Debian 多为 /var/lib/php/sessions,EulerOS / CentOS 多为 /var/lib/php/session(少一个 s),也有按版本分子目录的情况,以本机实际路径为准;
  • 应用自己写的缓存、缩略图、上传临时文件、按请求落地的小文件;
  • /tmp、/var/tmp 里长期无人清理的临时文件;
  • 邮件队列:/var/spool 下的积压;
  • 软件包缓存:Ubuntu 的 /var/cache/apt,EulerOS / CentOS 的 /var/cache/yum 或 /var/cache/dnf(这项更常表现为占空间,但小文件多时也会推高 inode);
  • 在系统盘上直接展开的 node_modules、多次重复解压的代码目录。

先看数量和文件年龄,再决定能不能删。不要对 du 结果里的目录直接 rm -rf。

三、安全清理:先确认,再删过期文件

清理前建议先记录当前状态,便于对照:

date
df -hT
df -iT

下面几种是比较常见、也相对好处理的来源。原则都一样:先 ls 或 find 看样本和数量,确认是过期或可重建的缓存后,再按时间条件删除。

1. PHP 会话文件

# Ubuntu / Debian
sudo find /var/lib/php/sessions -xdev -type f 2>/dev/null | wc -l

# EulerOS / CentOS
sudo find /var/lib/php/session -xdev -type f 2>/dev/null | wc -l

若数量达到几十万、上百万,多半是会话回收没有在跑(例如 session.gc_probability 为 0,且系统自带的清理定时任务被关掉)。先按时间查看,确认没有把仍在使用的会话一并清掉;下面以“超过 1 天”为例,路径请改成上一步确认过的目录:

# 先看数量,不要直接 -delete
sudo find /var/lib/php/sessions -xdev -type f -name 'sess_*' -mtime +1 | wc -l

# 确认无误后再删除
sudo find /var/lib/php/sessions -xdev -type f -name 'sess_*' -mtime +1 -delete

EulerOS / CentOS 上把路径换成 /var/lib/php/session。删完再执行一次 df -i,确认 IUse% 已经下降。Ubuntu 通常会通过 /usr/lib/php/sessionclean 做定期清理,EulerOS / CentOS 则可能依赖 systemd-tmpfiles 或 cron,清理后应检查这些任务是否还在。

2. 临时目录

sudo find /tmp /var/tmp -xdev -type f -mtime +7 | head
sudo find /tmp /var/tmp -xdev -type f -mtime +7 | wc -l

确认没有业务进程把重要文件放在这里之后,再删除过期文件。不要删除 /tmp 里仍被使用的套接字或锁文件。对不确定的文件,可以先移到一个临时目录观察,而不是直接删。

3. 软件包缓存

# Ubuntu
sudo apt-get clean

# EulerOS / CentOS 7
sudo yum clean all

# 使用 dnf 的 EulerOS / CentOS 8 及更新版本
sudo dnf clean all

4. 容器镜像层(如有)

容器运行时的存储目录里经常有大量小文件。如果确认占用主要来自 Docker 或同类运行时,应使用它们自己的清理命令(例如删除无用镜像、停止容器后的残留),不要手动删除 overlay2 或类似底层目录,否则容易把正在运行的容器写坏。这和“容器日志把磁盘写满”不是同一类问题:这里关注的是文件个数,不是某个 json-file 日志的体积。

清理后再次检查:

df -iT
touch /tmp/inode_probe_$$ && rm -f /tmp/inode_probe_$$

如果 IUse% 已经明显下降,并且可以重新创建文件,说明处理方向是对的。若数量降不下来,回到第二节,检查是否找错了挂载点,或者文件已被删除、inode 仍被进程占着。

四、已删除但仍被进程占用的 inode

文件被删除后,目录项会消失,du 和普通 find 都看不见它;只要还有进程打开着这个文件,对应的 inode 和数据块就不会释放。这在“磁盘空间对不上”时更常见,但打开的文件足够多时,同样会推高 df -i。

# 列出已删除但仍被打开的文件
sudo lsof +L1

若没有 lsof,可以按进程检查:

sudo bash -c 'for fd in /proc/[0-9]*/fd/*; do
  link=$(readlink "$fd" 2>/dev/null) || continue
  case "$link" in
    *"(deleted)") echo "$fd -> $link" ;;
  esac
done'

处理方式是重启或结束那个仍占着文件的进程,让内核回收 inode。不要去删 /proc 里的描述符,也不要在未确认的情况下对日志文件使用 truncate,以免搞乱正在写入的服务。释放后应看到 df -i 和 df -h 同时变化。

五、预防:监控 inode,并避免在系统盘堆积小文件

空间告警覆盖不到这类故障。华为云监控等产品默认的磁盘指标通常看的是容量使用率,根分区还有 30% 空间时,inode 也可能已经用尽。建议在 ECS 上单独检查 IUse%,例如每天记录一次,或在超过 80% 时告警:

#!/bin/bash
# 根据实际挂载点修改;阈值可按业务调整
threshold=80
df -P -i | awk -v th="$threshold" 'NR>1 && $1 !~ /^(tmpfs|devtmpfs|overlay)$/ {
  use=$5; gsub("%","",use)
  if (use+0 >= th) printf "inode high: %s %s IUse=%s\n", $1, $6, $5
}'

可以把它放到 cron 里,输出转到现有的监控或邮件通道。检查多个挂载点时,系统盘和数据盘都不要漏。

从源头上减少海量小文件,比事后清理更稳妥:

  • 让 PHP 或其他会话机制的过期回收真正跑起来,不要只靠业务流量偶然触发 GC;
  • 应用缓存设置目录上限和过期时间,避免按请求无限落地小文件;
  • 临时文件写到 /tmp 或专门的缓存目录,并靠 systemd-tmpfiles 或 cron 定期清理;
  • 大量小对象如果只是存储、不需要在本地 POSIX 语义下打开,可以放到对象存储,而不是堆在系统盘。

如果业务本身就必须在本地存放大量小文件,不要指望只靠给云硬盘扩容来彻底解决问题。扩容并扩展文件系统时,ext4 在新增块组时通常会带上一部分新的 inode,IUse% 有可能下降,但这既不能替代清理,也不能改变原有块组里已经定死的 inode 密度。ext4 的 inode 数量在格式化时就按“每多少字节对应一个 inode”算好了,已有文件系统无法在线改这个比例。新建数据盘时可以按文件大小预先规划,例如:

# 先确认设备名和是否为新盘,确认盘上没有需要保留的数据
# 下面只是格式化示例,设备名必须换成实际值
sudo mkfs.ext4 -i 4096 /dev/vdX

-i 4096 表示大约每 4 KiB 分配一个 inode,比常见默认值 16384 更适合大量小文件,代价是 inode 表本身会多占一些空间。如果文件普遍很大,反而应该用更大的 -i,避免浪费。格式化前必须确认设备名和盘内数据,做过格式化就无法直接恢复。

XFS 一般不需要靠调 inode 密度来应对“小文件很多”的场景,但仍然要保证有足够的空闲空间,并避免在单目录下堆积千万级条目——目录项过大同样会影响创建和查找性能。

六、几点注意事项

  • 全程带上 -xdev 或 du -x,不要从根目录一路走进数据盘、容器存储或远程挂载。
  • 先统计、后删除。会话、缓存可以按过期时间清;源代码、上传目录、数据库目录不能按“文件多”来删。
  • 不要手动改动容器存储驱动的底层目录,也不要在 /proc、/sys 里做清理。
  • 清理后不仅看 df -i,还要确认业务进程能够重新创建文件,并检查定时清理任务是否还在生效,否则过几天会再次耗尽。
  • 已有 ext4 分区无法通过简单改配置来增加 inode 密度。需要更多 inode 时,优先清理和把小文件迁到单独规划过的数据盘,而不是反复把系统盘扩得很大。

按这个顺序处理,可以把“空间还在、文件却创建失败”的问题从误判里分开,并在下次发生前先看到 IUse% 的爬升。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。