把一个大模型真正跑起来不难难的是跑起来之后你完全不知道它到底是快是慢、是卡在算力上还是卡在排队上。自托管 LLM 推理服务的人基本都会遇到这个阶段模型是起来了但请求一多就玄学时快时慢问就是“在推理”再问就没了。vLLM 自带了一套 Prometheus 指标端点和结构化指标覆盖 TTFT、KV Cache 使用率、GPU 利用率这些推理服务最核心的观测维度。把这套指标接进 Prometheus Grafana你就能把“感觉卡了”变成“瓶颈在 prefill、显存、还是调度”调优也不再是猜。这篇文章我按自己实际部署的经验把指标含义、启动方式、监控栈搭建、以及从数据到调优动作的完整路径整理出来适合刚把 vLLM 部署起来、正在为性能和稳定性挠头的工程师也适合准备做自托管 LLM 服务的同学直接抄作业。1. 自托管推理服务为什么非盯住这三个指标不可先说结论TTFT 管用户体验KV Cache 管显存命脉GPU 利用率管钱有没有白花。这三个指标不是并列的“看看也行”而是 LLM 推理服务里互相关联、牵一发动全身的三个核心变量。1.1 TTFT用户感知的第一道坎TTFT也就是 Time To First Token指从请求发出到收到第一个 token 的时间。在流式输出的场景里用户等的其实就是这个“第一句话”。LLM 推理有 prefill 和 decode 两个阶段prefill 阶段要把整个 prompt 一次性算完这个过程往往占了首 token 延迟的大头。prompt 越长、模型越大、排队越久TTFT 就越难看。很多团队只关注 end-to-end 延迟其实那个数字会骗人。一个 2000 token 的完整回答如果 TTFT 已经花掉 8 秒用户早就关页面了后面的流畅根本没意义。所以自托管服务上线第一天就要把 TTFT 的分位数监控建起来p50 看普遍情况p95/p99 看用户体验的最差尾巴。1.2 KV Cache显存里的隐形变量KV Cache 是 Transformer 架构推理时用来缓存历史 token 的 Key 和 Value 张量的空间目的是避免每个新 token 都要重新计算前文。代价是它吃显存而且是动态吃。它的占用可以用一个公式粗略估算KV Cache 单序列占用 2 × 层数 × KV head 数 × head_dim × 序列长度 × 每元素字节数注意前面那个 2 是 K 和 V 两份。以一个 36 层、GQA 后 8 个 KV head、head_dim 128、bf16每元素 2 字节的 8B 模型为例服务 8K 上下文时需要2 × 36 × 8 × 128 × 8192 × 2 1,207,959,552 字节 ≈ 1.12 GiB单序列这只是单条请求。8 条并发直接就 9 GiB 了快赶上模型权重本身。这也是为什么你看着显存很满但模型权重其实只占了一小部分剩下全让 KV Cache 吃了。监控vllm:gpu_cache_usage_perc这个指标就是监控这块动态显存到底还剩多少一旦打满系统就靠抢占和换出硬撑性能会断崖式下跌。1.3 GPU 利用率一个被误读的数字GPU 利用率是大家最熟悉也最容易误读的指标。我们常看到的 97%、100%很多来自 NVML 的 GPU busy 采样它表示时间内 GPU 引擎有没有在工作不代表算力真的榨干了。LLM 推理是典型的“算一会、等一会”模式有 token 要算的时候 GPU 忙到飞起调度空档它就歇着。单看一个利用率百分比你分不清是“模型太大被显存卡住GPU 饿着肚子等数据”还是“并发太低一张卡只喂了一个请求”。所以我的习惯是GPU 利用率一定要跟 KV Cache 使用率、running 请求数放在一起看才有意义。2. 把 vLLM 的指标端点用起来vLLM 对 Prometheus 的支持是原生内置的不需要额外插件更不需要在业务代码里埋点。只要服务正常启动/metrics路径就自动暴露指标抓下来就是标准的 Prometheus 格式。2.1 启动参数与 Docker 部署示例先看看怎么把服务跑起来以 Docker 部署为例一个典型的 vLLM 启动命令长这样docker run --rm --gpus all \ --ipchost \ -p 8000:8000 \ -v ~/models:/models \ vllm/vllm-openai:latest \ --model /models/Qwen3-8B \ --served-model-name qwen3-8b \ --gpu-memory-utilization 0.9 \ --max-model-len 32768这里有几个要点。--ipchost必须加vLLM 的多进程推理依赖共享内存做数据传输容器默认的 /dev/shm 只有 64MB不加大概率跑起来就崩或者异常慢。模型目录用-v挂载进容器方便换模型版本时不用重新打镜像。--gpu-memory-utilization 0.9表示允许 vLLM 最多吃掉 90% 的显存用于权重和 KV Cache这是后面调优最重要的旋钮之一。服务起来之后验证指标端点最直接的办法是 curlcurl -s http://localhost:8000/metrics | grep ^vllm | head -30能看到一堆以vllm:开头的行说明指标端点正常。如果什么都没有先查端口映射和容器日志别急着怀疑 vLLM。2.2 核心指标清单与版本差异vLLM 不同版本的指标名有过调整我在 0.6.x 到 0.7.x 版本上常用的核心指标整理如下升级前后最好都用 curl 确认一遍实际名字。指标名常见版本类型含义vllm:num_requests_runninggauge当前正在执行的请求数vllm:num_requests_waitinggauge排队中的请求数接近上限时说明容量不足vllm:gpu_cache_usage_percgaugeGPU 上 KV Cache 使用百分比超过 90% 要警惕vllm:cpu_cache_usage_percgaugeCPU 上的 KV Cache 使用率启用 CPU offload 时才有意义vllm:time_to_first_token_secondshistogramTTFT 直方图核心延迟指标vllm:time_per_output_token_secondshistogram每个输出 token 的耗时直方图即 TPOTvllm:e2e_request_latency_secondshistogram端到端请求延迟直方图vllm:generation_tokens_totalcounter累计生成的 token 数可算吞吐vllm:prompt_tokens_totalcounter累计处理的 prompt token 数vllm:num_preemptions_totalcounter抢占次数KV Cache 不够时会飙升vllm:request_success_totalcounter成功请求数vllm:request_failure_totalcounter失败请求数度量类型要分清。gauge 是当前状态值直接看counter 是累计值要用 rate 或 increase 算速率histogram 要用 histogram_quantile 算分位数。很多人第一次用 Prometheus 时拿 histogram 指标当普通数值展示结果面板上永远是一个奇怪的小数就是这个原因。注意网上有提到 vLLM 0.23.0 的 chunk_size 相关行为变化这类升级容易影响 prefill 的切块行为和指标表现。升级大版本后第一件事是对比升级前后的 TTFT 和 preemption 曲线而不是只跑个冒烟测试就放量。2.3 指标的 PromQL 查询方式拿到指标之后真正干活的是 PromQL。下面几个是我在 Grafana 里反复用的查询可以直接抄# 当前在途请求数排队 执行 sum(vllm:num_requests_running) sum(vllm:num_requests_waiting) # KV Cache 使用率 vllm:gpu_cache_usage_perc # 最近 5 分钟抢占速率 sum(rate(vllm:num_preemptions_total[5m])) # 输出 token 吞吐每分钟 sum(rate(vllm:generation_tokens_total[1m])) # 请求成功率 sum(rate(vllm:request_success_total[5m])) / ( sum(rate(vllm:request_success_total[5m])) sum(rate(vllm:request_failure_total[5m])) ) # TTFT p95核心中的核心 histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le) ) # TPOT p95 histogram_quantile(0.95, sum(rate(vllm:time_per_output_token_seconds_bucket[5m])) by (le) )histogram_quantile是 histogram 类型专用的聚合函数必须作用在_bucket系列上。我在刚接触 Prometheus 时犯过的错是拿rate去算_sum再除_count虽然能得出平均值但分位数完全算不出来。3. Prometheus Grafana 监控栈搭建指标已经暴露出来了接下来就是把它们接进 Prometheus再用 Grafana 做可视化。这一段我按自己的部署习惯来讲Prometheus 用 Docker 挂个配置文件就能跑Grafana 用官方容器整套东西半小时能搭完。3.1 Prometheus 配置和抓取先写一个最小的 prometheus.ymlglobal: scrape_interval: 5s evaluation_interval: 5s scrape_configs: - job_name: vllm metrics_path: /metrics static_configs: - targets: [host.docker.internal:8000] labels: service: qwen3-8bscrape_interval我建议设 5 秒不要太长。TTFT 这类延迟指标60 秒抓一次等你要排查问题的时候只能看到一根完全平滑的曲线什么细节都没有了。当然抓取频率越高Prometheus 存储占用越大自托管小规模场景 5 秒完全够用。如果 Prometheus 是容器启动vLLM 跑在宿主机上targets要写host.docker.internal:8000如果两个都在同一个 docker-compose 网络里直接写服务名即可。启动 Prometheusdocker run -d --name prometheus \ -p 9090:9090 \ -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \ prom/prometheus:latest打开http://localhost:9090/targets能看到 vLLM 这个 job 的 target 状态。如果显示 UP说明抓取链路已经通了。这一步很多人会忽略我建议养成习惯进了 Grafana 之前先在这里看一眼避免后面面板没数据时到处乱猜。3.2 Grafana 面板设计Grafana 启动之后添加 Prometheus 数据源这个在 UI 里操作就行地址填http://prometheus:9090或宿主机对应的地址。面板的布局我建议按“用户视角 - 系统视角 - 容量视角”三行来做第一行放请求与延迟在途请求数、QPS、TTFT 的 p50/p95/p99、TPOT 的 p95、end-to-end 延迟。这一行回答“用户现在感受如何”。第二行放显存与 KV Cachevllm:gpu_cache_usage_perc、vllm:num_preemptions_total的变化率、以及 GPU 显存总量和已用量。这一行回答“系统离崩溃还有多远”。第三行放吞吐与资源生成 token 速率、prompt token 速率、GPU 利用率、请求成功率。这一行回答“钱有没有白花”。这里有一个容易踩的坑vLLM 自带指标里没有特别细的 GPU 细粒度监控。vllm:gpu_utilization这类值通常是基于 NVML 的粗粒度采样在纯推理负载下仅供参考。如果要做精细的 GPU 诊断建议另外部署 DCGM exporter它能把 SM 活动、显存带宽利用率、温度、功耗全暴露出来。自托管只有一两张卡的时候vLLM 自带指标加 nvidia-smi 够用机架级别规模就直接上 DCGM别犹豫。4. 用指标驱动调优从数据到动作监控建设好了真正的价值在“看懂了之后知道动哪里”。这一节我把三类最典型的问题场景展开讲每个场景都包含具体的判断路径和对应动作。4.1 TTFT 偏高怎么定位TTFT 飙高先拆成三段看排队、prefill 计算、网络传输。排队看vllm:num_requests_waiting。这个值长期大于 0说明进来的请求超过了系统处理能力要么扩容要么限流要么把--max-num-seqs单批最大序列数调大提高吞吐。注意这里有个权衡把并发拉高ttft 可能继续恶化因为它属于“增加吞吐但牺牲首字延迟”的操作。prefill 慢看模型的 prompt 长度分布。长文本 prefill 计算量随序列长度线性增长大批量长 prompt 同时进来的时候GPU 再强也会被按住。对这种场景vLLM 的 chunked prefill 会把一个长 prompt 切成多个 chunk 分批处理避免一条大请求堵死后面所有人。在较新版本里它默认开启但如果自定义过调度参数建议确认一下。还有一个容易忽略的点max_model_len。如果业务里经常出现接近上限的长请求prefill 计算量会非常大TTFT 自然压不下来。实际部署时我通常按业务需求把--max-model-len设成略高于 P99 prompt 长度的值而不是无脑拉满。4.2 KV Cache 使用率和命中率怎么调KV Cache 满的前兆很明确vllm:gpu_cache_usage_perc一路走高逼近 100%随后vllm:num_preemptions_total开始涨。抢占就是 vLLM 发现显存不够把某些序列的 KV Cache 清掉、腾空间给新请求后面再轮到它时从头重新 prefill。这是性能杀手比慢一点更伤因为它意味着大量算力被浪费在反复计算同一段内容上。调整手段按优先级排列第一调--gpu-memory-utilization。比如从 0.9 提到 0.95给 KV Cache 多留一些空间。前提是权重和激活内存不会被挤爆所以别拍脑袋直接上 0.98改完要盯住显存曲线防止 OOM。第二降低并发量或者缩短--max-model-len。这两项都直接降低 KV Cache 占用上限同时也会降低服务能力需要在容量之间找平衡。第三开启 prefix caching。vLLM 较新版本已经默认启用它能把相同前缀比如系统提示词、多轮对话历史的 KV Cache 复用起来命中后 prefill 计算量大幅下降TTFT 也会跟着降。如果要确认效果可以看 prefill token 占比是不是在下降。注意不要只盯着“KV Cache 使用率越高越好”。使用率 99% 意味着系统在满负荷边缘走钢丝任何一点流量抖动都可能触发抢占。我一般把 85%~90% 视为黄色警戒线长期高于 90% 就启动容量规划。4.3 GPU 利用率低时的排查路径GPU 利用率低常见原因有三类。第一类并发不够GPU 在空转等数据。看vllm:num_requests_running是不是经常只有 1、2。如果是把--max-num-seqs调大或者提高业务侧的并发压力。vLLM 的 continuous batching 就是吃多请求的单请求压测时利用率上不去是正常现象。第二类模型太大显存全被权重和 KV Cache 占了计算单元饿肚子。典型现象是 GPU 利用率不高但显存已满。这种时候要么换量化模型如 AWQ、GPTQ 4bit给 KV Cache 腾空间要么用更小的max_model_len要么直接换更大显存的卡。我见过有人拿 11GB 显存的 2080 Ti 硬跑大模型量化和上下文长度都压到极限才勉强不 OOM这种配置下利用率上不去反而是常态说明瓶颈不在算力而在显存。第三类请求短而密调度开销占比高。大量短请求频繁进出调度器忙于切换GPU 的有效计算时间被压缩。这时候可以看 TPOT 和 preemptions 面板如果 TPOT 没毛病、吞吐也不差但利用率数值低那多半是采样方式体现不出真实负载不用太过纠结。5. 常见问题与排查技巧实录最后分享一些我在实际部署和监控 vLLM 服务时踩过、也帮别人排查过的典型问题。5.1 几个真实踩坑记录问题一curl /metrics 什么也没有有一种情况是容器里 vLLM 服务已经启动但host.docker.internal解析不到宿主机。确认方式很简单先进 Prometheus 容器里 ping 一下这个域名不通就改用宿主机局域网 IP或者干脆把 Prometheus 和 vLLM 放进同一个 compose 网络。问题二Grafana 面板全是空九成是 Prometheus 里 job 名没对上或者 target 还是 DOWN。剩下那一成是你在 Grafana 里填的查询变量名和实际指标名不一致。记住 vLLM 指标命名空间是vllm:查询的时候一定要带上前缀比如vllm:gpu_cache_usage_perc很多人一上来就搜gpu_cache_usage_perc搜不到就以为没数据。问题三TTFT 指标算出来是 0 或者无穷大histogram 类的直方图在流量很小时bucket 里数据不足histogram_quantile算出来可能为 0 或没有值这是正常的不是 bug。造一点压测流量或者看 p50 就行。问题四GPU 利用率在 Grafana 里是锯齿vLLM 的 GPU 指标采样本来就不像 DCGM 那么细粒度加上 Prometheus 5 秒抓一次曲线锯齿是正常现象。不要为了追求平滑把 scrape_interval 调到 1 秒存储成本上去了信息增益很小。问题五Windows 宿主机部署 vLLMvLLM 在 Windows 原生环境下支持不好我建议直接走 WSL2 或者 Docker Desktop 的 GPU 透传。模型文件可以用 ModelScope 或 HuggingFace 下好之后挂载进容器路径用-v映射不要放在容器里不然重装镜像模型就没了。5.2 排查速查表现象优先查看指标常见结论请求一直转圈首字很慢time_to_first_token_seconds、num_requests_waiting排队过长容量不足或 max_num_seqs 过小显存快满服务开始变卡gpu_cache_usage_perc、num_preemptions_totalKV Cache 吃紧触发抢占GPU 利用率高但吞吐上不去time_per_output_token_seconds、generation_tokens_total单请求计算密度低或者量化后精度下降GPU 利用率低但显存满gpu_cache_usage_perc、num_requests_running模型太大/上下文太长显存是瓶颈偶尔一个请求特别慢e2e_request_latency_seconds、num_preemptions_total抢占导致重新 prefill请求成功率低request_failure_total、日志输入校验、context length 超限或 OOM最后分享一个我自己的习惯每次改完 vLLM 的启动参数不要只盯业务联通性要回头看一眼 TTFT 的 p95 和 KV Cache 的峰值。很多参数调整是按下葫芦浮起瓢比如把max_num_seqs调大后吞吐涨了但 TTFT 可能悄悄恶化两个面板一起看才能判断这次改动是赚是赔。自托管 LLM 的调优没有一劳永逸指标面板就是你手里的仪表盘数据到位了剩下的就是反复试、对比、取舍。