磁盘不足告警排查实战:从监控阈值到根因复盘
1. 告警是怎么来的先弄懂监控阈值这套逻辑1.1 磁盘不足阈值告警常见判定方式监控系统判定磁盘不足远不止“用了多少百分百”这么简单。拿这次“某网点触发磁盘不足阈值告警”的情况来说告警平台读到的是两个维度的指标一个是文件系统容量使用率一个是文件系统 inode 使用率。容量使用率决定你能存多少数据inode 使用率决定你还能建多少个文件两者任何一个超过阈值都会触发告警也都有可能被误判成“磁盘不足”。具体到告警规则我在监控这边用的是一套“持续 N 分钟超过阈值才报警”的配置。为什么不能看到 90% 就秒报因为磁盘使用率是个缓变指标业务高峰期可能短暂冲到 88%但马上又回落秒报只会把值班同事的神经练废。这次网点告警的阈值是 85%持续 5 分钟才触发已经算比较保守了。1.2 为什么这次告警发生在“这个时间点”收到告警的第一时间要会看时间戳。告警平台上面写着 14:37 触发同时我也看了同一网点的磁盘监控曲线曲线显示 13:00 之后使用率从 62% 开始一路往上爬期间几乎没有任何回落。这种陡坡曲线和平时那种“慢悠悠涨一个月”的情况完全不同。斜率异常基本能推断是有程序在短时间往磁盘里塞了大量数据而不是正常业务增长的规律。有了这个判断排查方向就从“磁盘容量满了怎么办”直接变成“什么东西在短时间内写爆了磁盘”后面的大多数操作都是围绕这个核心问题展开的。2. 排查前别急着上手先把场景还原清楚2.1 容量型、inode型、只读型三种告警先分类别看都叫磁盘不足告警触发原因和处置方法是完全不同的。容量型最容易理解就是可用字节数不够了inode 型是文件数量爆了哪怕容量还剩几个 G也建不了新文件只读型则是文件系统因为错误被内核强制只读这是最危险的一种处理不当会丢数据。我这次收到告警后第一步不是着急登服务器而是先看监控里到底是哪项指标超了。当时告警详情显示 capacity 使用率 92%inode 使用率只有 23%所以确认是容量型问题inode 这块暂时不用管后面处理压力就小了很多。2.2 排查前先列一份信息清单别裸奔远程排查一个出问题的网点主机最忌讳的是连上就乱敲命令。可以先在心里过一遍信息机器 IP、操作系统版本、文件系统挂在哪个分区、业务是什么、最近有没有上线或改配置、这台机器之前有没有其他告警。值得留意的是磁盘告警经常和业务告警同时出现“任务执行失败企微进行告警”这类消息如果密集弹出很可能就是磁盘写满导致的连带效应要一起看。另外还要确认自己手里的登录账号有没有 sudo 或 root 权限。因为后面定位大文件、删日志、清理残留句柄几乎每步都需要高权限。如果权限不够就先申请权限不要浪费时间在只读检查上。2.3 查看系统基础信息和挂载结构登录服务器后我习惯先跑一组基础命令确认现状而不是直接 du。下面这组命令可以快速回答“磁盘挂载在哪里、一共多大、已经用了多少、还剩多少”df -h df -i lsblk mount | grep ^/devdf -h 看容量df -i 看 inodelsblk 看整体盘符和分区关系mount 确认挂载选项里有没有 noatime、是否 rw。这里有一个常被忽略的点df 输出里的“Use%”是已用块占总体块的比例不包括系统预留的 blocks所以有时候看起来 100%其实 root 用户还能往里写一点但普通业务进程已经写不进去了。3. 现场排查实录命令与判断逻辑3.1 df 和 du 的配合顺序很重要拿到现场的反馈信息后我看到了这样一份 df 输出示意Filesystem Size Used Avail Use% Mounted on /dev/vda1 100G 92G 2.0G 98% /var tmpfs 3.8G 0 3.8G 0% /dev/shm注意是在/var分区上爆掉的不是根分区。很多运维习惯先看根目录但服务数据、日志通常在 /var 上所以排查范围要跟着挂载点走否则容易找错方向。接下来用 du 找大目录。我采用的命令是顺着/var逐层往下看du -x -h --max-depth1 /var | sort -hr | head -20-x 表示不跨文件系统避免把其他挂载点的数据也算进来。--max-depth1 是只看一层排序后就能看出 /var 下面哪个目录最肥。当时输出里/var/log排在第一位占了 55G马上就有了重点嫌疑。3.2 锁定日志目录里的具体大文件明确了/var/log之后我不打算再对整个目录一层层 du 下去那样太慢。可以先列出最近修改过、且体积特别大的文件效率高很多ls -lhS /var/log/-lhS 会按文件大小倒序排列一眼就能看到异常文件。现场果然有几个单文件超过 5G 的日志一个叫 app-error.log一个叫 task-retry.log。更可疑的是这两个文件还在以肉眼可见的速度变大用watch -d ls -lh /var/log/app-error.log看了一眼大约每 5 秒就能涨几百 KB说明故障正在持续发生。3.3 用 lsof 找出被删除但还没释放的文件还有一种非常容易踩的坑磁盘空间被占满了但 du 找不到大文件。这是因为进程打开了一个文件运维已经把文件删除但进程仍然持有文件句柄空间并不会释放。这种情况在运行中的 Java 服务、消息队列、频繁写日志的进程上很常见。排查命令是lsof L1 | grep -i deleted | sort -k7 -rn | headL1 表示显示 link count 小于 1 的打开文件也就是被删除但仍被占用的文件。当时查出来有一个旧的业务日志被 log rotate 切走但因为进程没有 reopen旧的句柄一直占了几 G 空间。这个不解决就算你删了历史日志空间也回不来。3.4 结合进程线索找到真正的源头通过ls -l /proc/[pid]/fd/也可以反查哪个进程持有某个日志文件的句柄或者用fuser /var/log/app-error.log找到正在写这个文件的进程。顺着 pid 查到进程名是网点主机上一个 Python 写的定时任务脚本。写日志的行为本身没问题问题在于它处于一个失败重试的循环里任务调用远程接口失败异常堆栈写入 app-error.log然后脚本立刻重试又失败又写一条。每次堆栈信息可能就几十 KB但如果重试间隔只有 1 秒一小时就能写满几个 G。这也是为什么会看到“任务执行失败企微进行告警”以很高的频率弹出来因为在每轮重试失败后脚本都会调一次企微机器人发送告警企微接口本身也在消耗磁盘空间和网络资源形成恶性循环。4. 根因复盘为什么业务日志能把磁盘打满4.1 失败重试和告警循环是主要推手单看一个 app-error.log 写入速度你可能觉得它不算快。但问题在于这个失败重试逻辑没有退避机制也没有最大重试次数。脚本挂了 40 分钟后自动重启又继续下一次循环。我粗略算了一下假设一次异常堆栈 60KB每秒重试一次一小时就是 216MB半天就能吃掉好几个 G。再加上同事在修问题的过程中手动触发过几次任务日志量就更大。更讽刺的是告警系统本身的“成功恢复”消息也在写日志。任务失败会触发企微告警企微机器人发送成功后会记录响应日志这个日志也会写入磁盘。当磁盘接近满的时候所有写入都变慢服务超时又引发新的告警。整个过程就像滚雪球这也是为什么我们要强调“告警降噪”既要有通知又不能被通知淹没。4.2 日志文件没有切分和清理策略复盘时发现这台网点的系统里根本没有任何日志轮转配置。/etc/logrotate.d/下面只有系统自带的 syslog 配置业务日志完全没有纳入管理。这意味着如果没有人手工清理log 文件就会无限增长直到把磁盘打满。很多人觉得 logrotate 很基础但现实是很多网点的小成本项目容易忽略这一点。正确做法是给业务日志配置按天或按大小切分比如/var/log/app-error.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }copytruncate 是重点它先复制日志再清空原文件适合没有能力 reopen 日志句柄的进程。如果是 Java 服务更推荐 create copytruncate配合 logback 自身配置来避免句柄问题。4.3 普通容量告警背后藏着业务风险的信号磁盘满了只是一个现象真正要问的是业务侧为什么反复失败。这次远程接口失败的原因是上游系统接口出现“422 通信故障”这是业务侧的问题不是磁盘问题。但如果只把磁盘清理完就收工下次上游一抖动日志又会再次把磁盘打满。所以我在处理完磁盘之后还做了一件事要求业务开发同事给任务重试加上指数退避逻辑并且把失败消息改为聚合发送而不是每次失败都往企微群里丢一条。可以设定一个告警窗口5 分钟内同一任务最多通知一次避免成百上千条重复消息刷屏。这一步做完后续才真的安静下来。5. 快速处置与告警降噪5.1 快速清理的步骤先看内容再截断最后删当磁盘快满时现场最急需的操作是“止血”但千万不要见到大日志就rm -f这个动作除了解放空间什么信息都留不下来。我的顺序是先复制出磁盘告警时间段的最后 100 行日志留作风控和复盘证据。用truncate -s 0 /var/log/app-error.log将正在写的日志清空而不是删除文件。这样进程写入不中断空间立即释放。确认日志轮转配置存在后再删除更早的 .gz 压缩历史文件。最后执行df -h确认使用率降到了 85% 以下。有个注意点truncate 对正在被进程写入的文件是安全的但如果是需要保留现场的问题可以先cp或直接mv到别的目录再创建同名空文件。总之一条原则先备份现场再清理。5.2 告警恢复与人工确认的节奏磁盘使用率降下来之后监控并不会立刻恢复因为告警规则里有“持续 N 分钟低于阈值才恢复”的机制。这里要提一句一定要接恢复通知不然值班的人不知道问题已经缓解很可能跑到现场又做一轮无谓检查。我当时是在监控里把恢复通知也打开了并且设了一个 30 分钟的恢复观察窗口。因为故障刚处理完磁盘使用率会有一个回落再上升的过程过早确认恢复可能会掩盖残余的异常写入。观察窗口结束确认曲线平稳了业务日志也不再在告警群里刷屏才真正关闭这次告警事件。5.3 阈值设置与分组策略的经验值这次事件暴露了一个问题单一固定阈值 85% 在某些分区上过于宽松在某些分区上又过于灵敏。比较好的做法是分级设置指标普通级别警告级别严重级别磁盘容量使用率70%持续20分钟80%持续10分钟90%持续5分钟inode 使用率75%85%92%目录增长速率每10分钟增2GB持续2次每5分钟增4GB持续2次每5分钟增8GB持续1次同时告警通知也要做分组聚合。同样一条/var/log磁盘告警可以按“网点名称 分区名”分组一天最多告警 5 次避免同一故障反复刷新告警列表。夜莺或者 Prometheus Alertmanager 这类工具都支持这种规则关键是主动配置别用默认值。6. 防止复发监控、巡检、预案三件套6.1 监控层面加业务目录级监控比只加分区级监控更有用这次事后我做的第一件事是在监控平台上新增了/var/log目录的“磁盘累计增长速率”指标。怎么理解分区级监控只能告诉你“满了”目录级监控才能告诉你“以每小时 20G 的速度增长中”。后者对提前干预的价值高得多。实现上可以基于 node_exporter 的 textfile collector 或者自定义采集脚本每 1 分钟统计一次指定目录大小通过 PromQL 里的delta()函数算出增长速率。举例# 伪代码每60秒统计/var/log总大小推送到监控 while True: size sum(os.path.getsize(os.path.join(dp, f)) for dp, _, files in os.walk(/var/log) for f in files) send_metric(dir_size_bytes, {dir: /var/log}, size) time.sleep(60)注意用os.walk全量统计大目录的 CPU 消耗不低生产环境建议用du -s或者基于文件系统的 journal 增量统计。这次只是机器数量不多简单方案也够用。6.2 巡检任务把人的经验固化成脚本监控能兜底但日常巡检仍然不能省。我写了一个小巡检脚本每周五下午跑一次检查所有网点主机的以下内容磁盘使用率超过 75% 的分区排名过去 7 天内没有轮转的日志文件占用空间最大的 10 个文件以及是否存在大量 deleted 状态的打开文件句柄。脚本里加了个“预估可用天数”的逻辑用最近 7 天增长速率估算磁盘还能撑多久。如果预估可用时间少于 10 天就会提前触发提醒。这一步很值得做因为磁盘告警最怕的不是突发写满而是“温水煮青蛙”。6.3 应急预案不用太复杂但要能照着执行我把这次事件的处置过程整理成了一页纸预案内容包括告警出现后先看 df 还是先查 du、清理前要备份哪些内容、哪些文件可以直接 truncate、哪些文件必须先确认业务影响。预案不追求很长关键是有明确的负责人和操作顺序。比如“网点主机磁盘使用率超过 90%”时第一联系人是这个网点的驻场运维第二联系人是基础设施值班。避免出现告警弹出来半小时没人接手的情况因为每多等 10 分钟磁盘就会多写入几百 MB 日志。7. 常见问题速查与避坑经验7.1 df 和 du 统计不一致怎么办这是一个高频问题。df 显示磁盘快满du 统计了所有文件却感觉“没占这么多”。原因通常是文件被删除但进程仍持有句柄du 统计不到有隐藏挂载点du 默认不越过挂载边界文件系统元数据占用或预留空间。处理顺序先用mount和df -h对照确认分区再lsof L1 | grep deleted找句柄最后才考虑是否是 quota 或文件系统层面的问题。7.2 inode 满但容量剩余很多怎么办这种情况常见于消息队列目录、缓存小文件目录、邮件队列等。处置思路是清理历史小文件但要注意不能乱删像/tmp下的 session 文件和/var/spool/mqueue里的邮件都是有业务语义的。我的检查命令df -i for i in /var/*; do find $i -xdev -type f | wc -l; done哪个目录文件数特别多再进一步看里面是什么是否可以根据文件名或时间批量压缩。压缩成 tar.gz 会释放 inode但占用的容量变化不大需要提前评估磁盘空间是否足够。7.3 处理磁盘告警时最容易被忽略的“恢复通知”有一次我只清了磁盘没确认恢复通知结果第二天业务人员说“昨晚那条磁盘告警现在还在群里”。其实就是因为没有恢复通知也没有自动关闭告警一直处于“触发中”状态。监控系统里的告警不是清理完就结束的状态流转需要恢复条件满足或者人工确认这个流程要在处置时一并完成。这一块也被我纳入“告警降噪”的范畴高频故障通常需要同时配置触发、恢复、确认三个方面否则会产生大量无效消息把真正重要的告警淹没。8. 这次告警让我记住的几个经验8.1 告警只是信号不是答案踩过几次坑之后我越来越觉得告警消息里写“磁盘不足阈值告警”它只是告诉你“磁盘空间这个指标越界了”并没有告诉你“为什么越界”。真正有价值的是把这个信号放到整个链路里去理解时间点、曲线斜率、关联告警、最近变更四项合在一起才构成一个可操作的答案。8.2 系统排查指令要形成肌肉记忆这次排查用到的基础命令其实还是 Linux 系统排查指令大全里的那些df、du、lsof、lsblk、find、ps、fuser。难的不是命令本身而是知道在什么场景下按什么顺序使用。我建议每个运维朋友都建一个自己的速查笔记把每次处理过的告警场景和对应命令记录下来遇到类似问题的速度会快很多。8.3 处置完不等于结束还要闭环最后想分享一个习惯我会在每次磁盘告警处理完的第二天主动看一眼监控曲线是否恢复到正常斜率。如果有回升迹象就说明原始问题没有真正解决需要继续跟进。处理告警不是跑完一顿操作就可以收工把它当成一次根因分析的开端长期下来会省掉很多返工时间。这次网点的事件后来没有再复发靠的也就是这些“不起眼”的小闭环。