凌晨三点十七分,手机震了。监控大屏上一条告警跳出来:某台核心应用的负载已经从 1.2 冲到 18.7,持续五分钟没有回落。群里已经开始有人问“是不是谁动过配置”,而你知道,这时候离真正的业务受损可能只剩几分钟。深夜告警最折磨人的地方,不是技术难,而是你必须在信息极度不完整的情况下,用最短时间判断“这到底是不是真故障、影响面多大、要不要立刻爬起来处理”。我刚工作那几年,每次都是手忙脚乱地登服务器、跑 top、看日志,像无头苍蝇一样被现象推着走。后来踩的坑多了,慢慢把整个排查过程整理成了一张“作战地图”——从收到告警开始,先做什么、后做什么、每一步看什么指标,全都有固定套路。这篇文章就是想把这张地图完整地摊开给你看。
这套方法不挑具体监控系统,也不依赖某家云厂商的工具,全部基于 Linux 系统自带的命令和 /proc 文件系统。适用对象是那些需要半夜爬起来处理告警的运维、SRE、后端开发,也包括刚入门、想搞清楚“服务器告警到底怎么查”的新人。它的核心思路只有一句话:先把眼前的信息当成线索,而不是当成答案。这篇内容很长,但每一节都可以单独拿来当排查 checklist 用,希望下次告警炸你手机的时候,你能比我当年从容得多。
1. 凌晨两点的告警铃声:先做情报收集而不是盲目救火
半夜被吵醒的第一反应,往往是急着去“解决”问题。但绝大多数深夜告警的处理失败,都栽在“还没搞清楚状况就动手”上。你连问题是什么都不知道,就重启服务、扩容机器、回滚版本,不但大概率解决不了,还可能把故障现场破坏掉,后面连复盘都无从谈起。所以我把这套作战地图的起点,放在情报收集,而不是止血。
1.1 第一步先问三个问题:告警谁发的、发的什么、已经多久了
第一步是看告警平台的通知来源。不同来源对应的处理路径是完全不同的。
常见的告警来源有三种。一种是监控系统(比如常见的开源监控组件)自动触发的阈值告警,它说明的是“某个指标异常”,比如 CPU 使用率超过 90%、磁盘使用率超过 85%,这种告警通常指标明确,但背后原因未知;第二种是日志系统里出来的关键字告警,比如业务日志里突然出现大量“connect timeout”“OutOfMemoryError”,这类告警信息量更大,因为它直接暴露了异常的类型;第三种是用户在群里反馈“页面打不开”,这种最紧急,因为它直接说明业务已经受损,前面的探针可能已经失效了。
我自己的习惯,是把这三类信息全部汇到同一个临时会话里,备注上两个字段:“首次告警时间”和“当前是否仍在告警”。如果告警已经持续超过 10 分钟,或期间又叠加了其他告警,那就要按 P0(最高优先级)处理,马上登录机器;如果只是偶发一次、当前已恢复,可以先记录留待天亮后再查。
1.2 金十分钟:上机前先把这五样情报抓下来
无论你使用什么监控工具,我都强烈建议在登录服务器的前十分钟内,固定采集下面五类信息:
- 当前时间与告警持续时间:用于对齐日志时间线,确认故障发生的起点。
- 服务器 IP、主机名、所属业务模块:避免在处理过程中找错机器,或者混淆哪套服务在报错。
- 变更窗口信息:最近半小时内有没有上线、配置发布、扩容缩容、数据库迁移等操作。这一条的优先级极高,因为生产故障中有相当比例来自变更。
- 告警指标的具体数值:比如负载是 5 还是 50,磁盘使用率是 86% 还是 99%——这决定了你下一步是观察还是立刻介入。
- 上游/下游依赖的状态:你的服务可能本身没故障,而是数据库、缓存、消息队列先出问题了。如果只看本机指标,很容易误判为应用问题。
不要小看这十分钟的情报工作。它们最大的价值,是能帮你快速判断“这是不是一场真正的故障”。比如有一次告警显示某服务 CPU 使用率持续 100%,我登录上去正准备抓进程,先用上面这套方法梳理了一遍,发现是这台机器刚好承担了一个批处理任务,而批处理任务的启动时间正好在五分钟前——时间完全对得上,而且负载曲线是缓慢爬升而不是陡峭跳变,最终确认这是一次正常的业务调度,并非异常。如果当时直接按故障去处理,反而会打断正常作业。
2. 确认主机“活没活着”:从 SSH 无法连接到系统僵死的分水岭
情报收集完之后,下一步是确认主机当前的存活状态。这不是简单 ping 一下就算完事,而是要看清楚:操作系统层面的网络栈还通不通、用户态进程还能不能调度、内核有没有发生严重的资源争抢。不同的“活着”状态,对应完全不同的处理手段。
2.1 从探测 SSH 到区分“假活”与“真僵”
先说最紧急的情况:SSH 连不上。这时候优先尝试下面几种方式,按顺序来:
- ping:能通只代表网络层通,不代表系统正常。如果一个主机的 IP 还能 ping 通,但 SSH 卡在连接阶段毫无响应,通常说明内核网络栈还在工作,但用户态的 sshd 进程或更高层的服务已经无法正常调度。
- 尝试从带外通道登录:很多服务器有远程管理控制台(不同厂商叫法不同,公共语义是带外管理通道),如果带外能进去而 SSH 进不去,问题基本锁定在操作系统内部,不是网络链路。
- 尝试登录后立即执行 uptime:如果 uptime 都能卡住,说明系统负载极高或者 CPU 被完全占死,进入了“假活”状态。
有一个我反复踩过的坑是:SSH 能连上,但敲任何命令都要等几十秒才回显。这不是网络慢,而是系统负载已经高到让所有进程都在排队。这时候你看到的一切输出,其实都是“延迟的快照”,不要急着根据它们下结论。
2.2 系统还能回应时的第一轮体检:uptime / dmesg / 内存水位
如果 SSH 还能正常响应,我会固定跑三条命令,相当于对系统做一次“生命体征测量”:
uptime dmesg -T | tail -50 free -h- uptime里的 load average 是三个数字(1 分钟、5 分钟、15 分钟)。我的经验是:只看 15 分钟值判断趋势,看 1 分钟值判断当前压力。如果 1 分钟值远高于 15 分钟值,说明负载是刚刚才起来的,大概率是突发事件;如果三个值都高且接近,说明已经持续很久了。
- dmesg -T用来查看内核环形缓冲区最近的日志。特别注意里面有没有Out of memory: Kill process或hung_task_timeout_secs、blocked for more than 120 seconds这类关键字。出现这些,说明内核已经动手杀进程了,问题级别瞬间升到最高。
- free -h看内存水位。但这里我要提前打个预防针:free 的输出很容易骗人,光看 available 不够,得结合 buff/cache 的变化趋势来判断,这一点在后面专门展开。
这三个命令跑完,你对这台机器已经有一个大致的健康画像:是 CPU 压垮了系统,还是内存触发了杀进程,或是磁盘 IO 卡死导致内核报 hung task。接下来才进入逐项深挖的环节。
3. 系统三大资源的优先级排序:CPU、内存、磁盘谁先查?
很多人上了机器就是跑一个 top,看到哪个进程 CPU 高就盯着哪个看。这思路太线性了。一个业务故障往往不是单一资源引起的,而是多资源互相拖累的结果。我对新手最常见的建议是:先根据告警的类型决定排查优先级,而不是什么都从头查一遍。
3.1 CPU 打满与负载虚高的辨析:从 top 里的三个数说起
top 命令第一行有三个 load average 数字,它们常被误解。请记住一个粗略但实用的对应关系:在传统 Unix 系统中,load average 不仅统计正在运行的进程,还包括处于不可中断睡眠状态(D 状态)的进程。这是理解“负载高但 CPU 低”这个现象的关键。
具体到排查动作上:
- 如果 load 高,且CPU 使用率也高(us + sy 接近 100%),优先看 CPU 计算型任务,比如死循环、频繁 GC、大量正则匹配。
- 如果 load 高,但CPU 使用率并不高(us + sy 可能只有 30%),那么大量进程很可能是 D 状态,也就是阻塞在 IO 或者锁上。这时候你盯着 CPU 看永远找不到答案,要去看磁盘和进程栈。
判断 D 状态的进程,可以用这个命令去看有多少进程处于不可中断睡眠:
top -b -n 1 | awk '{print $8}' | sort | uniq -c如果 D 状态进程数量很多,下一步就去看磁盘 IO,用iostat -x 1观察%util和await。这两个值一旦长时间偏高,大部分情况下是底层存储响应变慢,而不是 CPU 的问题。
3.2 内存和 swap 的“假象”:free 输出里容易被忽略的 buff/cache
再看内存。free -h的输出里,buff/cache 那一列经常让人误判。Linux 会把空闲内存拿来当文件缓存,这是正常现象。你需要关注的核心指标是available这一列,它代表“在需要时可以被回收用于应用的内存”。
真正危险的信号出现在下面两种情况:
- available 不断下降,且 swap 的使用量持续上涨。swap 上涨说明内存已经不够用,系统开始把冷数据换到磁盘上。一旦出现这个信号,再叠加 IO 变慢,整个系统的响应时间会迅速恶化。
- dmesg 里出现 Out of memory。这代表内核已经选择杀掉一个或多个进程来释放内存。这种情况非常危险,因为被杀的可能不是你想保留的那个进程,而是刚好在排队等待内存的任意业务进程。
对于内存类告警,我建议不要只盯着 free 看,而是用ps aux --sort=-%mem | head -10找出内存占用最高的进程,再结合/proc/<pid>/status里的VmRSS字段,确认它是真实使用还是在吸缓存。很多 Java 应用因为堆外内存设置不合理,会持续消耗内存到触发 OOM,这类问题眼看不出来,得结合应用层内存参数排查。
3.3 磁盘不留余地的坑:有空间、有 inode、还是写不进去
磁盘排查里最让人抓狂的,就是“df 显示还有空间,但文件就是创建失败”。这通常有两个隐藏原因。
第一个是inode 耗尽。df 显示的是块空间,而文件系统还需要 inode 来存文件元数据。如果某个目录下疯狂生成小文件(比如临时文件、日志碎片被错误重定向、消息队列的持久化小文件),inode 就可能耗尽,表现就是 “No space left on device”。检查命令:
df -i第二个是文件系统只读。很多系统在检测到文件系统异常之后,会主动把挂载改成只读模式,防止数据进一步损坏。这时候touch /路径/test会提示只读文件系统错误,但/var/log/messages里可能已经有一堆 block error 的记录。遇到这种情况,不要急着 remount rw,先自查是否存在坏盘、虚拟化底层存储抖动等问题,否则强制改回读写模式很可能加重数据不一致。
资源排查的优先级我也统一归纳一下:
| 告警特征 | 优先排查资源 | 第一组命令 |
|---|---|---|
| CPU 使用率持续 100% | CPU / 计算逻辑 | top, pidstat -p, ps -eo pcpu |
| 负载高但 CPU 不高 | 磁盘 IO / 锁等待 | iostat -x 1, top 看 D 状态, cat /proc/ /stack |
| 内存持续下降 / OOM | 内存泄漏 / 缓存压力 | free -h, dmesg, cat /proc/ /status |
| 磁盘使用告警 | 空间 / inode / 只读 | df -h, df -i, mount | grep ro |
4. 从进程、线程到内核态:一次 CPU 飙高问题的完整实操回溯
这一章我想用一个虚构但非常典型的案例,完整走一遍 CPU 飙高的排查链路。假设你在某公司的某套系统上值班,告警内容是:某台业务服务器的 CPU 使用率 100%,持续时间 15 分钟。注意,这种告警看起来简单,但如果不按章法查,可能两小时过去了还在原地打转。
4.1 先找出“带头”的进程:top / pidstat / ps 的组合判断
登录后的第一件事,我通常执行:
top -c -b -n 1 -o %CPU | head -20-o %CPU让 top 按 CPU 使用率排序,-c显示完整命令行。这一步能快速定位到进程 PID。如果看到多个进程 CPU 都很高且都属于同一应用,就要考虑是不是请求量突增或者线程池跑满;如果只有一个进程高,那问题很可能集中在这一个进程内部。
接着用 pidstat 看更细的线程数据:
pidstat -t -p <PID> 1 5因为 Java、Go、Node 等应用都是多线程模型,-t参数能列出进程里线程维度的 CPU 占用。锁住 CPU 消耗最高的线程 TID。记下这个 TID,后续要顺藤摸瓜。
4.2 顺着线程 ID 往下挖:从线程栈到系统调用
找到线程 TID 后,就要把线程 ID 转变成“代码在干什么”的证据。这一步和语言栈强相关,但思路是共通的。
如果目标应用是Java,可以利用线程 dump 找到对应线程:
jstack <PID> > threaddump.txt再在 dump 文件里搜索线程 ID 的十六进制形式(比如 TID 是 12345,转十六进制为 0x3039),就能看到对应的线程栈。常见的高 CPU 线程栈往往出现在这几类位置:死循环的 while 语句、一些正则表达式回溯、GC 线程相关逻辑、或者是sun.misc.Unsafe等底层调用附近。如果你看到的是 GC 线程 CPU 占用极高,那问题不是代码死循环,而是堆内存太小或者对象分配太频繁,需要结合 jstat 看 GC 频率。
如果目标应用是Go,可以使用SIGQUIT信号触发 goroutine dump(要注意对生产环境的影响),或者借助 Go 自带的 pprof 工具采样 CPU profile。
如果目标应用是原生 C/C++,或者你不确定看代码路径,可以用 strace 看系统调用:
strace -p <TID> -c -f-c会在退出时打印系统调用的统计汇总。如果某个系统调用(比如 futex、nanosleep、sysinfo)出现频率异常,通常能暴露问题类型:大量 futex 说明线程在反复争抢锁;大量 nanosleep 说明可能在做无谓的轮询或重试。
4.3 从用户态窜到内核态:用 perf 看“到底在燃烧什么”
还有一种情况:top 里显示 sy(内核态 CPU)直接拉满,但 strace 却看不到任何明显异常的进程行为。这通常意味着高 CPU 消耗发生在内核态内部,可能是系统调用的路径实现、网络协议栈处理、内核模块异常等引起。
这时手上最顺手的工具是 perf。用 perf 做一个短暂的采样,获取热点调用栈:
perf record -F 99 -a -g -- sleep 30 perf report通过perf report的火焰图或调用栈统计,能直观看到 CPU 时间花在哪些函数上。比如你会发现默认的大量消耗集中在tcp_ack、kmem_cache_alloc、do_sys_open这类内核函数上。这些信息对下一步定位方向很关键:tcp_ack大量消耗通常代表网络上存在着异常重传,应用进程可能并没有在做高负载计算,而是在反复处理网络收包重传。
我在实操中的体会是:perf 这类剖析工具平时最好先在测试环境跑熟,等真到深夜里告警中,手忙脚乱地读火焰图是非常痛苦的一件事。最好的状态是你已经习惯特定语言、特定服务类型最容易出现的高 CPU 形态,然后照方抓药。
5. 日志会说话但不说废话:grep 之外的四种日志分析思路
很多人在故障排查时会犯一个共同的错误:日志一多就只会grep ERROR,然后把匹配到的几百行往屏幕上一铺,挨个看。这样效率极低,因为日志分析的本质不是找关键字,而是重建故障现场的时间线。下面这四种思路,是我处理深夜告警时用下来最高效的。
5.1 按时间线重建现场:journalctl 与日志文件的时间窗口切割
最常见、也最有效的第一步,是把日志切割在故障发生的窗口内。
如果系统使用 systemd 日志,推荐用:
journalctl --since "2026-06-01 02:50:00" --until "2026-06-01 03:20:00" -p warning查看这个时间窗口内所有 warning 及以上级别的日志。注意:不要只看 ERROR,warning 级别里往往藏着很有价值的信息,比如“connection reset by peer”“retrying after error”这些,它们能告诉你故障是从哪一刻开始恶化的。
如果是传统文本日志(比如/var/log/messages、应用日志),就用 sed/awk 截取时间片段,比如:
awk '$0 >= "[2026-06-01 02:50:00]" && $0 <= "[2026-06-01 03:20:00]"' /var/log/app/app.log > fault_window.log拿到故障窗口的日志之后,不要急着从中间读,而是先看第一行出现的错误是在哪一秒,这个时间点叫“故障起点”,后续很多排查都要以它为准展开。
5.2 识别“风暴型”故障:从高频重复到找到源头
还有一种典型告警形态——日志里没有复杂错误,只有同一条错误信息疯狂重复。比如pool-3-thread-1: Connection refused一秒出现几十次,或者ERROR java.sql.SQLException: Too many connections。
这种“风暴型”日志的排查思路和一般日志不一样,不要逐条分析内容,而是先统计重复次数最高的那几条:
一些日志管理工具里有现成的 pattern 统计功能,如果手头没有,可以用简单的脚本对日志做 pattern 归一化后统计。原理就是把日志里的数字、IP、时间戳等可变部分替换成占位符,然后按模式聚合。一旦发现某一条错误模式占比超过整体异常的 80%,就直接定位到核心故障源了。
比如一个典型场景:数据库连接池参数设置过小,导致高峰时应用日志里出现成百上千条 “connection refused”,下游数据库也对应出现大量连接数告警。这时候改应用日志没意义,得从连接池配置和数据库最大连接数两边同时处理。
5.3 日志无报错时,学会“横向对比”
有些故障更狡猾:日志里几乎找不到 ERROR,但业务就是超时、报警就在那里。我的经验是,这种故障往往不是“程序故意报错”,而是“被动变慢”。比如上游依赖变慢导致调用线程全部在等待,底层的 io 操作超时但业务框架没把它打成 ERROR,只体现在响应时间上。
排查这类问题,横向对比是必须的。打开同一服务不同实例的日志,对比同一时间段内的响应时长分布;或者打开不同服务的日志,看是否有同一时间点的响应时间都猛增的“共振”现象。如果多个无关服务在同一时间区间内都变慢,那就极有可能是有共享的基础设施出现了问题,比如网关节点异常、DNS 解析变慢、共享存储性能下降,而不是某个应用自身的 bug。
日志分析最忌讳的就是孤立地看待某一个服务的输出。做一个“多源时间线对齐”,往往比盯着一屏报错代码有价值得多。
6. 网络告警排查链路:连接积压、超时与丢包的快速判据
网络类告警在深夜出现的频率非常高,但它的排查难度往往不在命令本身,而在于网络问题经常是“跨层”的。你以为在查应用,其实问题在网络协议栈;你以为在查网络设备,其实是内核参数不合理。这一章只聚焦在登录服务器之后,如何用有限的命令快速建立判断。
6.1 从 ss 输出看懂半连接、全连接队列溢出
网络连接问题的核心是看队列。每个 TCP 监听端口都有两个队列:syn queue(半连接队列)和 accept queue(全连接队列)。队列溢出的直接表现,就是客户端连接建立慢、超时,甚至大量的 connection refused。
查看监听队列状态的常用命令是:
ss -lnt重点关注输出里的Send-Q和Recv-Q两列。对于 LISTEN 状态的 socket,Send-Q显示的是 accept queue 的上限值,Recv-Q显示的是当前已经排队的连接数。如果 Recv-Q 长期等于或接近 Send-Q,说明应用层 accept 新连接的速度跟不上连接到达的速度,全连接队列已经积压。这种情况要么是进程线程池耗尽来不及 accept,要么是某个连接被业务逻辑长时间占用,要么是短连接请求量超过了系统处理上限。
半连接队列溢出的迹象,则更隐蔽。它不会直接出现在 ss 的输出里,需要观察netstat -s里的 TCP 统计计数器:
netstat -s | grep -A 20 "Tcp:"如果看到SYN to LISTEN sockets dropped数量持续上升,基本可以断定半连接队列溢出了。常见原因有 SYN Flood 攻击、客户端重试策略异常、服务器的net.core.somaxconn或net.ipv4.tcp_max_syn_backlog配置过小。
晚上值班遇到这类告警,我一般先执行ss -lnt看队列积压,再看/proc/net/tcp或者ss -tanp | grep <端口> | wc -l统计当前连接总量,和正常时段对比一下量级。量级差异过大再去调整内核参数,不要一上来就盲目放大 backlog。
6.2 应用层超时与 connect 超时的边界判断
深夜最常见的口头禅是“网络是不是断了?”但绝大多数应用感知的“网络问题”,其实是应用层协议超时,而不是底层的物理链路中断。比如客户端配置的读超时是 3 秒,那么只要服务端在 3 秒内没有返回数据,客户端就会报超时。这时候底层网络可能好得很,问题只是服务端响应慢。
排查“超时类告警”的关键,是要把两层超时分开:connect 超时(建立连接阶段失败)和 read/write 超时(连接建立后数据读写阶段失败)。
- connect 超时更多指向网络路径不通、端口未监听、连接队列溢出、防火墙丢包。可以用
telnet <IP> <PORT>或者timeout 1 bash -c 'echo > /dev/tcp/<IP>/<PORT>'来测通;不通再逐跳检查路由和防火墙规则。 - read/write 超时则指向数据面问题,可能是服务端线程池满、IO 阻塞、数据库慢查询。这时候一把梭查网络没意义,重点应该回到前面的进程和日志排查。
我在实操里经常看到的情况是:两边业务各执一词,网络组说交换机没丢包,应用组说连接总断。最后发现是中间某一台负载均衡设备的空闲连接超时时间太短,长连接在空闲一段时间后被设备静默回收,应用层没有实现自动重连,于是执行到某条请求时才发现连接已失效,报一堆 timeout。这种跨层问题靠单机命令查不出来,必须在监控数据里把网络路径的每一跳都串起来看。
6.3 MTU、丢包与 TCP 重传的快速自查
当你怀疑是链路质量问题,也就是丢包和重传时,有三个数据源可以辅助判断。
第一是netstat -s | grep -E 'retrans|drop',观察 TCP 重传计数是否持续增长。重传率高说明链路丢包,或者对端处理不过来导致乱序、超时。
第二是检查 MTU。如果出现大量接近 MTU 上限的大包被丢弃,而小包正常(比如 SSH 能通,但 scp 大文件卡死),很可能是链路中间某段设备的 MTU 比服务器小,触发了分片问题。可以尝试把接口 MTU 调低测试,也可以检查 ICMP 分片禁止相关的报错。
第三是看网卡层的错误计数:
ip -s link show eth0重点观察 RX errors、RX dropped、TX errors、TX dropped。RX dropped 高说明网卡接收缓冲区或者内核收包路径有瓶颈;TX dropped 高则可能是发送队列饱和。看到这类指标后再调整 ring buffer 或队列长度才有意义,否则就是在瞎调参数。
网络排查最核心的原则是先分层,再动手。应用层、传输层、网络层、链路层,一层一层圈定嫌疑范围,绝不跨越层级的判断做事。
7. 告警处理完毕不等于结束:让下一次深夜不再被“炸醒”的复盘清单
故障处理完,系统恢复正常,很多人就以为结束了。但我可以坦率地说,每次真正拉开人与人差距的,是故障结束之后那一个小时做的事。一个故障如果只能带来一晚上的惊吓而不能沉淀成流程改进,那它就是纯粹的损失;如果你能把它变成下一次更从容面对问题的资本,那这个故障才不算白熬。
7.1 校准告警阈值与监控项,把“狼来了”的噪声降下来
很多团队会被深夜告警折磨,一个重要原因是监控项设得太多太敏感。运维和 SRE 最大的痛苦不是没有监控,而是监控系统把自己淹没在无效告警中。告警平台里告警一多,每次响铃都要人肉判断优先级,预警的价值反而被稀释。
复盘时第一件事,就是拿出这次告警对应的监控项,逐一审视三个问题:
- 这个指标还重要吗?是否可以直接反映业务受损?
- 阈值设得合理吗?会不会在正常流量波动下也触发?
- 告警持续时间设得太短了吗?是否只要一分钟的瞬时抖动就会通知?
我的做法是给每个告警项打两个标签:“可自愈”和“不可自愈”。可自愈的告警(比如瞬时 TCP 重传率升高、短时间 CPU 飘高)可以调大持续时长或者直接降级成记录;不可自愈的告警(比如某进程挂掉、磁盘只读)必须立刻通知。这个分类能把生产环境从“狼来了”的状态里解放出来。
7.2 沉淀为可执行的 SOP 与预案
处理完一次故障之后,最好能在一个小时内把这次事件写成一篇小复盘文档,包含五部分:故障现象、时间线、排查过程、根因、后续改进。不要写太长,但要能让他人照着这个文档复现整个排查思路。
我个人的习惯是,每个高频故障类别都沉淀一份一页纸的 SOP:
| 故障类别 | 分钟内必做的三件事 | 常见根因 | 回滚/缓解动作 |
|---|---|---|---|
| CPU 飙高 | top 看进程, pidstat 看线程, perf 采样 | 死循环、GC、资源争抢 | 重启单实例、限流、回滚发布 |
| 内存告警 | free 看 available, dmesg 查 OOM, ps 看 RSS | 内存泄漏、连接未释放 | 扩容内存、缩副本、dump 分析 |
| 磁盘告警 | df -h, df -i, iostat -x | inode 耗尽、坏盘、日志打满 | 清日志、扩展目录、迁移数据 |
| 连接超时 | ss -lnt 看队列, netstat -s 看重传, telnet 测通 | 队列溢出、依赖服务慢、防火墙 | 重启依赖服务、扩容、调整内核参数 |
这份 SOP 不需要面面俱到,它的核心价值是让下次值班的人不用从零开始思考,而是直接进入“按图索骥”的状态。深夜告警中,最贵的就是决策时间,SOP 能把你在清醒状态下做好的决策,复制到凌晨四点的自己手上。
7.3 自动化自愈与故障演练:让告警铃变成“已恢复”短信
复盘做到后期,可以逐步把一部分高频、低风险、根因明确的故障转化为自动化自愈流程。比如:
- 某个服务进程异常退出,但没有核心数据丢失风险,可以用进程守护或 k8s 的 restartPolicy 自动拉起;
- 磁盘使用率超过阈值,如果没有特殊挂载要求,可以启动定时清理脚本删除旧日志;
- 某台实例因为内存泄漏持续 OOM,可以设置更智能的健康检查,让容器编排层自动重启该实例并把流量切到其他副本。
自动化自愈不是说彻底消灭人工介入,而是把人的注意力从“重复的、低认知的恢复动作”中释放出来,留给真正需要判断力的问题。设置自愈规则时一定要谨慎评估它的副作用,比如自动重启虽然能快速恢复服务,但也可能掩盖数据一致性问题,属于需要重点把关的场景。
最后一个让我印象很深的心得,来自某位前辈的一句话:故障处理能力不是临场发挥出来的,而是平时排练出来的。我后来坚持每季度挑一个非核心服务做一次模拟故障演练,故意在网络层、应用层、存储层各注入一个故障,然后让大家按 SOP 走一遍处置流程。练过的团组和没练过的差别最大之处,不在命令熟练度,而在那种“哪怕半夜惊醒也能稳住节奏”的心态。你越熟悉这套排查链路,越能在混乱中保持清醒的头脑,这大概就是“作战地图”真正想给你的东西。