生产 5:事故 Runbook(8 类典型故障)
谁该读这一篇? 值班 SRE / 接 oncall 的工程师 —— 当 SGLang 出问题时一页 cheatsheet。 前置阅读:
03-monitoring.md、07-hands-on/03-debug-scheduler.md。 耗时: 30 分钟(精读 + bookmark) 学完能: 1. 识别 8 类常见 SGLang 故障的指纹; 2. 走标准 runbook 不慌; 3. 区分症状级修复 vs 根因修复; 4. 完成事故后写 postmortem。
1. 故障 1:KV 抢占级联(Preempt Storm)
症状:
- TPOT p99 突增到秒级。
- 日志大量 "[WARNING] preempt"。
kv_pool_used接近 100%。- 部分请求被 abort。
根因:
- 业务流量突增,prompt 超长。
- KV pool 装不下,scheduler 抢占 in-flight 请求腾空间。
- 抢占的请求重新加入队列,加剧紧张。
应急:
- 立刻降级:减
--max-running-requests限制并发。 - 拒绝超长 prompt:在 router 层加 prompt 长度限制。
- 扩容副本(如有 KEDA)。
根因修复:
- 评估业务真实 prompt 分布,调整
--max-total-tokens或换更大显存。 - 开 chunked prefill(默认开),避免单 prompt 占满。
- 考虑 P/D 分离。
2. 故障 2:NCCL Hang
症状:
- 多 TP 启动卡在 "Init NCCL begin",不退出。
- 或者跑起来后某个 forward 之后所有进程 hang,GPU util 卡 100%。
根因:
- 节点间网络丢包 / 防火墙阻断。
- 一个 rank crash 而其它 rank 还在 wait collectives。
- NCCL 版本和 driver 不匹配。
应急:
nvidia-smi topo -m看拓扑是否完整。NCCL_DEBUG=INFO重启抓详细日志。- 杀掉所有 sglang 进程,重启。
根因修复:
- 升级 NCCL(match CUDA major version)。
- 网络 MTU / 防火墙规则检查。
- 加 watchdog 自动检测 hang(NCCL_ASYNC_ERROR_HANDLING=1)。
3. 故障 3:GPU OOM
症状:
- 启动报
CUDA out of memory。 - 或运行中突然 OOM crash。
根因:
--mem-fraction-static太高,留不下 workspace。- KV pool 估算和实际不符(如 LoRA、spec decoding 额外开销)。
- CUDA Graph 占用没算进去。
应急:
- 降
--mem-fraction-static0.85 → 0.7。 - 减
--cuda-graph-bs。 - 关掉非必要功能(spec decoding、LoRA)。
根因修复:
- 业务 prompt + output 长度估算。
- 升级 SGLang(新版本 memory 估算更准)。
- 加 OOM 监控告警。
4. 故障 4:客户端重试雪崩
症状:
- 客户端大量超时后重试,server QPS 翻倍。
- waiting_queue_size 指数涨。
- 形成正反馈雪崩。
根因:
- 上游临时延迟(cache miss 突增)。
- 客户端无退避策略。
应急:
- router 加严格 max concurrency, 超过返回 503。
- 业务侧立刻关闭重试 / 加指数退避。
- 紧急扩容副本。
根因修复:
- 客户端永久加退避(jitter + cap)。
- 加 circuit breaker。
- Router 做请求级超时控制。
5. 故障 5:Prefix Cache 命中率塌方
症状:
cache_hit_rate从 60% 跌到 5%。- TTFT 翻倍。
- Throughput 降。
根因(按概率):
- 业务上线变更(system prompt 改了 / 加了 timestamp 让 prompt 唯一)。
- KV pool 太紧,evict 太快。
- 新流量进来稀释命中。
- RadixCache 出 bug(罕见)。
应急:
- 找业务侧确认 prompt 是否变化。
- 临时
--flush-cache重新热。 - 增加 KV pool 容量。
根因修复:
- system prompt 去掉变化字段(时间戳、用户 ID)。
- 加 cache 监控 + 告警。
- 业务上线 review 加 cache 影响评估。
6. 故障 6:冷启动延迟(扩容慢)
症状:
- 流量突增,HPA 扩了新副本但 ready 慢(5-10 分钟)。
- 期间老副本扛爆。
根因:
- 模型加载慢(70B+ 几分钟)。
- CUDA Graph 捕获慢(30-60s)。
- 镜像拉取慢。
应急:
- 手动从预热池提副本。
- 短期 over-provision(HPA min 设高)。
根因修复:
- 模型 ramdisk / NFS prefetch。
- 减
--cuda-graph-bs个数。 - 用预热好的镜像 / snapshot。
7. 故障 7:输出质量异常
症状:
- 用户反馈"答案变蠢了"。
- 业务 eval 分数下降。
- 没有明显的服务器异常。
根因:
- 模型权重被错误更新。
- 量化版本切换(fp8 vs bf16)。
- Tokenizer 不匹配(升级后 vocab 变了)。
- 受约束解码 grammar 缺陷。
应急:
- 立刻回滚到上一稳定版本(K8s rollback)。
- 跑业务 eval 验证。
根因修复:
- 上线前必跑 eval(CI)。
- 模型版本灰度发布。
- Tokenizer / model 配对锁。
8. 故障 8:LoRA Adapter 抖动
症状:
- 启用 LoRA 后 TPOT 抖动剧烈。
- CUDA Graph fallback 频繁。
- 某些 adapter 输出错乱。
根因:
- LoRA 切换打破 CUDA Graph。
- adapter 数量超出 cache 大小。
- adapter 权重未正确加载。
应急:
- 减并发的 adapter 数量。
- 不用 LoRA 时
--disable-lora。
根因修复:
- Pin adapter(不 evict 热点)。
- 用 LoRA-friendly attention backend。
- 业务侧合并 adapter 数量。
9. 通用排障流程
flowchart TD
A[报警 / 用户反馈] --> B{症状归类}
B -->|时延高| C[看 TTFT/TPOT/queue/cache]
B -->|失败率高| D[看 abort/OOM/HTTP 5xx]
B -->|质量差| E[业务 eval + 版本对比]
C --> F[根因?]
D --> F
E --> F
F -->|确定| G[根因修复 PR]
F -->|临时| H[症状抑制 + 监控]
H --> I[postmortem 计划长期修]
G --> J[写 postmortem]
I --> J
10. Postmortem 模板
# Incident: <title>
## Summary
- Time: 2026-05-29 14:00 - 15:30 UTC
- Impact: 30% chat QPS failed, p99 TTFT 5s
- Severity: SEV-2
## Timeline
- 14:00 alert fired (TTFT high)
- 14:05 oncall ack
- 14:15 identified KV pool full
- 14:20 mitigation: reduce max_running_requests
- 14:40 added capacity (2 replicas)
- 15:30 resolved
## Root Cause
业务上线时新 prompt 模板加入 `{timestamp}` 字段,cache_hit_rate 从 60% 降到 5%,
KV pool 装不下急速 evict, 触发抢占级联。
## What Went Well
- Alert 准确
- Oncall 5 分钟 ack
## What Went Wrong
- 业务上线无 cache impact review
- Postmortem 工具记录不全
## Action Items
- [ ] 业务上线模板加 cache hit 验证步骤 (owner: bob, due: 2026-06-05)
- [ ] 加 cache_hit_rate < 30% 告警 (owner: alice, due: 2026-06-01)
- [ ] postmortem 工具 review (owner: ...)
11. 小结
- 8 类典型故障:抢占、NCCL hang、OOM、重试雪崩、cache 塌方、冷启动、质量异常、LoRA 抖动。
- 应急三步:症状识别 → 临时缓解 → 根因修复。
- 通用流程:报警 → 归类 → 看指标 → 根因 → 修复 + postmortem。
- Postmortem 不甩锅,关注系统性改进。
12. 自检
- cache_hit_rate 塌方先看业务还是先看 SGLang 本身?
答案
**先问业务方**。生产事故里 cache 塌方 80% 是业务侧改 prompt 模板(加 timestamp / session_id / user_id 等 可变字段)。问业务方"最近 24 小时改没改 prompt?"5 秒能确认。 业务说没改才看 SGLang:(a) router 路由策略是否变(cache-aware → round-robin?);(b) KV pool 满 evict 频繁;(c) RadixCache 版本 bug(罕见)。 定位顺序优先级 = 解决速度优先级。业务变更复原快、SGLang bug 复原慢。- NCCL hang 时怎么快速定位是哪个 rank 卡?
答案
(1) **`py-spy dump --pid- 重试雪崩为什么不能只靠扩容解?
答案
雪崩是**正反馈循环**:服务慢 → 客户端超时 → 重试 → server 收到双倍流量 → 更慢 → 更多重试 → 直至崩。 扩容只解决"基础容量不够",解不了正反馈:扩容期间(5-10 分钟)流量已经 N 倍涨,新副本上线时已经全部过载,扩容意义不大。 正确解法: (1) **打断反馈**:router 加 max concurrency,超出立刻 503;客户端立刻看到失败而不是 timeout。 (2) **客户端退避**:指数退避 + jitter,配 circuit breaker。 (3) **降级**:返回 cache 答案 / 默认响应 / 排队页面。 (4) **限流 token bucket**:每 tenant 严格配额。 扩容是辅助,不是主线。- LoRA 跑慢的诊断步骤?
答案
(1) **看 `lora_swap_count`**:高频 swap → adapter cache 不够,热 adapter 装不下;调大 `--max-loras-per-batch` 或减并发 adapter 数。 (2) **看 `cuda_graph_fallback`**:LoRA 切换打破 graph,fallback 多 → TPOT 涨;考虑分流量到独立 LoRA 副本。 (3) **看 `lora_per_request_overhead_ms`**:单请求 LoRA 路径开销;> 5ms 异常,可能 LoRA rank 太大或 kernel 没用 Punica/S-LoRA。 (4) **看 `lora_active_count`**:active 太多(> 50)时 cache miss 严重;用 router sticky 按 adapter 路由到同副本。 (5) **无 LoRA 对比**:临时 `--disable-lora` 跑同流量,看时延差距;如果 5-15% 是预期;> 30% 是 bug 或配置错。- 输出质量下降第一时间该做什么?
答案
(1) **立刻回滚到上一稳定版本**(K8s rollback)—— 先恢复用户体验,再分析。 (2) **抢救一份"现场"**:dump 当前正在运行的 server 配置、版本、最近变更 git log;故障 prompt sample 保留。 (3) **跑业务 eval 对比**:rollback 后再用 same eval set 跑新版本看是否 regression。 (4) **检查模型版本**:是不是误推了 fine-tune checkpoint?量化版本切了?tokenizer 升级? (5) **检查受约束输出 grammar**:grammar 版本 / xgrammar lib 升级导致 mask 错误? (6) **检查 sampling 参数**:是否 default sampling 参数被改(如 temperature 全局变了)? (7) **postmortem**:根因找清楚,加 CI eval 防 regression 再上线。 原则:**先恢复,后分析**。质量问题用户最敏感,不能边修边让生产受影响。13. 下一步
06-cost-tuning.md— 成本管理。07-router.md— Router 配合。04-slo.md— 错误预算视角。
参考:Google SRE Book Ch. Effective Troubleshooting + Postmortem Culture。