预计阅读 8 分钟

生产 5:事故 Runbook(8 类典型故障)

谁该读这一篇? 值班 SRE / 接 oncall 的工程师 —— 当 SGLang 出问题时一页 cheatsheet。 前置阅读: 03-monitoring.md07-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 请求腾空间。
  • 抢占的请求重新加入队列,加剧紧张。

应急

  1. 立刻降级:减 --max-running-requests 限制并发。
  2. 拒绝超长 prompt:在 router 层加 prompt 长度限制。
  3. 扩容副本(如有 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 不匹配。

应急

  1. nvidia-smi topo -m 看拓扑是否完整。
  2. NCCL_DEBUG=INFO 重启抓详细日志。
  3. 杀掉所有 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 占用没算进去。

应急

  1. --mem-fraction-static 0.85 → 0.7。
  2. --cuda-graph-bs
  3. 关掉非必要功能(spec decoding、LoRA)。

根因修复

  • 业务 prompt + output 长度估算。
  • 升级 SGLang(新版本 memory 估算更准)。
  • 加 OOM 监控告警。

4. 故障 4:客户端重试雪崩

症状

  • 客户端大量超时后重试,server QPS 翻倍。
  • waiting_queue_size 指数涨。
  • 形成正反馈雪崩。

根因

  • 上游临时延迟(cache miss 突增)。
  • 客户端无退避策略。

应急

  1. router 加严格 max concurrency, 超过返回 503。
  2. 业务侧立刻关闭重试 / 加指数退避。
  3. 紧急扩容副本。

根因修复

  • 客户端永久加退避(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(罕见)。

应急

  1. 找业务侧确认 prompt 是否变化。
  2. 临时 --flush-cache 重新热。
  3. 增加 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 缺陷。

应急

  1. 立刻回滚到上一稳定版本(K8s rollback)。
  2. 跑业务 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. 自检

  1. 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 复原慢。
  1. NCCL hang 时怎么快速定位是哪个 rank 卡?
答案 (1) **`py-spy dump --pid `** 看每个 rank 的 Python 栈,卡在 `dist.all_reduce` 或 `ncclAllReduce` 的就是 hang。 (2) **`nvidia-smi`** 看每张 GPU 的 util 和 fan / temp,hang 的卡通常 util 100% 但温度不涨(kernel 在等通信)。 (3) **NCCL_DEBUG=INFO** 重启后看日志里最后一个 `NCCL INFO ...` 是哪个 rank、哪个 collective op。 (4) **`/var/log/nvidia/nccl-*.log`**(如开启)里有详细 collective trace。 (5) **网络抓包**:跨节点 NCCL 走 IB,用 `ibstat` 看链路;走 socket 用 `tcpdump` 看丢包。 定位 rank 后通常重启那个 Pod(K8s 自动 recreate),但根因(网络抖动 / Pod evict / driver bug)要单独修。
  1. 重试雪崩为什么不能只靠扩容解?
答案 雪崩是**正反馈循环**:服务慢 → 客户端超时 → 重试 → server 收到双倍流量 → 更慢 → 更多重试 → 直至崩。 扩容只解决"基础容量不够",解不了正反馈:扩容期间(5-10 分钟)流量已经 N 倍涨,新副本上线时已经全部过载,扩容意义不大。 正确解法: (1) **打断反馈**:router 加 max concurrency,超出立刻 503;客户端立刻看到失败而不是 timeout。 (2) **客户端退避**:指数退避 + jitter,配 circuit breaker。 (3) **降级**:返回 cache 答案 / 默认响应 / 排队页面。 (4) **限流 token bucket**:每 tenant 严格配额。 扩容是辅助,不是主线。
  1. 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. 输出质量下降第一时间该做什么?
答案 (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. 下一步

参考:Google SRE Book Ch. Effective Troubleshooting + Postmortem Culture。