深入理解VLLM监控与推理参数之间的关系

在生产环境中部署大语言模型(LLM)时,把服务跑起来只是第一步,真正的工程挑战在于看懂引擎内部正在发生什么。为什么 GPU 显存显示占满了 90%,吞吐量却上不去?为什么首字延迟(TTFT)突然飙升,而吐字速度(ITL)却很正常?调大某个启动参数后,到底改善了哪个环节,又牺牲了什么?

在 vLLM 0.30.0(基于全面成熟的 V1 EngineCore 架构)中,调度器、内存管理与指标采集经过了深度重构。下面从 vLLM 的底层执行机制出发,系统拆解其监控指标体系、核心启动参数、参数与指标的映射关系,并为你搭建一套可直接落地的生产级监控与排障分析框架。


一、 底层基石:理解 vLLM 0.30.0 的执行模型

要真正看懂监控指标和启动参数,必须先在脑海中建立 vLLM 0.30.0 的两个核心底层机制:双环异步架构(AsyncLLM + EngineCore)与基于 Token 预算的统一调度(Unified Token-Budget Scheduling)。

1. 双环异步架构:为什么采集指标不会卡住 GPU?

在 vLLM 0.30.0 的 V1 架构中,进程职责被严格切分为外环和内环:

  • 外环(AsyncLLM / API Server):运行在 CPU 异步事件循环中,负责 HTTP/gRPC 接入、Tokenizer 分词、Detokenizer 解码流式输出,以及 Prometheus /metrics 的统计记账(Bookkeeping)。
  • 内环(EngineCore):极致精简的调度与 GPU 执行循环。每一个 Step(迭代步),调度器计算本轮要处理的 Token,拼装成 Batch 下发给 GPU Worker 执行 Forward,并通过零拷贝机制将结果异步传回外环。

2. 请求的生命周期与三大状态

一个请求进入 vLLM 后,会经历以下阶段,每个阶段都直接对应一组监控指标:

  1. WAITING(等待态):请求已完成分词并进入调度器队列,但因为当前并发槽位已满、单步 Token 预算耗尽或物理 KV Cache 块不足,尚未被调度上 GPU(或因抢占被退回队列)。
  2. RUNNING(运行态):请求已被调度器准入,正在 GPU 上执行计算。在 V1 架构下,由于默认启用了 Chunked Prefill(分块预填充),同一个 Batch(单次 Forward 迭代)中可以同时混合正在做 Prefill 的请求块和正在做 Decode 的单 Token 请求。
  3. PREEMPTED(被抢占回退):当多个正在 Decode 的请求不断生成新 Token,把物理 KV Cache Block 池彻底耗尽时,调度器为了防止死锁,必须强行踢出(Evict)部分正在 RUNNING 的请求,释放它们占用的 KV Block,将其打回 WAITING 队列,等待后续显存空闲时重新计算(Recompute)。

二、 vLLM 0.30.0 核心监控指标(/metrics)深度全解

vLLM 原生在 /metrics 端点暴露标准的 Prometheus 格式指标。在 Prometheus 中,指标分为三类数据模型:

  • Gauge(仪表盘):可升可降的瞬时状态快照(如当前排队数、当前缓存使用率),查询时直接用原值或 avg_over_time / max_over_time。
  • Counter(计数器):只增不减的累计值(如累计生成 Token 数、累计抢占次数),查询时必须搭配 rate() 或 increase() 使用。
  • Histogram(直方图):按分桶(_bucket)统计的分布数据(如延迟分布、请求长度分布),查询时使用 histogram_quantile(0.95, sum(rate(..._bucket[5m])) by (le)) 计算 P95/P99 分位值,或用 _sum / _count 计算均值。

1. 调度器与队列饱和度指标(Scheduler & Queue State)

指标名称 (Metric Name) 类型 底层物理含义与计算逻辑 生产诊断与异常信号
vllm:num_requests_running Gauge 当前处于 RUNNING 状态、正在被调度器纳入活跃批次的请求总数。 反映当前并发批次饱满度。若长期打满 --max-num-seqs 或 --max-num-active-seqs 上限,说明并发槽位受限。
vllm:num_requests_waiting Gauge 当前滞留在 WAITING 队列中等待准入的请求总数。 最灵敏的过载预警信号。理想状态下应接近 0;一旦持续大于 0,说明请求到达速率超过了引擎消费速率或显存/槽位已满,TTFT 必然随之暴涨。
vllm:num_preemptions_total Counter 引擎自启动以来发生的请求抢占(Preemption)累计次数。 显存池被打穿的铁证。正常生产环境 rate(vllm:num_preemptions_total[1m]) 必须为 0。一旦大于 0,意味着已算好的 KV Cache 被迫丢弃并在后续重算,会导致吞吐断崖式下跌、延迟剧烈抖动。
vllm:request_success_total Counter 已完成请求总数,带有 finished_reason 标签(stop、length、abort)。 重点监控 finished_reason="abort"(客户端超时断开)和 finished_reason="length"(输出被 max_tokens 或 max_model_len 强行截断)的占比。

2. KV Cache 显存池与 Prefix Cache 指标(Memory & KV Blocks)

很多工程师最大的困惑是:“为什么 nvidia-smi 显示显存占用了 92%,而 vLLM 监控显示 KV Cache 只用了 15%?”
这是因为 vLLM 在启动阶段就会执行一次 Dummy Forward 探明峰值激活显存,随后将 --gpu-memory-utilization(默认 90%)允许范围内的剩余物理显存一次性向 CUDA 全部申请下来,切分成固定大小的物理 KV Block Pool。因此操作系统的显存监控永远是满的,只有 vLLM 自身的指标才能反映真实的 KV 显存水位。

指标名称 (Metric Name) 类型 底层物理含义与计算逻辑 生产诊断与异常信号
vllm:kv_cache_usage_perc Gauge 物理 KV Block 池中当前已被占用的 Block 比例(取值 0.0 ~ 1.0)。(注:v0.10.2+ 统一为此名称,早期版本名为 vllm:gpu_cache_usage_perc) 容量规划与自动扩缩容(HPA)的核心指标。注意:开启 Prefix Cache 后,已结束请求的缓存块在未被驱逐前不计入活跃锁死,该指标反映当前活跃占用水位。当超过 0.85 时需预警,达到 0.98+ 时极易触发 Preemption。
vllm:prefix_cache_queries (或 _total) Counter Automatic Prefix Caching (APC) 累计查询的 Token 数(或块查询数)。 配合 hits 使用,不可单独看绝对值。
vllm:prefix_cache_hits (或 _total) Counter APC 在 GPU KV Block 池中直接命中无需重算的累计 Token 数。 前缀缓存命中率公式:rate(vllm:prefix_cache_hits_total[5m]) / rate(vllm:prefix_cache_queries_total[5m])。在多轮对话、Agent 系统提示词固定或 RAG 场景下,命中率若突然下降,通常意味着负载均衡器(LB)采用轮询策略打散了同一会话的请求。
vllm:kv_block_lifetime_seconds Histogram 采样的物理 KV Block 从分配到被回收/覆写的完整存活时间分布。 反映 KV 块在显存池中的周转周期。
vllm:kv_block_idle_before_evict_seconds Histogram KV Block 在最后一次被请求访问后,到最终被 LRU 策略驱逐前的空闲等待时长。 诊断“缓存颠簸(Cache Thrashing)”的神器。如果该值极小(例如只有几秒)且 Prefix 命中率低,说明显存池太小,多轮对话的上一轮 KV 刚生成完还没等到用户发下一句就被新请求冲掉了。
vllm:kv_block_reuse_gap_seconds Histogram 同一个缓存 KV Block 连续两次被不同请求复用命中之间的时间间隔分布。 如果业务的多轮对话平均间隔是 30 秒,那么 idle_before_evict 必须大于 30 秒,才能保证这批请求真正享受到 Prefix Cache 加速。

3. 请求延迟拆解与 SLO 指标(Latency Breakdown)

在 vLLM 内部,一个请求的端到端生命周期严格满足以下时间拆解公式:

\[\text{E2E Latency} = \text{Queue Time} + \text{Prefill Time} + \text{Decode Time} \]

\[\text{TTFT (首 Token 延迟)} \approx \text{Queue Time} + \text{Prefill Time} \]

指标名称 (Metric Name) 类型 底层物理含义与计算逻辑 生产诊断与异常信号
vllm:time_to_first_token_seconds Histogram TTFT:从 API Server 收到请求,到生成并吐出第一个 Token 的耗时分布。 交互式体验的核心指标(通常要求 P95 小于 500ms~1s)。当 TTFT 升高时,必须结合下方两个指标拆解:到底是在排队(Queue Time 高),还是在算大 Prompt(Prefill Time 高)?
vllm:request_queue_time_seconds Histogram 请求在 WAITING 队列中等待首次被调度器选中的耗时分布。 系统算力或显存饱和导致排队的直接度量。
vllm:request_prefill_time_seconds Histogram 请求从首次被调度,到完成全部 Prompt 的 Prefill 计算耗时分布。 若 Queue Time 很低但 Prefill Time 极高,说明遇到了超长上下文输入(Compute-bound 算力瓶颈),或者请求在分块预填充(Chunked Prefill)中经历了多次迭代切片。
vllm:request_decode_time_seconds Histogram 请求生成首个 Token 之后,完成所有后续自回归 Token 生成的总耗时。 与输出 Token 长度成正比。
vllm:request_time_per_output_token_seconds Histogram Request-level TPOT:单请求维度的平均每输出 Token 耗时,即 \(\text{Decode Time} / (\text{Output Tokens} - 1)\)。 衡量整体生成速率的宏观 SLO 指标。每个请求完成时记录一次样本。
vllm:inter_token_latency_seconds Histogram ITL(流式帧间隔延迟):流式输出过程中,相邻两次吐出 Token 之间的真实墙钟时间差。 比 TPOT 更能反映用户感知的“卡顿感”。如果一个请求平时 20ms 吐一个字,但中间因为混入了一个大 Prompt 的 Prefill Chunk 卡了 400ms,TPOT 会把这个毛刺平均掉,而 ITL 的 P99 会立刻抓出这次流式卡顿!
vllm:e2e_request_latency_seconds Histogram 请求从进入到彻底生成完毕的端到端总耗时。 用于非流式调用(Batch Job / Agent 内部同步调用)的整体 SLA 监控。

4. 吞吐量与工作负载形状指标(Throughput & Workload Shape)

指标名称 (Metric Name) 类型 底层物理含义与计算逻辑 生产诊断与异常信号
vllm:prompt_tokens_total Counter 引擎累计处理的输入 Prompt Token 总数。 rate(vllm:prompt_tokens_total[1m]) 即为系统的输入吞吐率(Prefill TPS)。
vllm:generation_tokens_total Counter 引擎累计生成的输出 Token 总数。 rate(vllm:generation_tokens_total[1m]) 即为系统的生成吞吐率(Decode TPS),是衡量推理集群产出效率的头号指标。
vllm:iteration_tokens_total Histogram 每一个 Scheduler Step(即单次 GPU Model Forward)实际打包处理的 Token 总数分布。 透视 Continuous Batching 效率的显微镜。如果并发很高,但该指标均值接近 1,说明 Batching 机制失效;如果该值经常顶到 --max-num-batched-tokens 上限,说明正处于满载分块吞吐状态。
vllm:request_prompt_tokens Histogram 每个请求的输入 Prompt Token 长度分布。 监控上游业务流量特征的变化(如是否有异常超长 Prompt 突刺)。
vllm:request_generation_tokens Histogram 每个请求实际生成的 Output Token 长度分布。 结合 request_prompt_tokens 计算 Input/Output Ratio(输入输出比),判断当前业务是 Prefill 密集型(如文档摘要、RAG)还是 Decode 密集型(如代码生成、长文创作、CoT 推理模型)。

5. 投机采样指标(Speculative Decoding,按需启用)

当你使用 DeepSeek-MTP、EAGLE 或 Draft Model 开启投机采样时,关注以下指标:

  • vllm:spec_decode_draft_acceptance_rate (Gauge):草稿 Token 被主模型验证通过的接受率(通常大于 0.6 才有显著正收益)。
  • vllm:spec_decode_efficiency (Gauge) 与 vllm:spec_decode_num_accepted_tokens / vllm:spec_decode_num_draft_tokens (Counter):衡量单次主模型 Forward 额外白赚了多少个有效 Token。

三、 vLLM 0.30.0 核心启动参数深度解析

理解了指标之后,我们再来看启动命令 vllm serve <model> 中的核心参数。每一个核心参数的本质,都是在显存容量、计算吞吐(Prefill)和访存延迟(Decode)这三者之间划定边界。

1. 显存与 KV Cache 池化参数

  • --gpu-memory-utilization(默认 0.90)
  • 底层机制:决定 vLLM 可以使用单卡总显存的多少比例。vLLM 启动时会遵循以下显存分配方程:

\[\text{KV Cache Pool 显存} = \text{GPU 总显存} \times \text{utilization} - \text{模型权重显存} - \text{峰值激活显存 (Activation)} - \text{CUDA Graph 显存} - \text{PyTorch 上下文开销} \]

  • 深入调优:如果单卡只跑这一个 vLLM 进程,且没有其他训练或监控 Agent 抢显存,在 80GB 的 H100/A100 上可安全上调至 0.92 ~ 0.95,多出来的几个 GB 会100% 转化为额外的 KV Cache Blocks,直接推高最大并发容量。反之,如果报 CUDA Out of Memory,优先检查是否有外部进程占显存,或微调降至 0.85。

  • --max-model-len

  • 底层机制:限制单个请求允许的最大上下文长度(Prompt + Output)。模型本身的 config.json 往往声明支持 128K(如 Llama-3.1 / Qwen3),但如果你在启动时不显式调小它,vLLM 在启动预热(Warmup Profiling)时会按 128K 去校验并预留开销,甚至直接因为 KV 池不够容纳单条 128K 请求而拒绝启动。

  • 深入调优:永远按真实业务需求收紧此参数(例如业务最大只用到 16384 或 32768)。收紧后不仅能大幅降低预热开销,还能防止恶意或异常超长请求瞬间吃光整个 KV Cache 池。

  • --kv-cache-dtype(auto / fp8 / fp8_e4m3 / nvfp4)

  • 底层机制:默认 auto 使用与模型权重相同的精度(BF16/FP16,每元素 2 字节)。设为 fp8(H100/Ada/MI300 及更新架构支持)时,KV Cache 存储体积直接减半(1 字节),在 Hopper/Blackwell 架构(如 v0.30.0 针对 SM100 优化的 FlashMLA NVFP4)下甚至可进一步压缩。

  • 深入调优:性价比最高的参数之一。它有双重收益:第一,可用物理 KV Block 数量直接翻倍;第二,由于 Decode 阶段是典型的显存带宽受限(Memory-Bandwidth Bound)(每生成一个 Token 都要把所有历史 KV 从 HBM 搬运到 SRAM),KV 体积减半意味着搬运字节数减半,高并发下 ITL(吐字延迟)显著降低!

  • --block-size(默认 16 或 32,视后端而定)

  • 底层机制:PagedAttention 中每个逻辑/物理块包含的 Token 数量。

  • 深入调优:块越小(如 16),最后一个未填满块的内部内存碎片(Internal Fragmentation)越小;块越大(如 32 或 64),Block Table 索引越短,且在部分硬件或算子(如特定 FlashAttention / MLA 内核)下连续访存效率更高。通常保持默认即可,但在超长上下文场景下可测试 32 或 64。

  • **--enable-prefix-caching / --no-enable-prefix-caching**

  • 底层机制:V1 引擎默认开启 Automatic Prefix Caching (APC)。每个填满的 KV Block 会根据其前置路径和当前 Token 序列计算哈希值(v0.30.0 中还将 LoRA 路径与 cache_salt 纳入哈希以保证多租户安全隔离)。

2. 调度器与批处理控制参数(含 v0.30.0 核心特性)

  • --max-num-batched-tokens

  • 底层机制:单次 GPU Forward 迭代允许打包的最大 Token 总预算。在 V1 统一调度器中,假设该值设为 2048,当前有 100 个请求正在 Decode(每个消耗 1 个 Token 预算,共 100),那么本轮迭代还剩下 2048 - 100 = 1948 的 Token 预算可以分给 WAITING 队列里的新请求做 Chunked Prefill。

  • 深入调优:这是平衡 TTFT 与 ITL 的总阀门!

  • 调大它(如 8192 或 16384):大 Prompt 可以一次性或分极少次数完成 Prefill,GPU Tensor Core 算力利用率拉满,TTFT 和整体吞吐量显著提升;但代价是单次 Forward 耗时变长(例如从 15ms 变成 120ms),所有跟在这个 Batch 里一起跑 Decode 的老请求都得跟着等这 120ms,导致 ITL 飙升、流式输出卡顿。

  • 调小它(如 1024 或 2048):每次 Forward 都很短,正在流式输出的请求极其丝滑(ITL 极低且平稳);但长 Prompt 会被切成很多碎块分多轮跑完,TTFT 变长。

  • --max-num-seqs

  • 底层机制:单个迭代步(Step)允许同时拼入 Batch 的最大请求(序列)数。它同时决定了启动时捕获 CUDA Graph 的最大 Batch Size 档位。

  • --max-num-active-seqs(v0.30.0 重点引入的精细化调度参数)

  • 底层机制:在早期版本中,准入控制与单步 Batch 上限绑死在 --max-num-seqs 上。v0.30.0 引入了 --max-num-active-seqs,用于独立限制调度器同时准入 RUNNING 状态的活跃请求总数。

  • 为什么极其关键? 设想 200 个请求同时进来,刚开始都在做 Prefill 或刚生成几个字,KV Cache 只用了 30%,调度器把它们全放进了 RUNNING。但随着这 200 个请求同时不断吐字,5 秒后每个请求都长到了 4000 Tokens,KV Cache 瞬间爆满至 100%,引发雪崩式 Preemption。通过合理限制 --max-num-active-seqs,让超出安全水位的请求老老实实待在 WAITING 队列门口排队,就能从根源上消灭运行中途的显存踩踏与抢占重算!

  • --long-prefill-token-threshold

  • 底层机制:控制在启用 Chunked Prefill 时,单条长 Prompt 在单次迭代中最多能吃掉多少 Token 预算。在 v0.30.0 中,该参数进化为根据等待队列中的 Prefill 请求数自适应调整分摊下限:如果队列里只有一个长请求,就不做无意义的切碎;如果有多个请求同时等待,则自动限制单个大请求的单步配额,防止“大请求饿死小请求(Head-of-Line Blocking)”。

3. 并行、编译与服务化参数

  • --tensor-parallel-size (-tp) vs --pipeline-parallel-size (-pp) vs --data-parallel-size (-dp)

  • 底层原则:算得下且单机 NVLink 互联时,优先用 TP;放不下且跨机(IB/RoCE)时,外层套 PP;单卡或小 TP 就放得下时,优先用 DP(或多实例负载均衡)。

  • 原因:TP 在每一层 Transformer 的 Attention 和 MLP 后都需要执行一次 AllReduce 同步。TP 越大,单请求延迟(特别是 Decode 带宽和算力)越低,但跨卡通信开销呈线性增加;如果单卡能跑(如 8B/14B/32B FP8),跑 4 个 TP=1 实例的总吞吐量远高于 1 个 TP=4 实例。对于 DeepSeek / Qwen3-MoE 等混合专家模型,则搭配 --enable-expert-parallel 使用专家并行(EP)。

  • **--compilation-config 与 --enforce-eager**

  • 底层机制:vLLM 0.30.0 默认开启 torch.compile 与 CUDA Graph(将多个小算子发射打包成单次图执行,消除 Python 与 CUDA Kernel Launch 的 CPU 开销)。--enforce-eager 会关闭 CUDA Graph 和编译,仅用于调试或显存极度受限时节省几百 MB 到数 GB 的 Graph 显存,**生产环境切勿开启 --enforce-eager**,否则在小 Batch Decode 时会损失 30%~50% 的性能。

  • vllm preload(v0.30.0 新增快速重启机制)

  • 底层机制:生产调优经常需要重启 vLLM 调整参数,几百 GB 权重的加载动辄数分钟。v0.30.0 提供了 vllm preload 权重驻留守护进程,可将量化后的模型权重常驻在 GPU 显存中,主引擎重启或调参时秒级热挂载。


四、 核心全景图:启动参数与监控指标的映射联动表

在调优时,你动的每一个参数都会在监控面板上激起连锁反应。下表为你建立“参数调整 \(\rightarrow\) 底层物理变化 \(\rightarrow\) 监控指标响应”的精确映射:

启动参数 调整方向 直接联动的监控指标 指标变化趋势与底层因果解释
--gpu-memory-utilization 调大 (如 0.85 \(\rightarrow\) 0.93) vllm:kv_cache_usage_per vllm:num_preemptions_tota vllm:kv_block_idle_before_evict_seconds KV 使用率下降,抢占归零,块空闲存活时间变长。物理 KV Block 分母变大,不仅能同时容纳更多 RUNNING 请求,还能在池中保留更多历史 Prefix Cache,提升 prefix_cache_hits。
--kv-cache-dtype **auto \(\rightarrow\) fp8** vllm:kv_cache_usage_per vllm:inter_token_latency_second vllm:generation_tokens_total KV 使用率直接减半,ITL 下降,Decode 吞吐提升。每个 Token 占用的物理显存减半,且 GPU HBM 显存带宽压力减半,直接加速 Memory-bound 的 Decode 阶段。
--max-num-batched-tokens 调大 (如 2048 \(\rightarrow\) 8192) vllm:iteration_tokens_tota vllm:request_prefill_time_second vllm:inter_token_latency_seconds 单步 Token 数上升,Prefill 耗时缩短(TTFT 改善),但 ITL P99 升高。单次 Forward 塞入了更大的 Prefill 块,算力吃满,但拉长了单步执行时间,导致同批次 Decode 请求吐字间隔变长。
--max-num-batched-tokens 调小 (如 8192 \(\rightarrow\) 2048) vllm:inter_token_latency_second vllm:time_to_first_token_seconds` ITL 变得极其平滑低延,但长请求 TTFT 升高。长 Prompt 被迫拆成更多个小 Chunk 分多步算完。
--max-num-active-seqs / --max-num-seqs 调大 (如 64 \(\rightarrow\) 256) vllm:num_requests_runnin vllm:num_requests_waitin vllm:num_preemptions_total 运行数上限打开,排队数下降,但若超过 KV 容量承载力,num_preemptions_total 会暴涨。同时,由于单步并发序列过多,达到计算拐点后 inter_token_latency_seconds 也会线性变慢。
--max-num-active-seqs 调小 (如 256 \(\rightarrow\) 96) vllm:num_preemptions_tota vllm:request_queue_time_seconds` 抢占彻底消失,正在生成的请求 ITL 极快,但超出并发的请求会在门口排队(Queue Time 增加)。用可控的门口排队换取场内执行的绝对稳定(牺牲部分 TTFT 保 ITL 和总吞吐)。
--long-prefill-token-threshold 调小 vllm:time_to_first_token_seconds (短请求 vs 长请求) 短请求的 TTFT P99 显著下降,超长请求的 TTFT 略微上升。阻止单条万字长 Prompt 独占整个 Step 的 Token 预算,消除队列队头阻塞。

五、 实战落地:构建系统化的 vLLM 监控分析与排障框架

要将上述理论转化为生产力,建议在 Prometheus + Grafana 中搭建一套“三层监控分析体系”(业务黄金指标层 \(\rightarrow\) 引擎调度机制层 \(\rightarrow\) GPU 硬件物理层)。

第一步:配置核心 PromQL 监控面板(可直接用于 Grafana)

将以下 8 个核心 PromQL 表达式配置到你的 Grafana 看板中,即可掌控 95% 的运行状态:

  1. 端到端首字延迟 TTFT (P95 / P99):
histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le, model_name))

  1. 流式吐字间隔延迟 ITL (P95 / P99):
histogram_quantile(0.95, sum(rate(vllm:inter_token_latency_seconds_bucket[5m])) by (le, model_name))

  1. 请求生命周期耗时拆解(排队 vs Prefill vs Decode 平均耗时占比):
# 排队平均耗时
rate(vllm:request_queue_time_seconds_sum[5m]) / rate(vllm:request_queue_time_seconds_count[5m])
# Prefill 平均耗时
rate(vllm:request_prefill_time_seconds_sum[5m]) / rate(vllm:request_prefill_time_seconds_count[5m])
# Decode 平均耗时
rate(vllm:request_decode_time_seconds_sum[5m]) / rate(vllm:request_decode_time_seconds_count[5m])

  1. 实时吞吐率(Input Prefill TPS vs Output Decode TPS):
# 生成 Token 吞吐 (Decode TPS)
sum(rate(vllm:generation_tokens_total[1m])) by (model_name)
# 输入 Token 吞吐 (Prefill TPS)
sum(rate(vllm:prompt_tokens_total[1m])) by (model_name)

  1. KV Cache 池水位与抢占速率(核心稳定性告警):
# KV Cache 占用率
vllm:kv_cache_usage_perc
# 每秒抢占次数(必须等于 0!)
rate(vllm:num_preemptions_total[1m])

  1. Prefix Cache 命中率:
sum(rate(vllm:prefix_cache_hits_total[5m])) by (model_name) 
/ 
sum(rate(vllm:prefix_cache_queries_total[5m])) by (model_name)

  1. 单步 Batch 饱满度(平均每次 Forward 处理的 Token 数):
rate(vllm:iteration_tokens_total_sum[5m]) / rate(vllm:iteration_tokens_total_count[5m])

  1. 平均输入/输出长度画像(Workload Shape):
# 平均输入长度
rate(vllm:request_prompt_tokens_sum[5m]) / rate(vllm:request_prompt_tokens_count[5m])
# 平均输出长度
rate(vllm:request_generation_tokens_sum[5m]) / rate(vllm:request_generation_tokens_count[5m])


第二步:结合 GPU 硬件指标(DCGM)定性三大物理瓶颈

仅看 DCGM_FI_DEV_GPU_UTIL(即 nvidia-smi 里的 GPU 利用率)是毫无意义的——只要 GPU 上有一个很小的 Kernel 在跑,它就显示 100%,哪怕算力只用了 10%。必须结合 vLLM 指标 + NVIDIA DCGM 硬件探针指标 交叉定性:

瓶颈类型 vLLM 监控特征 DCGM 硬件指标特征 核心优化手段
1. 算力受限 (Compute-Bound) request_prefill_time_seconds 占比极高;iteration_tokens_total 长期打满上限。 DCGM_FI_PROF_PIPE_TENSOR_ACTIVE(Tensor Core 活跃度)极高(大于 60%~70%)。 1. 确保开启 Prefix Cache,并设置会话亲和性路由(Sticky Routing)。
2. 启用权重与激活同时量化(W8A8 FP8),利用 FP8 Tensor Core 提升计算吞吐。
3. 开启 Prefill/Decode 分离部署(Disaggregated Serving)。
2. 显存带宽受限 (Memory-Bandwidth-Bound) request_decode_time_seconds 占主导(如大于 85%);并发增加时 inter_token_latency_seconds 显著变慢。 DCGM_FI_PROF_PIPE_TENSOR_ACTIVE 较低(如 15%),但 DCGM_FI_PROF_DRAM_ACTIVE(HBM 带宽活跃度)极高(大于 75%)。 1. 首选量化:启用 --kv-cache-dtype fp8 及权重量化(FP8/AWQ),减少每步搬运的字节数。
2. 开启投机解码(Speculative Decoding / MTP),一次验证多个 Token,提高权重读取的利用效率。
3. 显存容量受限 (Capacity-Bound) kv_cache_usage_perc 长期大于 0.85;num_requests_waiting 持续堆积,或出现 num_preemptions_total。 HBM 带宽和 Tensor Core 均未跑满,但业务并发已经无法继续提升。 1. 开启 --kv-cache-dtype fp8,理论上可将 KV Cache 容量扩容近一倍。
2. 上调 --gpu-memory-utilization 至 0.93。
3. 合理设置 --max-num-active-seqs,避免过多请求竞争显存,减少抢占和重计算。

第三步:生产环境四大经典故障排障决策树(Playbook)

当线上告警响起或业务反馈“模型变慢”时,按照以下四步诊断树直接定位根因并修改参数:

场景一:首字延迟(TTFT)严重超标,但吐字速度(ITL)正常

  1. 看拆解指标:对比 vllm:request_queue_time_seconds 和 vllm:request_prefill_time_seconds。
  2. 分支 A — queue_time 占了大头(请求在排队):
  • 检查 vllm:kv_cache_usage_perc 是否接近 1.0?如果是,说明显存块不够用了,新请求进不来 \(\rightarrow\) 开启 --kv-cache-dtype fp8 或扩容实例。
  • 如果 kv_cache_usage_perc 很低(如只有 40%),但 vllm:num_requests_running 呈一条水平直线死死贴在某个数值(如 64 或 128)\(\rightarrow\) 遇到了“伪饱和”! 你的 --max-num-seqs 或 --max-num-active-seqs 设得太保守了,明明有显存和算力,却把请求挡在了门外 \(\rightarrow\) **调大 --max-num-active-seqs / --max-num-seqs**。
  1. 分支 B — prefill_time 占了大头(排队很快,但算得慢):
  • 检查 vllm:request_prompt_tokens 是否出现了超长 Prompt?
  • 检查 --max-num-batched-tokens 是否设得太小(例如 1024),导致一个 8K 的 Prompt 被强行切成了 8 个 Step 才算完 \(\rightarrow\) 在 ITL 允许的前提下,适当调大 --max-num-batched-tokens(如 4096 或 8192)。
  • 检查 prefix_cache_hits / prefix_cache_queries 是否过低 \(\rightarrow\) 将网关的 Round-Robin 负载均衡改为基于 Prefix Hash 或 Session ID 的一致性哈希路由。

场景二:流式输出一卡一卡的(ITL P99 极高),用户体验差

  1. 对比 vllm:request_time_per_output_token_seconds (TPOT) 与 vllm:inter_token_latency_seconds (ITL)。
  2. 分支 A — TPOT 均值还可以,但 ITL P99 极高(周期性顿挫):
  • 根因:典型的 Prefill 干扰 Decode(Jitter)。当正在流式输出的请求遇到一个新进来的长 Prompt 时,由于单步预算过大,这个 Step 耗时激增。
  • 药方:调小 --max-num-batched-tokens**(例如从 8192 降到 2048),或者调小 --long-prefill-token-threshold**(例如设为 1024),限制单步 Prefill 的最大切片大小。
  1. 分支 B — TPOT 和 ITL P50/P95 全面升高(整体吐字变慢):
  • 根因:当前并发 vllm:num_requests_running 过高,或上下文过长,已经把 GPU 的 HBM 显存带宽彻底打满。
  • 药方:启用 --kv-cache-dtype fp8 降低带宽消耗;或通过 --max-num-active-seqs 限制单实例最高并发度,配合水平扩容分流。

场景三:rate(vllm:num_preemptions_total[1m]) > 0,吞吐忽高忽低剧烈抖动

  1. 根因:物理 KV Cache 池被打爆(vllm:kv_cache_usage_perc 触及 1.0),调度器正在频繁踢出已运行一半的请求并重算。
  2. 药方(按优先级排序):
  • 立竿见影:设置或调小 --max-num-active-seqs,控制同时进入 RUNNING 阶段的请求数量,宁可在门口 WAITING 排队幾百毫秒,也绝不允许进场后算到一半被踢出去重算!
  • 开源节流:开启 --kv-cache-dtype fp8;调高 --gpu-memory-utilization(如 0.93);在网关或请求侧严格限制 max_tokens,防止异常死循环生成把单个请求撑到几十 K 长度。

场景四:K8s 自动扩缩容(HPA)指标选型误区

  • 千万不要用:CPU 利用率、DCGM_FI_DEV_GPU_UTIL(永远虚高)、DCGM_FI_DEV_FB_USED(启动即锁满)。
  • 黄金组合触发器:使用 Prometheus Adapter 采集以下两个指标作为 HPA 扩缩容依据:
  1. vllm:kv_cache_usage_perc > 0.65(显存水位线预警)
  2. vllm:num_requests_waiting > 3(持续超过 15~30 秒队列非空)

六、 交互式沙盒:vLLM 0.30.0 参数与监控指标联动模拟器

为了让你直观感受上述启动参数与监控指标之间的动态博弈关系,可以通过下方的交互式模拟器调整流量并发与上下文长度以及 vLLM 核心启动参数(--max-num-batched-tokens、--max-num-active-seqs、--kv-cache-dtype),实时观察 vllm:kv_cache_usage_perc、队列状态、抢占告警以及 TTFT / ITL 延迟曲线的变化:

posted @ 2026-11-11 16:21  虾野百鹤  阅读(9)  评论(0)    收藏  举报