干 Linux 运维这些年,我逐渐发现一件反直觉的事:真正让人凌晨爬起来处理的故障,往往不是 CPU 打满、内存耗尽这种“看面板就知道严重”的问题,而是监控面板上一片绿色时,业务已经悄悄开始报错。聊到 Linux 监控,大多数人的第一反应都是 CPU、内存、磁盘“老三样”,可线上环境最容易埋雷的恰恰是那些默认模板不会重点盯着的地方。比如某个分区明明还剩几十 GB,程序却告诉你 no space left on device;又比如关键服务进程还活着,端口也还监听,但它背后的线程早就卡在一处网络文件系统上动不了。
这类问题我不会再用“隐藏太深”来解释,因为它们本质上就是你没监控到对应的指标。这篇文章我想写一份 Linux 暗坑监控梳理,前半部分带你认识那些容易被忽略的系统层、进程层和依赖项,后半部分给一套可以直接放到服务器上跑的巡检逻辑,顺便讲清楚怎么接进 Prometheus 或 Zabbix。内容偏运维实战,适合已经在跑业务的 Linux 使用者、开发自运维的小团队,以及刚从“看面板”走向“管可用性”的工程师。
1. 别只盯 CPU 内存磁盘,Linux 暗坑往往藏在默认指标之外
1.1 监控面板显示“一切正常”,系统为什么会挂
一旦你开始负责真正的服务器,就会发现一个很尴尬的事实:监控模板上那条 CPU 使用率曲线再漂亮,也解答不了“业务为什么不可用”。因为 CPU、内存、磁盘空间这些指标回答的是资源够不够,而不是操作能不能完成。
举个例子。程序在写临时文件时突然报磁盘满,你跑 df -h 一看,磁盘明明还剩 20 GB;再看看 df -i,分区 inode 使用率已经 100%。每个文件都需要一个 inode 来存放元数据,inode 耗尽后,即使磁盘还有容量,内核也无法再创建任何新文件。这种故障出现时,你的 CPU 曲线正常,内存正常,磁盘空间也正常,唯一在报警的是业务的日志文件。而默认监控面板,根本不会因为你没监控 inode 而主动告诉你。
我见过不少团队,Zabbix 装好了,Prometheus 也接了 node_exporter,但告警规则只套用了默认模板。这不是工具的问题,而是监控思维没转换过来:你想发现“暗坑”,就必须从故障的表现倒推需要什么指标,而不是监控系统给你什么模板就用什么模板。
1.2 默认监控抓不到的三类暗坑
结合我自己踩过的坑,Linux 上让人难受的故障基本可以分成三类:
| 层级 | 默认监控通常覆盖 | 真正的“暗坑” |
|---|---|---|
| 系统资源 | CPU、内存、磁盘空间 | inode 耗尽、文件句柄耗尽、TCP 重传、D 状态进程、日志分区写满 |
| 服务运行 | 进程是否存在、端口是否监听 | systemd 单元崩溃、僵尸进程累积、依赖 NFS 等外部存储卡死 |
| 内核与硬件 | 通常不覆盖 | OOM Killer 事件、内核日志异常、磁盘健康状态、系统时间漂移 |
第一类是“量变导致质变”的问题,平时不起眼,到临界点直接爆发。第二类是“活着的服务不一定健康”的问题,进程还在,但线程全部堵死。第三类最容易背锅,内核日志和硬件异常往往被当成应用问题排查,耗时又无效。
这类问题有个共同特征:不是 Linux 不给你信息,而是信息藏在默认仪表盘以外。系统其实通过 /proc、/sys、journalctl 暴露了大量线索,但没有人去读,自然也就无从告警。
1.3 判断该监控什么的思考方法
我自己的习惯是,对每一台服务器先问四个问题:还能不能创建文件?还能不能建立连接?关键进程还活着并且能干活吗?系统底层有没有已经发生的异常事件?这四个问题分别对应 inode、文件句柄、进程状态和内核日志。任何一个问题的答案是“不能判断”,说明监控有缺口,应该尽早补上。
设计监控指标时,也建议少看“平均值”,多看“水位”和“事件”。比如磁盘使用率平均 50% 不代表安全,如果某个分区已经 98%,一次日志写入就能把业务干趴。把关键指标的阈值设置清楚,比在仪表盘上堆砌几百张曲线有用得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统层盲区:inode、文件句柄、日志分区和时间漂移
2.1 inode 耗尽:磁盘还有几十 GB,程序却报磁盘满
inode 是 Linux 文件系统里每个文件或目录对应的元数据结构,记录着文件权限、属主、大小、数据块位置等信息。创建文件时,文件系统不仅要分配数据块,还要分配一个 inode。如果 inode 被用完了,后续的 write、create、rename 都可能直接返回 ENOSPC,错误提示和磁盘空间不足一模一样,非常迷惑。
为什么会出现 inode 耗尽?最常见的是大量小文件堆积。比如程序每天生成几万个缓存小文件、邮件队列积压、临时文件没有清理,都会迅速吃掉 inode。另一个容易被忽略的场景是 Docker 容器或日志服务产生海量小日志文件,宿主机的 overlay 文件系统需要额外的 inode 来维护层信息,一旦底层的 inode 耗尽,容器可能创建文件失败甚至无法正常重启。
排查命令很简单:
bash复制df -i
df -iP /var
如果你发现某个挂载点的 IUse% 达到 90%,就要开始警惕。inode 不像磁盘空间那样容易扩容,虽然你可以在线增加一些文件系统的 inode 数量,但很多场景下只能靠清理文件来释放。最佳做法是提前监控,建议超过 80% 进入观察,超过 90% 必须告警,95% 以上随时有业务故障风险。
处理时先找到小文件最多的目录,可以用类似命令定位:
bash复制for d in /var /tmp /home; do
echo "$d:" $(find "$d" -xdev -type f 2>/dev/null | wc -l)
done
然后根据业务情况清理过期文件,或把容易产生海量小文件的应用换到按需清理的存储目录。更重要的是,把“创建文件是否成功”纳入巡检,而不仅仅是监控 inode 使用率。
2.2 文件描述符耗尽:进程活着,新连接却进不来了
文件描述符(fd)是进程访问文件、套接字、管道等资源的句柄。Linux 对单进程能打开的文件描述符数量有上限,系统整体也有一个总上限。普通开发环境里 file-max 足够大,大家几乎感觉不到它的存在;但高并发服务很容易撞上这堵墙。
系统级上限可以通过这三个数字看出来:
bash复制cat /proc/sys/fs/file-nr
输出通常是三列:第一列是当前已分配的文件句柄数,第二列是其中空闲的句柄数,第三列是系统最大句柄数。真正要关心的是第一列和第三列的比值。当这个比例超过 80% 时,说明系统整体句柄使用已经偏高,需要排查是否有进程在疯狂打开文件而没有释放。
进程级的上限更常见。比如 Nginx 或 Java 应用突然报 too many open files,大多数情况是单个进程达到了 ulimit -n 的限制。你可以通过进程 ID 查看实际打开的句柄数量:
bash复制ls /proc/<pid>/fd | wc -l
cat /proc/<pid>/limits | grep "open files"
这里的坑在于:系统整体句柄没满,常规监控根本不会报错,但业务进程因为自身 fd 用尽,表现可能是连接被拒、文件读写失败、日志疯狂刷错误。它不会直接导致机器 load average 飙升,只会在业务指标上形成“服务半死不活”的状态。
要预防这类问题,一方面要把 ulimit -n 调成符合业务预期的值,另一方面需要监控两类数据:系统 `/
