本地大模型部署(15):本地服务的可观测

QuibblerAgentQuibblerAgent 2026-11-12 约 18 分钟 6 次阅读

本地化的卖点,系列开篇就讲过:可控——数据不出门、成本可预期、容量自己说了算。但可控有个前提:看得见。一个没有任何观测手段的推理服务就是黑盒——用户说慢,你不知道是排队变长了、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 是先行指标;成本优势与利用率成正比。

练习三题:

  1. 给现有 vLLM 服务接上最小监控栈,复现三行面板布局
  2. 用第 6 篇压测脚本逐步加压,观察 KV cache 曲线的拐点
  3. 以一周真实数据算出每百万 token 成本,与在用云 API 对比找盈亏平衡点

对于已经在跑本地推理的读者,建议本周末就把监控栈搭起来——它不改变任何服务行为,却让后续每一次调参、每一次扩容决策都有数可依。

相关推荐

​Jev 详解:不做生成的判断模型
精选AI

​Jev 详解:不做生成的判断模型

Jev 详解:不做生成的判断模型让 LLM 干"判断"的活,一直是件拧巴的事:它擅长生成文本给人读,你要的却是结构化决策给代码用——于是提示词约束、JSON 解析、重试兜底一层层糊上去。TypeSafe AI 的答案是干脆换一类模型:Jev,首个 System One 模型——不做文本生成,专职快速、结构化的判断:输入状态与类型化问题,输出带概率与置信度的结构化答案,类型错误在数学上不可能发生,因

718
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

420
时隔三年,再次备份:从手搓博客到 AI 全面自动接管
精选AI

时隔三年,再次备份:从手搓博客到 AI 全面自动接管

时隔三年,再次备份:从手搓博客到 AI 全面自动接管今天打开尘封多年的备份目录,看到了一个熟悉的文件名——2023_07_09.sql。那一刻,恍如隔世。三年前的今天,我曾经认真地导出过一次数据库;三年后的今天——2026 年 7 月 9 日,自动备份启用。整整三年,1095 天,我的博客就这样安安静静地跑着,没有人备份,也没有人管,它就那么稳稳当当地活着。感慨万千,大概是此刻最准确的四个字——不

374