跳到主要内容
推理优化

10.2 可观测性:Prometheus 指标、Grafana 看板与日志

vLLM v0.26 的 Prometheus 指标体系(请求时延/调度/KV缓存/投机解码)、Grafana 看板、结构化日志与告警设计

PrometheusGrafana可观测性指标日志告警

可观测性是运维的眼睛。LLM 推理服务与普通 Web 服务最大的不同:瓶颈不在 CPU/内存,而在 GPU 利用率、KV Cache 水位和队列深度。vLLM v0.26 的指标体系围绕这三个维度设计——这一节讲清”看什么指标、怎么看、怎么告警”。

📑 目录


1. 指标架构:v0.26 的 Rust 指标引擎

1.1 从 Python 到 Rust

v0.26 把指标采集从 Python 迁移到了 Rust 引擎核心(rust/src/metrics),通过 prometheus-client crate 注册。这也是 v0.26 整体”引擎 Rust 化”的一部分(第 4 章的 Tokenizer、第 6 章的调度器同理)。

服务端 /metrics 端点(Prometheus 文本格式,OpenMetrics)
  ├── RequestMetrics    → 请求级时延、token 数(e2e、TTFT、TPOT 等)
  ├── SchedulerMetrics  → 队列、KV 缓存、前缀缓存、抢占
  └── ApiServerMetrics  → HTTP 层(仍在 Python 侧)

旧 Python 指标(vllm:xxx 命名)→ 迁移中,v0.26 以 vllm: 前缀(冒号)为新命名

📌 关键点:v0.26 指标名采用 vllm: 冒号前缀(如 vllm:time_to_first_token_seconds),与旧版 vllm_time_to_first_token_seconds(下划线)不同——查文档、抄 PromQL 时先确认版本对应的命名。

1.2 开启方式

# serve 时默认开启(--enable-prometheus-metrics 默认 true)
vllm serve meta-llama/Llama-3.1-8B-Instruct --port 8000

# 验证
curl localhost:8000/metrics | head -20
# Prometheus 抓取配置(K8s ServiceMonitor)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: vllm
spec:
  selector: { matchLabels: { app: vllm } }
  endpoints:
  - port: metrics          # 8000/metrics
    interval: 15s
    scrapeTimeout: 10s

2. 核心指标解读(按用途分组)

2.1 请求时延组(SLO 的直接来源)

指标类型含义
vllm:time_to_first_token_secondsHistogramTTFT(首 token 时延)——第 9.1 章 SLO 的两个指标之一
vllm:e2e_request_latency_secondsHistogram端到端请求时延
vllm:inter_token_latency_secondsHistogramITL(相邻 token 间隔)
vllm:request_time_per_output_token_secondsHistogram每输出 token 耗时(吞吐的反面)
vllm:request_queue_time_secondsHistogram排队等待时间(调度压力的直接体现)
vllm:request_prefill_time_secondsHistogramPrefill 阶段耗时
vllm:request_decode_time_secondsHistogramDecode 阶段耗时

2.2 调度与队列组(扩缩容的输入)

指标类型含义
vllm:num_requests_runningGauge正在运行的请求数
vllm:num_requests_waitingGauge等待中的请求数
vllm:num_requests_waiting_by_reasonGauge等待原因细分(KV 空间不足?抢占?)
vllm:num_preemptionsCounter累计抢占次数(>0 说明负载已超容)

2.3 KV 缓存组(容量瓶颈)

指标类型含义
vllm:kv_cache_usage_percGaugeKV 缓存占用比例(0-1)——最重要的容量指标
vllm:kv_block_lifetime_secondsHistogramKV block 存活时长
vllm:kv_block_reuse_gap_secondsHistogramblock 复用的间隔

2.4 前缀缓存组(缓存命中率)

指标类型含义
vllm:prefix_cache_queriesCounter前缀缓存查询次数(token 数)
vllm:prefix_cache_hitsCounter命中 token 数
vllm:external_prefix_cache_hits/queriesCounter外部 KV 连接器(跨实例共享缓存)的命中/查询
vllm:prompt_tokens_cachedCounter缓存命中的 prompt token 数
vllm:prompt_tokens_by_sourceCounterprompt token 来源分布(命中/未命中)

2.5 吞吐与投机解码组

指标类型含义
vllm:generation_tokensCounter生成 token 总数(算吞吐的分母)
vllm:prompt_tokensCounterprompt token 总数
vllm:spec_decode_num_accepted_tokensCounter投机解码接受 token 数
vllm:spec_decode_num_draft_tokensCounter草稿 token 数
vllm:spec_decode_num_draftsCounter草稿次数

2.6 健康组(异常检测)

指标类型含义
vllm:request_successCounter成功请求数(失败率的分母参照)
vllm:corrupted_requestsCounterlogits 出现 NaN 的请求数(模型出问题的信号

📌 关键点运维只盯 4 个指标就能覆盖 80% 场景num_requests_waiting(过载?)、kv_cache_usage_perc(容量?)、time_to_first_token_seconds 的 p95(SLO 达标?)、num_preemptions(抢占风暴?)。其余指标是排查时的下钻入口。


3. Grafana 看板与 PromQL 实战

3.1 核心 PromQL 查询

# 1. TTFT 分位数(SLO 达标判断)
histogram_quantile(0.95, sum by (le) (rate(vllm:time_to_first_token_seconds_bucket[5m])))

# 2. 前缀缓存命中率(5 分钟窗口)
rate(vllm:prefix_cache_hits[5m]) / rate(vllm:prefix_cache_queries[5m])

# 3. 过载判定:等待/运行比
vllm:num_requests_waiting / (vllm:num_requests_running + vllm:num_requests_waiting)

# 4. KV 缓存水位
vllm:kv_cache_usage_perc

# 5. 吞吐(token/s)
rate(vllm:generation_tokens[1m])

3.2 看板三件套

看板内容用途
总览请求量、TTFT p50/p95、吞吐、队列值班大屏
容量KV 缓存水位、GPU 利用率、等待原因扩容决策
缓存前缀命中率、spec decode 接受率优化效果验证

💡 提示:Production Stack(10.1 第 5 节)自带全套 Grafana 看板(vLLM + LMCache),grafanaDashboards.enabled: true 即开——先用官方看板,再按需定制,不要从零画。


4. 结构化日志与请求追踪

4.1 结构化日志

vLLM 的日志走标准库,生产用 JSON 结构化输出方便采集:

# 环境变量开启(示例,以官方文档为准)
export VLLM_LOGGING_LEVEL=INFO
{"timestamp":"...", "level":"INFO", "msg":"Received request ...", "request_id":"...", "model":"..."}
  • 日志采集:Promtail/Fluent Bit → Loki(轻量)或 ELK(重型)
  • 日志级别:INFO(默认)/ DEBUG(排障)/ WARNING / ERROR

4.2 请求 ID 贯穿

vLLM 每个请求有唯一 request_id指标(按 request_id 打点)+ 日志(打印 request_id)配合才能做单请求的时延拆解:

一次慢请求的排查链路:
  1. 告警发现 TTFT p95 恶化
  2. 日志按 request_id 查该请求的 queue_time / prefill_time / decode_time
  3. 指标看同窗口 kv_cache_usage_perc 是否打满 → 定位是容量还是代码问题

📌 关键点可观测性的终点是”单请求可追踪”。没有 request_id 贯穿,时延恶化只能猜;有了它,10 分钟内能定位到”排队慢 / prefill 慢 / decode 慢 / 重试导致”的哪一环。


5. 告警设计:什么该报警,什么不该

5.1 告警四原则

原则说明反例
告警 = 需要人处理的事件可自动恢复的别报警单次 TTFT 超时
用比率,不用绝对值绝对值随负载波动waiting > 100 报警
SLO 先行告警与 SLO 绑定,而非指标绑定指标波动报警
预留恢复期持续 N 分钟才触发,避免抖动立即报警

5.2 vLLM 生产告警清单(推荐起点)

# 1. 容量:KV 缓存打满持续 5 分钟 → 扩容/降负载(P1)
vllm:kv_cache_usage_perc > 0.95

# 2. 过载:等待/运行比 > 2 持续 5 分钟(P1,说明排队失控)
vllm:num_requests_waiting / (vllm:num_requests_running + vllm:num_requests_waiting) > 0.67

# 3. SLO:TTFT p95 超阈值持续 10 分钟(P2)
histogram_quantile(0.95, sum by (le) (rate(vllm:time_to_first_token_seconds_bucket[5m]))) > 2

# 4. 抢占:抢占速率 > 0 持续 5 分钟(P2,容量预警)
rate(vllm:num_preemptions[5m]) > 0

# 5. 健康:corrupted_requests 增长(P1,模型输出异常)
increase(vllm:corrupted_requests[5m]) > 0

5.3 不该报警的

  • 单次请求超时:重试即可 → 用错误率(5 分钟窗口 > 1%)代替
  • GPU 利用率低:不是故障——可能是前缀缓存命中率高(好事)或负载低
  • 前缀命中率下降:先看是否换了请求分布,再决定是否处理
  • 副本重启 1 次:K8s 自动恢复 → 关注”重启频率趋势”而非单次

💡 提示:告警的终极检验:报警了,值班同学知道下一步干什么吗? 每条告警配”处理手册”(runbook)——查什么指标、看什么日志、什么操作可缓解。没有 runbook 的告警是噪音。


📝 总结

  • v0.26 指标引擎:Rust 化(prometheus-client crate),指标名 vllm: 冒号前缀
  • 4 个核心指标num_requests_waitingkv_cache_usage_perc、TTFT 分位数、num_preemptions
  • PromQL 四连:TTFT 分位数、命中率、等待比、吞吐
  • 单请求可追踪:request_id 贯穿日志与指标
  • 告警四原则:需要人处理、用比率、SLO 先行、预留恢复期
  • 每条告警配 runbook,否则是噪音

🎯 自我检验清单

  • v0.26 指标名为什么是 vllm: 前缀?旧版是什么?
  • KV 缓存水位用什么指标?怎么在 Grafana 画出来?
  • 前缀缓存命中率的 PromQL 怎么写?
  • 判断”过载”用哪两个指标的组合?
  • 单请求慢,怎么用 request_id 拆解时延?
  • 什么场景应该报警,什么场景不该报?
  • corrupted_requests 指标的含义?

📚 参考资料