当前位置:首页>Linux>Linux 文件系统 inode 耗尽排查

Linux 文件系统 inode 耗尽排查

  • 2026-10-11 07:16:34
Linux 文件系统 inode 耗尽排查
SRE实战 · 生产故障复盘

Linux 文件系统 inode 耗尽排查

当磁盘空间充足却提示 No space left on device,SRE 如何快速破局?

01

场景还原:大促压测期的诡异 ENOSPC

2026年3月14日 09:15,某头部零售平台在「2026年Q1全链路压测」期间,核心交易链路节点突然爆发大规模写入失败。监控大盘显示,应用侧 P99 延迟从 45ms 飙升至 12s+,业务日志疯狂打印 java.io.IOException: No space left on device。值班 SRE 第一时间介入,常规检查却发现一个反直觉现象:数据盘 /data 使用率仅为 38%,剩余可用空间高达 1.2TB。

⚠️ 故障定级:P1 核心交易中断

磁盘明明有海量剩余空间,内核却拒绝分配存储资源。这是典型的「假满盘」现象,底层根因直指文件系统元数据瓶颈——inode 耗尽。

在 Linux VFS 架构中,文件创建需要同时满足两个条件:Block(数据块)充足,且 Inode(索引节点)充足。Block 存储实际内容,Inode 存储文件元数据(权限、属主、时间戳、Block指针)。当海量小文件持续生成,即使 Block 远未打满,Inode 池也会率先枯竭,导致内核返回 -ENOSPC。

02

排查过程:从表象到精准的定位链路

面对 inode 耗尽,常规 du -sh 毫无意义。SRE 团队迅速切换至元数据视角,执行标准化排查流:

Step 1:确认 inode 水位

terminal
$ df -ihFilesystem      Inodes  IUsed   IFree IUse% Mounted on/dev/vdb1        500M    500M       0  100% /data$ dmesg | tail -n 5[2026-03-14T09:15:33.882Z] EXT4-fs warning (device vdb1): ext4_create: inode limit reached[2026-03-14T09:15:33.885Z] EXT4-fs error (device vdb1): ext4_journal_check_start: inode table full, no space left

输出明确显示 IUse% 达到 100%,内核日志直接打印 inode limit reached。分区格式化时默认分配了 5亿 个 inode,当前已全部耗尽。

Step 2:定位 inode 聚集目录

面对数千万级小文件,find /data -type f | wc -l 会导致 IO 打满、机器假死。SRE 采用分治法,逐层统计目录 inode 数量:

terminal
$ for dir in /data/*/; do echo -n "$dir: "; find "$dir" -maxdepth 1 -xdev -type f 2>/dev/null | wc -l; done | sort -t: -k2 -nr | head -10/data/app_logs/: 482000000/data/cache/tmp/: 1240000/data/exports/csv/: 850000

结果一目了然:/data/app_logs/ 独占 4.82 亿个 inode。进一步查看该目录下文件特征:

terminal
$ ls -lhS /data/app_logs/ | head -5total 1.2G-rw-r--r-- 1 appuser appuser 1.2K 2026-03-14 09:10 trace_req_a8f3c.log-rw-r--r-- 1 appuser appuser 0.8K 2026-03-14 09:10 trace_req_b12d9.log-rw-r--r-- 1 appuser appuser 1.5K 2026-03-14 09:10 trace_req_c77e1.log

文件体积极小(0.8K~1.5K),但数量级呈指数增长。结合发布记录,确认是 2026年3月10日 上线的「分布式链路追踪 Agent v2.4.0」引入了异步落盘降级逻辑。

03

根因分析:小文件风暴与内核调度冲突

通过 strace -p <PID> -e trace=openat,write,close 抓取进程系统调用,结合研发代码 Review,完整还原故障链条:

1. 降级策略缺陷

新 Agent 默认将 Span 数据批量发送至远端 Collector。当压测导致网络拥塞(TCP 队列积压)时,Agent 触发 fallback_to_disk 逻辑。但实现存在严重 Bug:每条 Trace 独立生成一个 .log 文件,而非追加写入同一 Buffer 文件。

2. 清理机制失效

原设计依赖后台 cleanup_job 在 Collector 恢复后删除本地缓存文件。但由于压测期间 CPU 负载长期 >90%,GC 停顿频繁,定时任务线程被阻塞,清理队列积压超 12 小时,形成单向写入的 inode 漏斗。

3. Ext4 元数据瓶颈

Ext4 文件系统在分配 inode 时需锁定 inode table bitmap。当并发创建请求 > 5000/s 时,ext4_alloc_inode 引发严重自旋锁竞争,dmesg 中出现大量 soft lockup 告警,进一步拖慢系统调用返回速度。

对比维度
故障前 (2026-03-13)
故障中 (2026-03-14)
内核表现
IUse 占比
12.4%
100%
ext4_create 直接返回 -ENOSPC
单目录文件数
~4.2万
4.82亿
dir_index 哈希冲突,lookup 延迟飙升
平均文件大小
4.2MB
1.1KB
Block 碎片化率 0%,Inode 碎片化 100%
内核锁竞争
<0.1%
34.8% (inode_bitmap_lock)
CPU iowait 虚高,实际卡在元数据分配
04

修复方案:止血与根治并行

🛠️ 紧急止血(MTTR 控制在 15 分钟内)

1. 停止写入源:通过配置中心下发 agent.fallback.enabled=false,阻断新文件生成。2. 批量清理:使用 xargs 分片删除,避免 rm -rf 单进程打满 CPU。3. 释放句柄:对已删除但仍被进程 hold 的文件,重启相关 Java 进程释放 inode。

📝 分步执行命令参考

Step 1:安全删除(按 10万/批)

find /data/app_logs/ -type f -name "*.log" -mtime +0 -print0 | xargs -0 -n 100000 rm -f

Step 2:验证 inode 回收(需延迟 10-30s 生效)

watch -n 2 "df -ih /data | grep -v Filesystem"

Step 3:重建目录结构(防再次打满)

rm -rf /data/app_logs && mkdir -p /data/app_logs/$(date +%Y/%m/%d) && chown appuser:appuser /data/app_logs -R

🔮 长期架构优化

1. 存储引擎替换:将临时缓存目录挂载至 tmpfs 或使用 XFS 文件系统。XFS 采用动态 inode 分配机制,理论支持 2^64 个文件,彻底规避静态上限问题。2. 日志规范落地 强制要求所有 Sidecar 采用 Logrotate + 单文件追加模式(Append-only),禁止按 RequestID 切分独立文件。3. 配额限制:通过 ext4 quota 或 cgroup-v2 io.max 限制单目录 inode 创建速率,设置硬阈值拦截。

05

总结沉淀:防御体系与一键诊断

本次故障暴露出 SRE 日常巡检的盲区:监控大盘长期仅关注 df -h(Block 使用率),完全缺失 df -i 指标采集。2026年Q1 起,我们已将 Inode 水位纳入基础监控基线,并沉淀了以下标准化资产。

📦 SRE 必备:一键 inode 诊断脚本

该脚本支持快速扫描 Top N 目录、检测已删除未释放句柄、输出结构化报告。可直接保存为 /usr/local/bin/check_inode.sh 并在巡检 Cron 中定时执行(建议 2026-06-08 前完成全量主机部署)。

check_inode.sh
#!/usr/bin/env bash# 2026-03-20 SRE Team: Inode Exhaustion Diagnostic Toolset -euo pipefailTOP_N=${1:-5}TARGET_DIR=${2:-/}echo -e "\n[INFO] 当前系统时间: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"echo "=========================================="echo "🔍 1. 全局 Inode 使用概览"echo "=========================================="df -ih | grep -vE '^Filesystem|tmpfs|devtmpfs' | awk '{printf "%-20s %8s %8s %8s %5s %s\n", $6, $2, $3, $4, $5, $1}'echo ""echo "=========================================="echo "📊 2. 顶层目录文件数统计 (Top $TOP_N)"echo "=========================================="find "$TARGET_DIR" -maxdepth 1 -xdev -type d 2>/dev/null | while read dir; do    count=$(find "$dir" -xdev -type f 2>/dev/null | wc -l)    if [ "$count" -gt 0 ]; then        printf "%-40s %10d files\n" "$dir" "$count"    fidone | sort -k2 -nr | head -n $TOP_Necho ""echo "=========================================="echo "🗑️  3. 已删除但未释放的句柄检测"echo "=========================================="DEL_COUNT=$(lsof +L1 2>/dev/null | wc -l)if [ "$DEL_COUNT" -gt 0 ]; then    echo "⚠️ 发现 $DEL_COUNT 个已删除文件仍被进程占用!"    lsof +L1 2>/dev/null | awk '{printf "%-10s %-20s %-10s %-10s %s\n", $1, $2, $3, $4, $9}'else    echo "✅ 无泄漏句柄,inode 回收正常。"fiecho ""echo "=========================================="echo "💡 建议操作"echo "=========================================="echo "若 IUse > 80%,请执行:"echo "  1. find <dir> -type f -mtime +1 -delete"echo "  2. lsof +L1 | awk '{print $2}' | sort -u | xargs -r systemctl restart"echo "=========================================="

最新文章

随机文章