生产事故应急手册:运维速查命令与高可用架构防护实战指南
发布时间:2026/10/8 16:47:56 作者:尧图编辑部 阅读量:1,286

1. 事故现场的第一反应先灭火再救火最后才是写报告干了这么多年运维我最怕的不是半夜被叫醒而是被叫醒之后的那一分钟内涌进群里的十几条“系统挂了”“页面打不开”“用户一直报错”。说句实话生产事故这件事百分之八十的慌乱都发生在头十分钟。头十分钟稳住后面就是按部就班地定位、止血、恢复头十分钟乱了后面就是全员猜谜、重复操作、越修越糟。这篇实战白皮书就是把我这些年踩过的坑、熬过的夜、总结出来的速查命令和架构级防护思路一次性整理出来。不管你是在大厂做SRE还是在中小公司一个人兼着运维和开发遇到事故时能有一张清晰的作战地图和没有这张地图完全是两种结果。先建立一个最基础的心智模型任何生产事故处置顺序永远是“止损 - 定位 - 恢复 - 复盘”。很多新人上来就想定位根因这是最大的误区。系统还在持续报错、用户还在受影响你花二十分钟慢慢查代码业务损失早就扩大了。正确的做法是先判断这个故障是“需要立即切流量/重启/降级”的级别还是“可以容忍观察几分钟”的级别。前者先做止损动作哪怕动作粗暴一点比如摘掉故障节点的负载均衡、强制重启应用、回滚最近一次发布都比让故障持续扩散要强。第二个关键认知是事故现场要有一个明确的“总指挥”。这不是官僚主义而是避免多头操作。运维、开发、DBA、网络各查各的最后在一个群里互相问“你那边查得怎么样了”效率极低。总指挥的角色通常是值班负责人或资深SRE他负责把所有人的信息汇总到一张时间线上确认下一步动作。记住事故现场不需要民主讨论需要的是明确指令和快速执行。还有一点我想特别强调信息同步的节奏比信息本身更重要。哪怕你说的是“还没查到原因”每隔五到十分钟也必须在群里同步一次状态告诉大家“还在查目前排除了XX”。这种同步一方面是让老板和业务方安心另一方面也能让其他同事在不知情的情况下不重复你已经排查过的路径节省宝贵的窗口时间。我在实际处理中习惯把每次同步都带上时间戳和已排除项这张时间线在后面写复盘报告时就是最宝贵的素材。在动手敲命令之前先把故障的影响范围圈出来。是单台机器的问题还是整个集群的问题是某个接口变慢还是全站不可用是网络入口的问题还是应用内部的问题这三个问题回答了排查路径基本就窄了一半。这也是为什么下面要说到的命令清单要分层——你不可能用同一套命令解决所有层面的故障必须有一套体系化的排查思路。2. 运维速查命令清单按故障类型直接翻对应章节这一节是整篇的实操核心。我整理的命令不追求大而全只追求“生产事故中真正用得上、能给出关键信息的”。我把它们分成系统资源、网络、应用、数据库和日志五个维度每一条都标注了适用场景和判断阈值方便你在高压环境下直接照着执行。2.1 系统资源类CPU、内存、磁盘、IO一网打尽CPU 飙高时第一个命令必须是uptime。这个命令同时展示当前时间和系统1、5、15分钟的平均负载不用纠结绝对值重点看趋势如果1分钟负载远高于15分钟说明是刚刚开始的突发负载如果三个数值都很高说明问题已经持续了一段时间可能需要更激进的手段。顺着负载高这个线索下一步用top按 CPU 排序找出吃 CPU 最狠的进程 PID。这里有个小技巧——top进入交互界面后按大写P可以按 CPU 排序按大写M可以按内存排序别每次进去都从上往下肉眼翻。定位到进程之后pidstat -p PID 1可以持续输出该进程每一秒钟的用户态和内核态 CPU 占用。用户态高通常是业务代码在做计算内核态高则可能是系统调用频繁比如磁盘 IO 或网络收发。这一步能帮你在“代码问题”和“系统问题”之间划一条初步的界线。内存方面free -h是第一个要执行的命令但它只告诉你总量和剩余量无法定位谁吃了内存。真正排查内存泄漏或内存飙高top按内存排序找到可疑进程后要用/proc/PID/status里的 VmRSS 字段确认物理内存的真实占用。这里特别提醒一句千万不要被free里的 used 值吓到Linux 会尽量把空闲内存用来做文件缓存cached这部分在应用需要时可以释放真正需要警惕的是available这个字段它才是“在不触发 swap 的前提下还能分给新程序的内存”。磁盘则是一个“看似简单实则坑最多”的层面。df -h看的是文件系统的使用率不会因为文件被删除而立即变化——如果你用rm删了大文件但空间没有释放通常是有进程仍然持有该文件的句柄用lsof | grep deleted找到那个进程重启它或者让它重新打开文件空间才会真正释放。IO 方面用iostat -x 1重点看%util和await前者接近100%说明磁盘确实忙不过来后者如果稳定高于几十毫秒说明有 IO 等待堆积。顺带提一个很多老手都在用的命令dstat它能把 CPU、磁盘、网络、内存的实时数据组合在一屏里输出排查“某个时段系统突然卡顿”这类模糊问题时比一个个敲命令高效得多。2.2 网络层排查从连通性到数据包内容网络故障的特点是一旦发生会影响一大片服务所以排查思路要清晰先确认边界再逐段缩小范围。第一步是确认本机网络状态ip -s link看是否有丢包和错误计数ss -s看当前的 TCP 连接统计摘要。ss是netstat的替代品输出更快信息也更全我已经很久不主动用 netstat 了。第二步是确认与你调用方的连通性ping只适合看通不通看不了链路质量要用mtr 目标IP——它结合了 traceroute 和 ping能逐跳显示丢包率一眼就能看出问题出在局域网、运营商骨干还是对端机房。端口连通性用telnet IP 端口或nc -vz -w 3 IP 端口超时时间一定要设置不然会卡在那里像死机一样。如果端口通但接口报错考虑直接抓包看应用层数据。tcpdump -i eth0 -nn port 端口 -c 100抓 100 个包配合-w参数落盘后用 Wireshark 分析能定位到是 TCP 重传过多、还是响应报文的延迟来自对端。我处理过好几次“服务间调用超时”的案子最后都是抓包发现对端进程收到了请求但迟迟没有返回数据问题根本不在网络而在对端应用线程池满了——这就是为什么抓包能力是网络排查的最后一块拼图它能让你拿到事实而不是猜测。2.3 应用与中间件Java、Nginx、MySQL 的高频指令如果你在维护 Java 系的应用那么下面这几个 jdk 自带命令是必须滚瓜烂熟的。jps -l列出当前机器上的 Java 进程和主类jstack PID打印线程快照jmap -heap PID查看堆内存配置和当前分配jstat -gcutil PID 1000每秒输出一次 GC 情况。这套组合拳基本覆盖了“内存溢出、线程死锁、GC 频繁”三类最常见事故。线程问题的完整链路是这样的先用top -Hp PID找出进程内 CPU 占用最高的线程 TID然后用printf %x TID把十进制转成十六进制再到jstack PID的输出里搜这个十六进制线程号nid就能直接看到那个线程当前执行的代码栈。这一串动作我建议每个人都在测试环境自己走一遍确保熟练因为现场没人有时间教。Nginx 方面nginx -t检查配置语法nginx -s reload平滑重载配置tail -f /var/log/nginx/access.log实时观察请求。遇到 502/504 时要看error.log里的 upstream 状态配合ss -tan state time-wait | wc -l确认连接回收是否正常。MySQL 这边最高频的场景是慢查询和连接打满show full processlist直接看当前所有正在执行的 SQL 状态配合SHOW GLOBAL STATUS LIKE Threads_connected判断连接池水位慢 SQL 用slow_query_log抓到之后EXPLAIN看执行计划重点关注 type 字段有没有从ref降到ALL全表扫描以及 rows 是否比预期大了几个数量级。2.4 日志与全链路追踪让证据替你说话所有速查命令里我私心觉得最“救命”的是日志相关的技巧因为日志操作通常来钱最快。tail -f配合grep是标准动作但真正高效的是tail -n 2000 日志文件 | grep -E ERROR|Exception|超时 | tail -n 100——先取末尾两千行再过滤而不是对整个大文件 grep速度差好几倍。另外要善用journalctl --since 10 minutes ago -u 服务名查看 systemd 服务的近期日志它有内置的时间过滤比翻日志文件优雅得多。全链路追踪Trace是现代微服务排查的事实标准。如果你公司的系统已经接入了 SkyWalking、Zipkin 或 Jaeger事故处理时第一件事就是拿入口请求的 traceId 去查整条调用链看瓶颈在哪个服务。还没接入的话一个低成本的过渡方案是在日志中约定一个 requestId 字段贯穿所有内部调用grep 的时候直接按这个标识串起多个服务的日志。我自己做过的最有价值的一件事就是推动团队在所有日志打印中统一包含 traceId这之后排查跨服务问题的平均耗时下降了不止一半。日志是事故现场最忠实的证人和最有力的工具前提是你平时把它打印好、管理好。3. 实战案例拆解三个真实事故从发现到恢复的全过程3.1 案例一接口突然平均耗时翻五倍有一次值班监控大屏显示订单查询接口的平均耗时从 120ms 涨到 600ms但 CPU 和内存都看不出明显异常。当时我的第一反应是“这不是单机资源问题而是链路问题”于是先查了统一网关的入口日志确认耗时上涨是整个集群的普遍现象而不是某一台机器的问题排除了单点故障。接着去看调用链发现这个接口内部调用了三个下游服务用户中心、商品中心、价格服务。追踪结果把矛头指向价格服务——平时 20ms 返回的调用现在稳定在 400ms。紧接着我开始查价格服务的线程池和连接池jstack看到大量线程阻塞在 Redis 的 GET 请求上。顺藤摸瓜查 Redisredis-cli --latency一看延迟达到了好几百毫秒再SLOWLOG GET拉出慢命令发现罪魁祸首是一条巨大的SMEMBERS调用把 Redis 的 CPU 打满了。最后定位到是业务方上线了新功能每次请求都会把一个几千成员的集合全量取出而当时这个集合的 QPS 又特别高。整个处理过程大致花了四十分钟但真正“拍板”的只有几步调用链定位到下游、线程栈定位到 Redis、慢命令定位到具体 key。这里我想分享一个经验排查链路问题永远沿着“入口 - 中间层 - 依赖”的方向走每走一步排除一个嫌疑对象不要同时怀疑所有东西。事后给业务方加了 memcache 缓存把 SMEMBERS 结果缓存住接口耗时立刻回到 100ms 以内同时限制了这个集合的容量避免它无限膨胀。3.2 案例二磁盘告警空间明明删了却没释放有一次磁盘使用率冲到 95%值班同事巡检发现是日志目录太大顺手执行了rm -rf /app/logs/*.log。可是命令执行完再df -h一看使用率纹丝不动群里立刻炸了锅有新人怀疑是不是“rm 根本没生效”。我在群里回了一句先lsof | grep deleted看看是哪个进程还握着这些被删的文件。结果一查Java 应用进程打开着所有被删除日志文件的句柄文件虽然从目录里消失了但内核还在为进程保留数据块空间自然释放不了。处理办法其实很简单让应用平滑重启一遍让进程重新打开日志文件空间立即释放。这件事有两个教训第一个教训是排查磁盘空间之前一定要先确认有没有进程持有已删除文件的句柄尤其在日志类场景极其常见第二个教训是我们从此给所有日志文件配置了logrotate轮转策略按大小和天数双维度切割、压缩、清理从根上避免日志把磁盘打满。这类案例在排障手册里算是“冷知识”但真的遇到时很容易让团队卡壳半小时以上值得单独写进应急手册。3.3 案例三后半夜 Full GC 卡死所有核心线程这是让我印象最深的一个案例。凌晨三点被监控告警吵醒交易核心服务出现大面积超时但机器负载不高进程还活着就是接口几乎全部不返回。我先用jstat -gcutil PID 1000看了一眼 GC 情况发现 Old 区占用率持续在 98% 以上Full GC 每隔几秒触发一次每次耗时四五秒。这个状态就解释了为什么接口全部超时——应用线程全都堵在等待 GC 完成。接着用jmap -dump:formatb,file/tmp/heap.bin PID把堆内存打了一个快照。当时是凌晨不能影响线上所以打了快照之后先把这台节点的流量摘掉再决定怎么恢复。摘流量之后进程的 Full GC 压力依然存在但因为没有了新请求线程等一等总能恢复。随后我重启了这台节点并同时保留了堆快照文件。白天下班之前用 MATMemory Analyzer Tool打开快照看 Dominator Tree发现有个名为LocalCache的对象占了 70% 的堆空间——本地缓存没有设置 TTL在高峰期把所有订单详情都存了进去结果内存直接爆了。恢复动作本身并不复杂改造方案也不复杂——给缓存设置合理的过期时间、配置最大条目数、把命中率低的对象挪到 Redis。但这件事让我深刻地认识到内存问题的根因往往不是“谁分配太多”而是“谁没有释放”而定位“谁没有释放”最有效的工具就是堆快照分析。与其在事后苦思冥想不如早点把jmap和 MAT 这套组合练熟。4. 架构级防故障设计指南与其反复救火不如拆掉引火药桶4.1 冗余与多活把“单点”消灭在架构设计阶段运维做久了都会有一个执念凡是可能单点故障的环节都必须有冗余。这个理念不是让你上来就搞复杂的多活架构而是分层去做。最基本的Nginx 至少两台做负载均衡数据库至少一主一备并且把自动切换跑通应用服务至少两个节点分布在不同的物理机或机架上。我曾经接手过一个环境所有服务都堆在同一台物理机上有多“勇猛”就不说了一次硬件故障整个业务全灭。后来我花了两周时间把所有服务拆开、部署到多台机器再配合负载均衡那之后再没出过因为单机宕机导致的全站不可用。进阶一点的思路是多活。但我想给一个诚恳的建议——多活不是标配很多业务体量并不需要跨城多活强行上多活反而可能引入数据一致性的“大坑”。对大多数团队来说更务实的做法是核心读服务支持多副本就近读核心写服务做到数据库主备秒级切换整个切换流程用脚本固化下来每季度演练一次。单点不可怕可怕的是你从来没演练过“单点挂了之后怎么切”。4.2 容量规划和限流熔断别等系统自己扛不住才想办法很多故障的本质是“请求量超过系统承载上限”。与其纠结为什么流量涨了这么多更现实的问题是——你有没有在流量进来之前就做好防御。限流是保护系统不被突发流量冲垮的第一道闸门。接入层可以做 IP 维度限流应用层可以做用户维度或接口维度限流实现方式从最简单的计数器、到令牌桶算法、再到Sentinel/Hystrix这种框架级方案都有。我的经验是不要一开始就追求算法多高级先能在入口处拦住“明显不合理的流量”就能在事故出现时保住核心链路。熔断则是保护“下游脆弱依赖”的最后一道闸门。当某个下游服务的错误率超过阈值熔断器打开后续请求快速失败而不是继续打爆下游。这里有个大坑——很多团队把熔断的阈值设得太低或者超时时间设得太长导致熔断迟迟不触发系统照样被打垮。我的建议是超时时间宁可激进一点例如 500ms 甚至更短错误率阈值宁可先从严再逐步放宽先保证系统本身活着再谈用户体验。还有一点必须配套做——降级预案。哪些接口在紧急时刻可以返回兜底数据哪些功能可以直接关闭都要提前列成清单而不是等事故来临再去问业务“这个能不能降”。4.3 超时、重试与幂等细节里的保命符这一节看着“软”其实是生产事故高发区尤其是微服务架构下。一次调用不设置超时时间下游挂了你跟着挂一个重试不加幂等保护重复下单、重复转账这类事故就会接踵而至。超时方面连接超时connect timeout和读超时read timeout必须分开设置。连接超时可以短几百毫秒到 1 秒读超时则要根据业务接口的真实耗时分段设置。最忌讳的是所有接口用一个统一的超时时间——慢接口太多导致整体等待过长快接口被拖累整个链路都被拖垮。重试方面我有一条铁律写操作默认不重试除非接口明确实现了幂等。读操作可以重试一两次但重试必须加退避策略比如第一次等 100ms第二次等 300ms否则流量低谷时下游刚喘口气你的重试又把人家打趴了。幂等设计上核心是给每次操作生成一个全局唯一业务号比如订单号下游通过这个号码去重而不是依赖数据库的唯一索引去硬扛——除非你确认能够承受并发的唯一索引冲突。4.4 可观测性建设把“盲猜”变成“看图说话”每次事故复盘我都建议大家问自己一个问题“如果再发生一次我们能更快发现吗能更快定位吗”如果没有监控和日志你就等于蒙着眼睛修飞机能不能修好全凭运气。基础的可观测性三件套是 Metrics指标、Logging日志、Tracing链路追踪。指标解决“系统现在怎么样”——CPU、内存、QPS、错误率、P99 延迟这些都要有面板和告警日志解决“到底发生了什么”——每个服务的日志要集中采集便于按 traceId 串联链路追踪解决“请求在哪个环节慢了”——就是前面反复提到的调用链。这套体系不是一蹴而就的但每落地一块事故的处置效率就会有肉眼可见的提升。我现在养成了一个习惯新服务上线之前必须回答“它有没有接指标采集、有没有打关键日志、有没有接入链路追踪”这三个问题否则视为不合格。告警规则是另一个容易被忽视的重灾区。告警太多等于没有告警——我见过有的团队一天响几百条后来干脆所有人都把群屏蔽了。好的告警应该符合三个原则可触发、有意义、有响应预案。宁可只设五个核心告警也不要设五十个杂音告警。另外告警一定要带上“谁能处理”的联系人和“大致怎么看”的提示不然半夜叫醒一个刚入职的新人他只会对着屏幕发呆。4.5 故障演练与应急预案用确定的演练对抗不确定的故障最后一块拼图是故障演练。我知道很多人觉得背预案文档是形式主义但真正的价值不在于“背”而在于“练”。只有演练过你才会发现预案里有哪一步写得不清不楚、哪个脚本根本跑不通、哪个负责人已经离职了。我组织过一次混沌工程演练把其中一个核心数据库节点直接断网结果现场乱成一团原因很简单——自动切换脚本里依赖了一个内网域名的解析而那个域名所在的机器也在同一台故障机架上。这个隐患如果不在演练中暴露真出事的时候会直接变成大事故。演练从最简单的开始杀掉一台应用服务器看流量是否自动摘除重启数据库备机看主从同步是否正常恢复配置中心或者注册中心挂一台看服务是否仍然可用。每做完一个演练更新一次应急预案。执行同样动作之前请一定确保是演练模式而不是真正的生产流量——我听说过有团队把演练脚本写错在白天高峰期真的把生产节点全部重启了的事故。加上“演练必须通过审批、在维护窗口期内、有回滚方案”这三条铁律基本就能把演练本身的风险降到最低。5. 事故复盘的正确姿势不只是“谁的责任”更是“系统的进化”事故处理完真正的价值才刚刚开始——复盘。可惜大多数团队把复盘开成了“批斗会”或者“免责会”。我理想的复盘框架是整理时间线确认每步操作是有效还是无效找出根因和次生问题产出具体的、可验证的整改项明确负责人和截止时间然后跟踪闭环。有一个好用的衡量标准复盘会的产出不是一份纪要而是接下来三周内的行动列表否则这场复盘会就是浪费时间。时间线上我觉得最有价值的是记录“无效动作”——比如有人执行了一个查询命令但拿到的数据根本没帮助到判断这类动作通常说明监控面板缺了一项关键指标或者被查询的文档没有覆盖场景。把这些无效动作变成改进监控、改进文档的具体工单就是实打实的系统性进步。反过来如果只是把责任归结到某个同事“操作失误”那同一类事故换个场景还会再发生。复盘还有一个容易漏掉的产出是“应急手册更新”。新遇到的一种故障模式、一条更好用的命令、一个更快的判断口径都应该沉淀到团队的知识库里。我在团队里推行了一个简单的约定每次事故处理完当班人员必须更新一份“事故速查单”哪怕只增加一行“遇到 XX 先执行 XX”。攒上一年这份速查单就是最贴合团队实际情况的作战手册比任何外部教程都管用。6. 最后分享一点个人心得写了这么多最后说点掏心窝的话。生产事故这种事没人喜欢但它就是运维工作的一部分。我见过太多同行因为一次事故就变得畏手畏脚——不敢发布、不敢动配置、把所有变更都拖到“万无一失”——这种心态反而更容易制造出“因噎废食”式的次生事故。我的体会是每一次事故都是在给系统“验伤”处理一次事故你对系统运行机制的理解就会深一层团队的协同节奏也会更拧紧一分。还有一个经验想分享给刚入行的朋友处理事故切忌单打独斗。擅自动作之前先把你看到的现象和想做的下一步发到群里让大家知道你在做什么。很多时候你以为自己在“救火”但另一个同事正在做相反的操作两个人互相把对方的动作推翻白白浪费窗口时间。配合和沟通在生产事故面前同样是“保命技能”。如果这篇白皮书能让你在下次事故现场少慌一秒、快一分钟那这些熬过的夜就没白熬。