生产 3:监控(Prometheus / Grafana / Tracing)
谁该读这一篇? 要把 SGLang 接入监控体系、定 dashboards / alerts 的 SRE。 前置阅读:
02-kubernetes.md。 耗时: 35 分钟 学完能: 1. 列出 SGLang 必看的"黄金 5 指标"; 2. 写一份能直接抄的 Grafana 面板 JSON; 3. 写一份 Prometheus 告警规则; 4. 接入 OpenTelemetry trace; 5. 知道哪些指标对应业务什么含义。
1. 黄金 5 指标
| 指标 | PromQL 模板 | 看什么 |
|---|---|---|
| TTFT p99 | histogram_quantile(0.99, rate(sglang_e2e_first_token_latency_seconds_bucket[5m])) |
用户感知"卡多久才开始打字" |
| TPOT p99 | histogram_quantile(0.99, rate(sglang_inter_token_latency_seconds_bucket[5m])) |
用户感知"打字速度" |
| Throughput | rate(sglang_generation_tokens_total[1m]) |
单副本 token/s |
| Cache hit rate | sglang_cache_hit_rate 或 derived |
前缀命中率 |
| Queue depth | sglang_num_queue_reqs |
排队长度 |
任何 dashboard 至少要有这 5 张图。
2. SGLang 暴露的指标速查
curl localhost:30000/metrics | grep "^sglang:" | head -30
主要类别:
# 请求级别
sglang:e2e_request_latency_seconds_bucket
sglang:e2e_first_token_latency_seconds_bucket
sglang:inter_token_latency_seconds_bucket
# 计数器
sglang:num_requests_total
sglang:num_input_tokens_total
sglang:num_output_tokens_total
sglang:num_aborted_requests_total
# 状态
sglang:num_running_reqs
sglang:num_queue_reqs
sglang:num_used_tokens
sglang:cache_hit_rate
sglang:gen_throughput
# 调度器
sglang:scheduler_idle_time
sglang:prefill_tokens_per_iter
sglang:decode_batch_size
# 投机解码(如开)
sglang:spec_accept_rate
sglang:spec_avg_accepted_tokens
# MoE(如适用)
sglang:moe_expert_load
sglang:moe_imbalance_ratio
sglang:moe_alltoall_latency_ms
源码:srt/observability/ 和 srt/metrics/。
3. Prometheus 抓取配置
# prometheus.yml
scrape_configs:
- job_name: sglang
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
static_configs:
- targets:
- sglang-7b.llm.svc.cluster.local:30000
labels:
model: qwen-7b
gpu: h100
K8s 用 Operator 的 ServiceMonitor(02-kubernetes.md §10)。
4. 告警规则(抄走)
# alerts.yml
groups:
- name: sglang
rules:
- alert: SGLangTTFTHigh
expr: histogram_quantile(0.99, rate(sglang_e2e_first_token_latency_seconds_bucket[5m])) > 1
for: 2m
labels: {severity: warning}
annotations:
summary: "SGLang TTFT p99 > 1s on {{ $labels.instance }}"
- alert: SGLangTPOTHigh
expr: histogram_quantile(0.99, rate(sglang_inter_token_latency_seconds_bucket[5m])) > 0.1
for: 5m
labels: {severity: warning}
annotations:
summary: "SGLang TPOT p99 > 100ms on {{ $labels.instance }}"
- alert: SGLangCacheHitDrop
expr: avg_over_time(sglang_cache_hit_rate[10m]) < 0.2
for: 10m
labels: {severity: warning}
annotations:
summary: "SGLang cache hit rate dropped < 20%"
- alert: SGLangQueueBuildup
expr: avg_over_time(sglang_num_queue_reqs[5m]) > 100
for: 5m
labels: {severity: critical}
annotations:
summary: "SGLang queue depth > 100 sustained — capacity?"
- alert: SGLangAbortRateHigh
expr: rate(sglang_num_aborted_requests_total[5m]) > 1
for: 5m
labels: {severity: critical}
annotations:
summary: "SGLang aborting requests on {{ $labels.instance }}"
- alert: SGLangKVPoolFull
expr: sglang_num_used_tokens / sglang_max_total_num_tokens > 0.95
for: 5m
labels: {severity: warning}
annotations:
summary: "KV pool > 95% used — evict storm likely"
- alert: SGLangDown
expr: up{job="sglang"} == 0
for: 1m
labels: {severity: page}
annotations:
summary: "SGLang instance {{ $labels.instance }} is DOWN"
- alert: SGLangSpecAcceptRateLow
expr: avg_over_time(sglang_spec_accept_rate[10m]) < 0.5
for: 15m
labels: {severity: info}
annotations:
summary: "Spec decoding accept rate < 0.5 — negative optimization?"
5. Grafana 面板骨架
最关键的 8 个 panel:
| Panel | 类型 | PromQL |
|---|---|---|
| TTFT p50/p99 | Time series | 见 §1 |
| TPOT p50/p99 | Time series | 见 §1 |
| Throughput | Time series | rate(sglang_generation_tokens_total[1m]) |
| Cache hit rate | Stat | sglang_cache_hit_rate |
| Running / Queue | Time series | sglang_num_running_reqs / sglang_num_queue_reqs |
| KV pool used | Gauge | sglang_num_used_tokens / sglang_max_total_num_tokens |
| Spec accept rate | Time series | sglang_spec_accept_rate (if applicable) |
| Error rate | Stat | rate(sglang_num_aborted_requests_total[5m]) |
可以用 Grafana 自带 LLM dashboards(社区有 SGLang 模板)作为起点,按业务调整。
6. SLO 仪表板
业务侧关心的 SLI:
sli_ttft_p99_seconds = histogram_quantile(0.99, rate(sglang_e2e_first_token_latency_seconds_bucket[1h]))
sli_tpot_p99_seconds = histogram_quantile(0.99, rate(sglang_inter_token_latency_seconds_bucket[1h]))
sli_success_ratio = (1 - rate(sglang_num_aborted_requests_total[1h]) / rate(sglang_num_requests_total[1h]))
SLO 目标:
TTFT p99 < 500 ms 目标 99% 时间满足
TPOT p99 < 50 ms 目标 99.5% 时间满足
Success > 99.9%
错误预算 = (1 - SLO_target) × 时间窗口。 错误预算用光 → 暂停新功能上线、专攻稳定性。
7. 日志聚合
SGLang 日志 → stdout → Docker / K8s 收集 → Loki / ELK。 关键日志事件:
[INFO] Prefill batch.../Decode batch...周期性[WARNING] ... preempt ...[ERROR] ... OOM .../... NCCL ...[INFO] Application startup complete
可以用 Loki LogQL 查关键事件:
{namespace="llm",app="sglang-7b"} |~ "preempt|OOM|abort|hang"
8. 分布式追踪(OpenTelemetry)
SGLang 支持 OTel:
--otlp-endpoint http://otel-collector:4317 \
--otlp-traces-sampler-arg 0.01 # 1% 采样
每个请求会生成一条 trace,包含:
- TM tokenize span
- Scheduler match_prefix span
- Scheduler run_batch span
- Detokenize span
接 Jaeger / Tempo 看时间分布。
业务把自己的 trace_id 通过 header 传过来,全链路串起来:
client trace_id ──HTTP header──▶ SGLang
├── 父 span: 客户端 trace_id
└── 子 spans: 内部各阶段
9. 自定义指标
在 srt/observability/ 加:
from prometheus_client import Counter, Histogram
my_counter = Counter("sglang_custom_event_total", "Custom events", ["type"])
my_hist = Histogram("sglang_custom_latency_seconds", "Custom latency")
# 在合适位置调用
my_counter.labels(type="cache_evict").inc()
with my_hist.time():
do_something()
/metrics 端点自动暴露。
10. 监控阅读"工作流"
每天值班的固定动作:
- 看 SLO 面板(TTFT / TPOT / Success)—— 红就深入。
- 看 Throughput / Cache hit rate / Queue —— 找趋势变化。
- 看 Top alerts —— 处理或抑制。
- 周报:错误预算 / 容量 / 异常事件。
不要追着每个 spike,关注趋势 + p99。
11. 小结
- 黄金 5 指标:TTFT / TPOT / Throughput / Cache hit / Queue。
- Prometheus 抓
/metrics,告警规则按上面抄。 - Grafana 面板 8 个 panel 是基础。
- OpenTelemetry 让全链路 trace 可达。
- 业务 SLI/SLO 单独建面板,错误预算管理上线节奏。
12. 自检
- TTFT 突增但 TPOT 平稳,可能原因?
答案
TTFT 反映 prefill + 排队,TPOT 反映 decode 步时。TPOT 不变说明 GPU forward 性能正常。 TTFT 突增原因: (1) **排队**:waiting_queue_size 涨——流量突增 / 容量不够。 (2) **prompt 突然变长**:业务侧改 system prompt / RAG 带更多 context;prefill 算力翻倍。 (3) **cache_hit_rate 跌**:突然命中率从 60% → 10%,每请求都从零 prefill。 (4) **chunked prefill 触发**:长 prompt 被切多轮,TTFT 累计多步。 (5) **新流量进未 warmup 副本**:K8s 扩容新 Pod 还没沉淀 cache。 优先查 (1) 和 (3)。- Cache hit rate 跌到 0.1,先看哪个指标?
答案
按优先级: (1) **`sglang_num_used_tokens` / `sglang_max_total_num_tokens`**:KV pool 是否 95%+,evict 频繁;是的话 hit rate 跌是因为 cache 留不住。 (2) **业务方变更**:问业务有没改 prompt 模板(最常见,加 timestamp / random salt)。 (3) **router 路由策略**:是否从 cache-aware 切到 round-robin / 重启了 router。 (4) **新业务上线**:新 prompt 模板还没 warmup。 (5) **`/flush_cache` 误调用**:管理 API 被误触。 先看 (1) 排除显存压力,再排查 (2) (3)。- 错误预算用尽时该怎么办?
答案
按 Google SRE 规范分级处理: - **用尽 50%**:Yellow,关注但仍正常发布。 - **用尽 100%(错误预算耗尽)**:Red,**冻结新功能上线**,所有变更需对外解释、必须是降低错误率的修复;不允许新增风险。 - **持续超额**:升级到 OKR / 产品决策层面,可能要(a) 调 SLO(更宽松,承认能力上限);(b) 加投资(更多副本 / 更好硬件);(c) 砍业务(降需求)。 预算耗尽不是"系统挂了",是"承诺与现实差距过大"的信号;要么改承诺要么投资改进。- OTel 1% 采样会丢什么信息?
答案
1% 头部采样的影响: (1) **长尾不可见**:p99 / p999 由 1% 中的 1%(万分之一)支撑,1% 采样后 p99 完全 unreliable。 (2) **罕见事件遗漏**:每天才几次的特定 tool call 失败、特定模型 OOM 等,可能根本采不到。 (3) **关联请求断链**:业务有 multi-step agent,一步采样到、下一步没采到,trace 链条断。 (4) **错误请求采样率不偏**:错误请求占比 < 1% 时几乎不会被采到,看不到错误 trace。 优化:**tail-based sampling**——先全采,根据响应结果决定是否保留(错误 / 慢请求 100% 留,正常请求 1% 留);OTel Collector 支持。- KV pool 95% 是不是该立刻扩容?
答案
不一定,先看是不是稳态。 95% **稳态**:实际就是 sweet spot——KV pool 充分利用,cache 沉淀好,hit rate 高。这是健康的"满载",不需要扩容(扩反而降利用率)。 95% **持续涨 + waiting_queue 涨**:才是真过载,evict 频繁、新请求等不到 slot;扩容。 95% **突发**:可能业务突增 / 长 prompt 涌入,看后续趋势再决定。 判断口诀:**KV pool 利用率 + waiting queue 长度 一起看**。pool 满 + queue 短 = 健康;pool 满 + queue 长 = 过载。13. 下一步
04-slo.md— SLO / 容量公式。05-incident-playbook.md— 故障 runbook。06-cost-tuning.md— 成本和监控联系。
上游源码:
sglang/python/sglang/srt/observability/、sglang/python/sglang/srt/metrics/。