本地化的卖点,系列开篇就讲过:可控——数据不出门、成本可预期、容量自己说了算。但可控有个前提:看得见。一个没有任何观测手段的推理服务就是黑盒——用户说慢,你不知道是排队变长了、GPU 打满了,还是 KV cache 触发了抢占重算;有人问自建到底比调云 API 便宜多少,你拿不出一个数字。本篇补上这块短板:给推理服务装上仪表盘,再把仪表盘读数换算成钱。
应用层的常规监控——日志、进程、HTTP 状态码——在 EDD 系列第 11 篇(线上监控)已经覆盖,Prometheus + Grafana 那套可以直接搬。本篇聚焦推理基础设施特有的部分:token 吞吐、首字延迟、KV cache、GPU 硬件,最后落到成本核算与容量决策。指标名以 2026 年 10 月的 vLLM v0.32.0 实际输出为准,命令可照抄。全文按"指标分层 → 采集 → 可视化 → 告警 → 成本 → 决策"组织。
1、监控指标分层
监控不是指标越多越好,而是每个指标都要能回答一个你会被问到的问题。按"谁问、问什么"分层,本地推理服务需要六类。
| 层 | 核心指标 | 回答的问题 |
|---|---|---|
| 吞吐 | 输入与生成 tokens/s | 机器总共出了多少活 |
| 延迟 | TTFT、TPOT 的 P50/P99 | 用户体感快不快 |
| 队列 | 等待与运行请求数 | 是不是过载了 |
| 容量 | KV cache 利用率 | 还塞得下多少并发 |
| 硬件 | GPU 利用率、显存、温度 | 卡还健不健康 |
| 质量 | 成功数与错误数 | 服务坏没坏 |
两个延迟指标先定义清楚。**TTFT(Time To First Token)**是从请求进队到吐出第一个 token 的时间,决定"多快开口";TPOT(Time Per Output Token)是后续每个 token 的平均间隔,决定"吐字多快"。聊天场景用户对 TTFT 敏感——超过 2 秒就感觉卡;长文批量生成则对 TPOT 敏感。看均值会被长尾骗,盯 P99 才是真实体感。
队列是延迟的先行指标:waiting 数从 0 涨起来,几分钟后 TTFT 必然飙升,告警要在这一层响,而不是等用户投诉。KV cache 利用率则是容量水位线,第 11 篇(KV cache 深度)讲过它的物理含义——满了之后引擎开始抢占重算,吞吐悬崖式下跌。
分层表只是清单,落地还有一步:先跑三天基线。把每个指标在真实流量下的日常区间记下来——比如 TTFT P99 平时 800 ms 上下、KV cache 峰值七成——第 5 节的告警阈值就是从基线推的。凭空设阈值要么天天误报,要么出了事不响。
2、vLLM 指标端点
vLLM 的 OpenAI 兼容服务自带 Prometheus 指标端点,vllm serve 起服务即有,零配置。直接抓一下看看。
curl -s http://127.0.0.1:8000/metrics \
| grep -E '^vllm:(num_requests|gpu_cache)'
有流量时的典型输出如下(截选):
vllm:num_requests_running{model_name="qwen3-8b"} 12
vllm:num_requests_waiting{model_name="qwen3-8b"} 3
vllm:gpu_cache_usage_perc{model_name="qwen3-8b"} 0.41
三行分别读出:12 个请求正在批内执行、3 个在排队、KV cache 用了 41%。日常最该盯的八个指标如下。
| 指标 | 类型 | 含义 |
|---|---|---|
vllm:prompt_tokens_total |
Counter | 累计输入 token 数 |
vllm:generation_tokens_total |
Counter | 累计生成 token 数 |
vllm:num_requests_running |
Gauge | 正在执行的请求数 |
vllm:num_requests_waiting |
Gauge | 排队等待的请求数 |
vllm:gpu_cache_usage_perc |
Gauge | KV cache 占用比,1 即满 |
vllm:time_to_first_token_seconds |
Histogram | 首 token 延迟分布 |
vllm:time_per_output_token_seconds |
Histogram | 每 token 间隔分布 |
vllm:request_success_total |
Counter | 成功完成请求数 |
类型决定用法:Counter 要套 rate() 看增速,Histogram 要套 histogram_quantile() 取分位数,Gauge 直接画曲线。比如实时生成吞吐就是 sum(rate(vllm:generation_tokens_total[5m]))。注意 V1 引擎落地后指标清单有过增删(如 prefix cache 命中率一组),完整清单见官方指标设计文档,以自己版本的 /metrics 实际输出为准。
两个 Counter 还能顺手校准容量参数:rate 后的输入加生成 token 数除以 rate 后的成功请求数,就是平均每请求的 token 长度。如果实测均值只有 3K 而 max-model-len 预留了 32K,第 6 篇讲过的容量浪费就摆在眼前——监控数据反哺启动参数,正是本篇的意义。
指标之外,vLLM 还支持 OpenTelemetry trace 上报(新版参数为 --traces otlp,旧版叫 --otlp-traces-endpoint,以 vllm serve --help 为准),可把单条请求的执行 span 发往 Jaeger 或 Tempo。日常看板用不到它,排查"某一条请求为什么慢"时再开。
3、GPU 指标采集
vLLM 的指标止步于引擎内部,GPU 本体的利用率、显存、温度不在其中,需要 NVIDIA 的 DCGM exporter 补位。它是 DCGM(Data Center GPU Manager)的 Prometheus 出口,Docker 一行接入。
docker run -d --name dcgm-exporter \
--gpus all --restart unless-stopped \
-p 9400:9400 \
nvcr.io/nvidia/k8s/dcgm-exporter:3.3.9
--gpus all 是最容易漏的参数:漏掉它容器内看不到 GPU,/metrics 里一行卡指标都不会有。起来后验证。
curl -s http://127.0.0.1:9400/metrics | grep -E '^DCGM_FI_DEV_GPU_UTIL'
常用的四个指标:DCGM_FI_DEV_GPU_UTIL(SM 利用率)、DCGM_FI_DEV_FB_USED 与 DCGM_FI_DEV_FB_FREE(显存已用与剩余,单位 MB)、DCGM_FI_DEV_GPU_TEMP(温度)。功耗类指标先记住位置,下一节的成本核算要从它取数。
4、Grafana 监控看板
采集端就绪,搭最小可视化栈:Prometheus 抓取 + Grafana 展示,一个 compose 文件搞定。
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
depends_on:
- prometheus
ports:
- "3300:3000"
Grafana 容器默认端口 3000 容易与既有服务撞车,这里映射到 3300。抓取配置把两个数据源指进来。
global:
scrape_interval: 15s
scrape_configs:
- job_name: vllm
static_configs:
- targets: ["192.168.1.10:8000"]
- job_name: dcgm
static_configs:
- targets: ["192.168.1.10:9400"]
docker compose up -d 后访问 3300,登录进面板添加 Prometheus 数据源即可画图。面板建议三行布局,从上到下对应"现在怎样、用户感受如何、还剩多少余量"。
- 第一行状态条:运行与等待请求数、生成 tokens/s、错误率
- 第二行延迟:TTFT 与 TPOT 各画 P50、P99 两条线
- 第三行容量:KV cache 百分比、GPU 利用率、显存、温度
TTFT P99 的查询语句直接抄:histogram_quantile(0.99, sum by (le) (rate(vllm:time_to_first_token_seconds_bucket[5m])))。不想从零拼面板,Grafana 面板库 搜 vLLM 有多个社区成品可导入,再按自己的指标名微调即可。
一个实战细节:Prometheus 默认只保留 15 天时序数据,第 6 节的成本核算要拉一周曲线,刚好够用,别再调短。15 秒的抓取间隔对延迟类指标已经足够精细,再小只会撑大存储。Grafana 里日常巡检看 1 小时窗口,容量与成本趋势看 7 天。
5、告警规则设计
看板是给人巡检的,告警是给系统值班的。三条规则分别盯容量、体验、硬件三类风险。
groups:
- name: vllm-alerts
rules:
- alert: KVCacheNearFull
expr: avg_over_time(vllm:gpu_cache_usage_perc[10m]) > 0.90
for: 10m
- alert: TTFTP99High
expr: histogram_quantile(0.99, sum by (le)
(rate(vllm:time_to_first_token_seconds_bucket[5m]))) > 3
for: 5m
- alert: GPUMemNearFull
expr: DCGM_FI_DEV_FB_USED /
(DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) > 0.95
for: 5m
规则写法见 Prometheus 告警规则文档,通知渠道用 Alertmanager 或 Grafana 自带告警接即时消息机器人均可。三条各自的含义与处置如下。
- KV cache 超 90% 持续 10 分钟:容量透支,抢占在即——降
max-model-len或上量化 - TTFT P99 超 3 秒持续 5 分钟:用户已体感变卡——查队列堆积、限流上游
- 显存余量低于 5%:OOM(Out Of Memory)前兆——查是否有其他进程挤进卡里
阈值是起点不是圣旨:3 秒对聊天偏松、对语音助手致命,按业务调。告警的价值在"先行",等错误率涨起来再响就晚了——这也是第一条不盯错误率而盯 KV cache 的原因。
另外克制告警数量:个人或小团队的服务,三条起步足矣。每条告警都必须对应一个你愿意半夜爬起来执行的动作,否则它会在第三周被静音——被静音的告警比没有告警更糟,因为它制造了"有监控"的错觉。
6、成本核算模型
监控搭好后,成本核算只需要一个模型:每百万 token 成本 = 核算期总成本 ÷ 核算期产出。总成本三块,产出从监控曲线读,都不用拍脑袋。
| 成本项 | 估算口径 | 示例(RTX 4090 主机) |
|---|---|---|
| 电费 | 加权平均功耗 × 电价 | 475 W × 1 元/度 ≈ 0.48 元/小时 |
| 折旧 | 整机 2 万元 ÷ 3 年 ÷ 8760 小时 | ≈ 0.76 元/小时 |
| 运维摊销 | 搭建与巡检人力折算 | ≈ 0.1 元/小时 |
平均功耗取 DCGM 功率曲线一周均值,产出取 rate() 积分出来的 token 数,比任何估算都准。算一次只要十行。
hours = 24 * 7 # 核算周期:一周
elec = 475 / 1000 * 1.0 # 电费,元/小时
dep = 20000 / (3 * 8760) # 折旧,元/小时
ops = 0.1 # 运维摊销,元/小时
tokens = 2.1e6 * hours # 产出,取自监控曲线(周均值)
cost = hours * (elec + dep + ops) / (tokens / 1e6)
print(f"{cost:.2f} 元/百万 token") # 0.64
示例配置算出 0.64 元/百万 token,对比 2026 年国内云端同级模型 1~2 元/百万 token 的输出价明显便宜。但优势有个前提——产出要撑得起来。与云 API 对比的通用公式是:本地划算当且仅当"云单价 × 日用量 > 日固定成本 + 边际电费",移项即得盈亏平衡日用量 = 日固定成本 ÷(云单价 − 本地边际单价)。
代入示例:日固定成本约 21 元(折旧加运维),本地边际单价约 0.23 元/百万 token(电费增量部分),云单价按 1.5 元计,平衡点约 17 百万 token/天。日均产出低于这个数,机器闲置折旧照摊——同样的硬件在 10% 利用率下单位成本会涨到 2 元/百万 token 以上,比云还贵。本地的成本优势与利用率成正比,这是买卡前唯一需要背的口诀。对比云价时注意口径:厂商通常输入、输出分开计价,缓存命中另算,拿自己业务的输入输出比例折算成等效单价再比,否则容易高估本地优势。数据不出门、无速率限制这些收益不进公式,但决策时往往比几毛钱差价更重。
7、容量决策仪表
第 6 篇给过一张"症状 → 处置"对照表,但那是事后翻日志;本篇把它升级成实时看曲线。监控跑两周,"该加卡、该加量化还是该换引擎"就不再是感觉题。
| 监控读数 | 诊断 | 处置 |
|---|---|---|
| KV 常态超 90%,waiting 常态大于 0 | KV cache 容量瓶颈 | 降 max-model-len;上量化(第 10 篇) |
| GPU 利用率超 95%,吞吐不再涨 | 算力瓶颈 | 加卡或换更强的卡 |
| TTFT P99 高,GPU 利用率低于 60% | 调度或预填充低效 | 调大 max-num-batched-tokens;试 SGLang(第 8 篇) |
| 功耗满载,吞吐平平 | 能效比差 | 投机解码(第 12 篇)或更激进量化 |
两条经验。其一,KV cache 水位线是最值钱的一条曲线:它同时预告延迟劣化与吞吐悬崖,第 11 篇的原理在这里变成可操作的仪表。其二,任何升级决策先用监控数据估算收益再动手——加一张卡能让 waiting 降多少,第 6 篇压测脚本加上本篇的曲线能给出相当准的预判,避免为了一个"感觉慢"去花一万五。
8、总结
本篇把推理服务的"看得见"补齐:六层指标选型、vLLM /metrics 八个核心指标、DCGM exporter 一行接入、Prometheus + Grafana 最小栈、三条告警,最后用监控数据落成成本模型与容量决策表。核心两句话:队列与 KV cache 是先行指标;成本优势与利用率成正比。
练习三题:
- 给现有 vLLM 服务接上最小监控栈,复现三行面板布局
- 用第 6 篇压测脚本逐步加压,观察 KV cache 曲线的拐点
- 以一周真实数据算出每百万 token 成本,与在用云 API 对比找盈亏平衡点
对于已经在跑本地推理的读者,建议本周末就把监控栈搭起来——它不改变任何服务行为,却让后续每一次调参、每一次扩容决策都有数可依。

