☰
磁盘不足告警排查实战:从监控阈值到根因复盘
2026/9/30 3:06:10 网站建设 项目流程

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 '^/dev'

df -h 看容量,df -i 看 inode,lsblk 看整体盘符和分区关系,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-depth=1 /var | sort -hr | head -20

-x 表示不跨文件系统,避免把其他挂载点的数据也算进来。--max-depth=1 是只看一层,排序后就能看出 /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 | head

+L1 表示显示 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,这个动作除了解放空间,什么信息都留不下来。我的顺序是:

  1. 先复制出磁盘告警时间段的最后 100 行日志,留作风控和复盘证据。
  2. 用truncate -s 0 /var/log/app-error.log将正在写的日志清空,而不是删除文件。这样进程写入不中断,空间立即释放。
  3. 确认日志轮转配置存在后,再删除更早的 .gz 压缩历史文件。
  4. 最后执行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 处置完不等于结束,还要闭环

最后想分享一个习惯:我会在每次磁盘告警处理完的第二天,主动看一眼监控曲线是否恢复到正常斜率。如果有回升迹象,就说明原始问题没有真正解决,需要继续跟进。处理告警不是跑完一顿操作就可以收工,把它当成一次根因分析的开端,长期下来会省掉很多返工时间。这次网点的事件后来没有再复发,靠的也就是这些“不起眼”的小闭环。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询