深夜告警炸裂?这份Linux故障排查“作战地图”请收好
凌晨两点半,手机连续震了六七下,监控大屏一片红。作为一个被Linux服务器告警支配过无数次的人,我太熟悉这种心跳加速的感觉了。CPU 100%、磁盘满、进程挂掉、端口失联……每一条告警背后都意味着一台机器正在拉响警报,而你要在最短时间内判断“这到底是不是事故”“影响面多大”“要不要立刻叫醒别人”。
这篇文章就是一份Linux故障排查“作战地图”。它不会教你背命令,而是告诉你:收到告警后第一件事做什么、第二件事做什么、什么情况该优先救火、什么情况可以先深挖根因。适合所有被Linux告警追着跑的运维、后端开发、SRE和刚入行的系统管理员。即使你现在还没遇到“炸裂”的深夜,也建议先收藏,等真出事的时候,你会感谢这份地图。
1. 告警响应的第一原则:先恢复,后排查
很多人一收到告警就冲进服务器,top、free、df一顿敲,恨不得马上找到“罪魁祸首”。这个方向没错,但顺序容易出问题。要知道,凌晨两点的告警,KPI永远只有一个:让业务先恢复正常,再考虑“为什么”的问题。
1.1 收到告警后,先回答这三个问题
在我眼里,任何告警都是一道三选一的判断题:
- 这是不是真的故障?有些告警是误报,比如监控项阈值设得过低、vSphere证书状态告警到期、zabbix采集器自身卡死,业务其实稳如泰山。这类告警的典型特点是:只有监控平台在喊,业务无感,登录服务器看各项指标都正常。
- 影响范围有多大?单机影响还是集群整体?一个节点挂了,有负载均衡兜底,你可以从容处理;如果是数据库主库告警,那就得立刻评估是否需要切换。
- 能不能快速止血?重启服务、重启机器、回滚版本、摘流量,哪一步最快能恢复业务?先做,再慢慢复盘。
这三点想清楚,你就知道自己处在“救火模式”还是“排查模式”。救火模式下的原则是:不做任何可能让情况恶化的操作。比如机器负载极高时不要贸然重启,除非你确认手里的重启操作能带来正面效果。
1.2 建立你自己的“告警速查手册”
我自己有一个习惯:每处理完一次故障,就把告警内容、登录后第一眼看到的指标、根因、处理动作、验证方式压缩成三五句话,记到本地笔记里。时间久了,这本手册就成了个人“作战地图”的核心。
举个例子,某次vSphere证书状态告警,我第一次遇到时还去查ESXi证书体系,折腾了半小时。后来手册里多了一行:证书告警不等于系统故障,登录vCenter确认过期时间和影响范围,通常可以顺延处理。再遇到同类告警,三分钟就能定位。这个习惯在深夜告警场景下特别值钱——凌晨的大脑本来就不如白天清醒,有一份现成的速查记录,能帮你少走很多弯路。
1.3 认清告警背后的“系统画像”
不同角色的服务器,故障优先级完全不同。数据库服务器的CPU飙高,可能引起雪崩;而一台离线计算节点的磁盘告警,大概率可以等到天亮再处理。所以排查之前,先问自己:这台机器在我架构里是什么角色?它挂了会影响哪些服务?有没有冗余?这个问题决定你后续每一步的节奏。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. CPU与负载异常:先从load average读起
负载类告警是所有Linux告警里最频繁的。我见过太多人一看“load average 20”就慌了,其实负载高不一定代表CPU忙,也可能是磁盘在拖后腿。
2.1 看懂uptime输出的三组数字
uptime 是最快的体检工具,它会告诉你系统当前时间、运行时长、登录用户数,以及最近1分钟、5分钟、15分钟的平均负载:
bash复制uptime
# 输出示例: 14:32:10 up 128 days, 3:42, 2 users, load average: 8.50, 4.30, 2.10
这里的load average是“可运行线程数+不可中断睡眠线程数”的滑动平均。1分钟8.5、5分钟4.3、15分钟2.1,说明负载正在快速上升,属于突发情况;反过来,如果1分钟2.1、15分钟8.5,说明高峰已经过去,系统正在恢复。
判断负载是否“过高”,不能只看绝对值,要看CPU核数。8核机器跑8.5的负载,其实还在临界边缘;2核机器跑8.5,那已经是严重超载。我习惯用 nproc 或 grep -c processor /proc/cpuinfo 先确认核数,再对比负载数值,否则很容易误判。
2.2 用top和mpstat区分“真忙”还是“假忙”
负载高之后,立刻敲 top,按大写P把进程按CPU使用率排序,找到最耗CPU的进程。但我建议你再多走一步,用 mpstat -P ALL 1 3 看每个CPU核的分布情况:
bash复制mpstat -P ALL 1 3
如果某个核跑满,其他核空转,可能是单线程应用瓶颈;如果所有核 uniformly 跑满,就是进程数量远超核心数,典型的“计算密集+线程爆炸”。这里有个容易忽略的点:top里的%CPU默认是按单核百分比算的,Java这类多线程进程显示到1000%以上都很正常,并不代表机器一定有问题。别被数字吓到,结合mpstat看整体。
再看load高但CPU并不忙的场景。top里如果wa(I/O wait)很高,说明进程在等磁盘,这时候CPU看似空闲,负载却下不来。继续用 iostat -x 1 3 看%util和await,如果%util接近100%、await明显偏高,根因就锁定在存储层了。
2.3 常见CPU故障的快速处理参考
| 现象 | 可能原因 | 快速动作 |
|---|---|---|
| 所有核跑满,进程列表混乱 | 定时任务集中爆发、脚本死循环 | pstree查进程树,kill异常进程,临时停cron |
| 单核跑满,负载平缓 | 单线程应用瓶颈 | 确认业务是否依赖单核性能,再考虑扩容 |
| load高、CPU低、wa高 | 磁盘I/O瓶颈 | iostat定位磁盘,排查慢查询或日志写入风暴 |
| 用户态CPU高 | 应用计算密集 | 抓线程栈(jstack/pstack)看热点 |
| 内核态CPU高 | 系统调用频繁/网络中断风暴 | 结合perf或sar -c排查异常软中断 |
一个真实案例:某次告警显示负载飙到30,但CPU用户态只有5%,wa也不高。排查下来是某个Java进程疯狂创建线程,导致内核态CPU高企。这类问题用top看%sy就能发现端倪,比盲目重启有效得多。
3. 内存与磁盘排查:OOM和空间耗尽的“双鬼拍门”
内存和磁盘问题,是除CPU之外最常见的深夜告警来源。它们的共同点是:早期没有明显症状,一旦恶化,系统行为立刻变得诡异——服务假死、进程被杀、日志写不进去。
3.1 用free和vmstat判断内存水位
free -h 是内存排查第一站,但很多人只看free列,觉得“内存还剩很多”。这是一个经典误区。Linux会尽量用空闲内存做页缓存(buff/cache),这部分内存在应用需要时可以释放。真正需要关注的是available列,这才是“真实可用内存”。
bash复制free -h
# 输出示例: total used free shared buff/cache available
# Mem: 30Gi 20Gi 1.2Gi 120Mi 8.6Gi 8.9Gi
如果available持续偏低,再看 vmstat 1 5 的si、so两列。si(swap in)和so(swap out)长时间不为0,说明系统正在疯狂换页,内存已经吃紧到影响性能的地步了。这种时候,单纯加内存是远期方案,眼前要做的是找出占内存的大户进程,评估是否重启释放。
3.2 OOM是结果,不是原因
登录服务器后发现进程不见了、日志里出现“Out of memory: Kill process”,多数人的第一反应是改oom_score_adj,或者把进程设置成不可被杀。但OOM只是内核的“极端避险行为”,真正的根因通常是两个:一是内存泄漏,二是内存配置不合理(比如Java堆设置超过物理内存)。
我处理过一个案例:某后端服务每隔几小时就会被OOM杀掉一次,看起来像经典的Java堆内存配置过大。但排查后发现问题出在JVM的元空间无限增长,代码里有动态生成类的逻辑。这时候重启应用只是暂时续命,不修复生成类的代码,迟早还会再挂。
排查OOM时,先看 /var/log/messages 或 journalctl -k | grep -i oom 确认被杀进程,再用 dmesg -T | tail -100 看内核日志里的内存信息。如果有cgroup限制,还要注意容器场景下,容器本身的内存上限可能是OOM的更直接原因。
3.3 磁盘排查:不只是df -h这么简单
磁盘告警算是最容易处理的,但也是最容易“处置不当”的。收到磁盘使用率告警后,第一反应通常是 df -h 看哪个分区满了,然后赶紧删文件。这个流程没错,但要提醒你一个坑:如果删了文件,df显示的使用率还不变,说明有进程还持有已删除文件的句柄。
bash复制df -h
# 发现 / 分区已经100%
du -sh /* 2>/dev/null | sort -hr | head -10
# 找到大目录,进一步定位大文件
lsof +L1 # 查找被删除但仍被进程占用的文件
lsof +L1的输出非常关键。它能列出“删了但没真正释放”的文件和对应进程。遇到这种情况,正确做法是重启对应进程(或者至少让进程重新打开文件),而不是继续盲目删其他文件。日志切割后空间不释放,大多就是这个原因。
排查磁盘时还有一个隐蔽角落:inode耗尽。df -i 看一下,如果Inodes列显示100%,即使df -h还有几百G剩余,文件也照样创建不了。这种场景常见于某个目录下有海量小文件,比如临时文件目录、邮件队列、php session目录。先 for i in /*; do echo $i; find $i | wc -l; done 找出小文件聚集地,再批量清理。
3.4 磁盘I/O的真假忙:理解iostat的关键指标
iostat -x 1 3 里有几个值要一起看:%util接近100%说明设备确实处于“饱和”状态,但真正影响业务的是await(平均I/O响应时间)。如果%util高但await低,说明磁盘在拼命干活,响应还不算慢,可能是正常的大批量读写;如果%util不算高但await很高,更可能是磁盘有坏块重试,或者有大量随机小IO在排队。
我遇到过一台机器,数据库落盘特别慢,iostat显示%util只有40%,但await高达300多毫秒。后来排查出是底层存储的raid卡策略问题,写缓存没生效。这种问题靠内核命令只能发现现象,根因还得靠硬件层面的信息来确认。
4. 网络与进程排查:端口不通和服务假死的识别
端口告警、连接超时、服务无响应,是线上运维的高频噩梦。这类问题的表象都在网络,根因可能藏在网络栈、防火墙、进程状态等不同层面,排查时最忌讳“想到哪查到哪”。
4.1 端口不通,先分层定位
接到“端口不通”的告警,我建议你按“从下往上”的顺序来:先确认主机层面的连通性(ping),再确认端口监听状态(ss),再确认防火墙规则(iptables/firewalld),最后回到应用本身。这个顺序能最快把问题范围缩小。
bash复制ping -c3 <目标IP>
ss -lntp | grep <端口号>
iptables -L -n | grep <端口号>
systemctl status <服务名>
如果ping通但ss里看不到端口监听,说明服务没起来或者正在启动中;如果ss能看到监听,但外部连不上,多半是防火墙拦了;如果防火墙没拦,应用也活着,那就得tcpdump抓包看握手是否正常。
一条tcpdump的常用姿势:
bash复制tcpdump -i any tcp port 8080 -nn -c 100
通过观察SYN包有没有SYN-ACK回复,能判断是内核协议栈在收包、还是应用层根本没回应。SYN收到了但没回应,说明应用进程可能已经假死,线程卡死导致accept队列满。
4.2 连接数过高,先分清“正常”还是“异常”
连接数告警有两种典型走向:一种是被恶意刷流量,一种真的是业务量上涨。用 ss -s 可以快速看当前连接数概况:
bash复制ss -s
如果TIME_WAIT数量异常庞大,通常是短连接请求过多,可以考虑开启net.ipv4.tcp_tw_reuse或者调整keepalive参数;如果ESTABLISHED持续走高,而业务量没有明显涨,要怀疑是连接泄漏——应用层拿着连接没释放。这时候用 ss -antp 配合进程号,看看是哪个进程占用了大量连接,再回到应用日志里确认原因。
4.3 进程活着但服务不工作,警惕“假死”
“进程还在,CPU也不高,但业务请求全超时”,这种情况最常见的原因是线程池耗尽或死锁。用 top -Hp <pid> 看线程状态,如果大量线程处于D(不可中断睡眠)或阻塞状态,配合 jstack(Java应用)或 gdb(C/C++应用)抓线程栈分析。
这里要特别提醒:遇到大量D状态线程时,不要急着kill进程。D状态往往意味着线程在内核里等待I/O,强杀可能导致数据不一致。更稳妥的做法是确认是不是磁盘或网络I/O卡死导致的,等I/O恢复后再观察线程是否自动解锁。有一次我着急重启了一个卡在NFS等待的进程,结果NFS挂载点和本地数据出现了不一致,后续数据修复花的时间比重启多得多。
4.4 进程管理三件套:systemctl、kill、systemd-analyze
排查进程问题,很多人习惯“日志不明确就直接重启”,但重启前最好先厘清服务当前状态:
bash复制systemctl status <服务名>
systemd-analyze blame # 看看开机启动时哪些服务拖慢了启动
systemctl status 不仅显示运行状态,还会列出最近几条日志,往往能直接点明启动失败或崩溃的原因。而 systemd-analyze blame 在排查“机器重启后服务迟迟起不来”时特别管用,能精确看到某个unit的启动耗时,帮你找到是哪个服务在拖后腿。
如果进程真的需要杀掉,建议顺序是:先 systemctl stop,不再需要了再 systemctl kill,实在不行才用 kill -9。原因在于,优雅停止能让进程做清理工作,比如释放锁、落盘缓冲数据、反注册服务;直接kill -9等于把房子门踹了,火灾是灭了,但里面的家具也全砸了。
5. 日志与告警联动:在“炸裂”的告警里找到关键线索
深夜的告警往往不是一条,而是一串。主告警下面跟着十几条连带告警,如果逐条去看,很容易陷进去。所以,日志排查是整张“作战地图”里最需要方法来支撑的部分。
5.1 日志文件是“现场”,journalctl是“时间轴”
传统的日志排查方式,是登录服务器进 /var/log/ 目录,用 tail -f 盯更新。这种方法在故障发生时依然有效,但我建议你同时学会journald的时间维度的排查方式:
bash复制journalctl -u <服务名> --since "10 minutes ago"
journalctl -k --since "10 minutes ago" # 看内核日志
以“10分钟前”为起点,把系统日志、内核日志、应用日志串起来看,能快速还原故障时间线。还有一个容易被忽略的位置:/var/log/syslog 或 journalctl -e 会记录系统级的异常,包括cron任务执行失败、磁盘报错、网络接口down等,这些往往和业务告警有因果联系。
5.2 从告警文本里提取“高价值信息”
告警内容本身是有结构的。以zabbix告警为例,常见的告警文本包含监控项名称、触发时间、当前值、阈值,这些信息要充分利用。
- 当前值远大于阈值,往往不是瞬时抖动,而是持续恶化的结果,查看这个指标过去15分钟的走势比盯着当前值更有意义。
- 告警触发时间,如果总是整点附近,八成是定时任务导致,比如每小时准点执行的全量备份、日志切割、报表汇总。
- 告警“一闪而过”或“反复抖动”,可能是指标确实在阈值边缘徘徊,先调整监控阈值或告警窗口,不要每次都当作事故处理。
我个人有个习惯:把每类告警的“触发模式”总结成一句话。比如“磁盘告警如果只在每月1日凌晨出现,可能是日志轮转的固定动作”。这些规律积累多了,深夜看告警的效率会高很多。
5.3 告警屏蔽手段,用对是工具,用错是隐患
前面提到的热搜词里有FlashDuty告警屏蔽不生效、zabbix手动确认关闭告警之类的问题,这属于监控平台侧的常见操作坑,但和Linux排查也有关联。
告警屏蔽的本质是“临时关闭某种条件下的通知”,它不改变系统状态。如果屏蔽了告警但系统问题没解决,等于蒙着眼睛开车。处理告警屏蔽问题时,先确认屏蔽条件是否精确匹配了当前告警的标签。比如你屏蔽了“主机A的CPU告警”,但实际触发的是“主机A的负载告警”,标签不匹配,屏蔽自然不生效。
更安全的做法:不要把屏蔽当成长期方案。真正解决根因后,清掉屏蔽规则,或者给屏蔽规则加一个明确的截止时间。任何跨天的“临时屏蔽”,最终都会变成隐患。这个经验来自我踩过的坑——一次临时屏蔽忘了撤,一个月后同一台机器磁盘写满,全家才知道。
5.4 用“时间线+关联性”构建自己的排查捷径
故障排查能力强的人,并不是比你会多敲几条命令,而是更擅长把零散信息串成线。每次告警进来,我会在草稿纸上(或者记事本里)列三行:
- 告警时间,以及这个时间点系统里发生了什么定时任务/变更?
- 告警对象,是单台还是多台?如果是多台,有没有共性?(比如同一机柜、同一交换机、同一应用集群)
- 最近一次变更记录,有没有人在这台机器上做过部署、配置修改、内核参数调整?
这第三个问题最容易被忽略,却往往是根因所在。Linux服务器的很多故障,源头都在“变化”而不是“常态”。
6. 给新手的10条Linux排查实战心法
这十条内容,算不上什么高深理论,但都是我在一线解决过真实问题后沉淀下来的操作习惯,希望能帮你少走弯路。
6.1 命令记不全没关系,会用man和--help就够了
很多人觉得Linux排查必须把上百条命令背下来,其实没必要。排查的核心是思路,命令只是工具。不确定参数时,man 或 命令 --help 永远是第一选择。
我常用的一种记忆方法:每个场景记一条“锚点命令”。比如CPU问题用top,磁盘问题用df,网络问题用ss,日志问题用journalctl。记不住完整参数没关系,搜一下就行。怕的不是不熟悉命令,而是不知道“这个场景该查什么”。
6.2 永远先看一眼时间
很多故障看起来随机,实际上有很强的规律性。看一眼当前时间,再对照监控图上的故障发生时间,能帮你快速缩小范围。比如整点告警通常和cron有关,凌晨4点的告警可能和系统备份撞在一起。电力的高峰期、业务流量高峰、定时任务密集窗口——这些时间因素都是发现根因的钥匙。
6.3 变更记录是排查的第一手资料
线上环境最怕的不是故障,而是“不知道什么东西变了”。如果你所在团队有发布窗口或操作审计日志,先查最近一次变更;如果没有,那你自己的笔记、聊天记录、邮件通知,都可能是线索。我在处理过的一次“诡异”故障里,最后发现是有人前一天把系统的内核参数 vm.swappiness 改了,导致应用被换页拖垮。变更记录,真的能救命。
6.4 别忽略dmesg这个“沉默的告警源”
现在的监控平台把很多指标都采集走了,但有些内核层级的异常,不一定有现成的监控项。dmesg -T | tail -50 是一个很容易被忽视但极其有用的命令。硬件报错(比如磁盘I/O错误、温度告警)、TCP丢包、内核panic的迹象,都会出现在这里。遇到“什么都正常但服务就是卡”的情况,我第一件事就是查dmesg。
6.5 生产环境慎用kill -9,不是所有进程都能瞬间杀掉
这里再强调一次:kill -9会让进程完全没有机会清理自己的现场。对很多中间件来说,强杀意味着数据不一致、锁没释放、集群节点被误判定为故障。如果进程确实卡死,优先尝试正常杀(kill,或者systemctl stop),给进程留几秒钟的“体面离场”时间。对于Java进程,先试试 kill -3 抓线程dump,再说杀不杀的事。
6.6 把“告警”当问题本身,而不是只看表象
告警文本提示的是“端口失联”,但根因可能在“磁盘满导致日志写不进去,进程随之假死”。所以收到告警后,不要只盯着告警描述的指标,要把系统整体的CPU、内存、磁盘、网络、进程状态都快速过一遍。有点像医生看病,你头痛可能根因在颈椎,不从全局入手,只会误诊。
6.7 命令输出要留档
排查故障过程中,我习惯把执行过的关键命令输出都粘贴进自己的排查记录,比如top、free、df、journalctl的结果。这不仅方便自己复盘,后续写报告、同步给同事都很有用。建议定期把典型故障的排查过程整理成文档——这既是你个人能力的沉淀,也是团队运维知识库的一部分。
6.8 单机排查和集群故障要用不同策略
如果是多台机器同时告警,先别急着逐台排查。先看共同点:是不是同一个交换机下的机器?是不是同一个应用集群?是不是只有某一类实例挂了?多台机器同时出问题,大概率根因在“公共层”,比如配置文件推送错误、数据库连接失效、负载均衡器故障。逐台排查反而会延误时机。
6.9 快捷键和辅助工具能显著提速
排查时手速也很关键。Ctrl+C中断、Ctrl+Z暂停、Ctrl+Z后bg转后台,这些基础快捷键要熟练。htop、iftop、ncdu这类交互式工具,比原生命令更直观,排查效率有明显提升。不过要注意,这些工具在最小化安装的服务器上不一定有,生产环境没有就敲原始命令,不影响判断。
6.10 不要一个人死扛,该叫醒的人要叫醒
深夜的故障,最怕“一个人默默扛两小时”。很多问题,多一个人从不同角度看,思路瞬间就打开了。如果是你无法独立判断的严重告警,建议十五分钟内就拉人进群,先同步现状再讨论方案。团队协作排查,跑得永远比单打独斗快。
我在实际运维中最深的体会是:故障排查的本质不是比拼命令数量,而是比拼“定位问题”的速度。你心里对系统运行的脉络越清楚,对命令原理理解得越透彻,深夜告警带来的压迫感就越小。这张“作战地图”里的每一条命令、每一个思路,都是我在真实事故中用时间换来的。希望你以后遇到情况,能比我从容一点点,那就够了。
