返回博客

用 Grafana 和 Prometheus 监控 vLLM 生产环境

在服务器上跑大语言模型是一回事,实时了解模型内部运行状态是另一回事。当你在 A100 服务器上用 vLLM 部署 Qwen3.6-27B 时,你需要实时掌握 GPU 利用率、请求吞吐量、token 生成速率、队列深度等关键指标。

本文记录我们在两台 A100 服务器上部署 vLLM 监控面板的完整过程,涵盖架构设计、部署步骤、常见陷阱和关键指标解读。

vLLM 监控面板

为什么要监控 vLLM?

vLLM 是大语言模型推理的行业标准引擎,从本地部署到云端大规模服务都在使用。但如果没有监控,你就是在盲飞:

好消息是:vLLM 内置了 Prometheus 指标端点——386 个指标,通过 /metrics 端点直接暴露。你只需要 Prometheus 来采集,Grafana 来可视化。

架构概览

监控栈非常简单轻量:

vLLM (端口 8391) → /metrics 端点 (Prometheus 格式,386 个指标)
    ↓
Prometheus (端口 9090) → 每 15 秒采集一次 /metrics
    ↓
Grafana (端口 3000) → 使用 vLLM Monitoring V2 面板可视化 (ID: 24756)

三个组件运行在同一台服务器上。vLLM 提供服务,Prometheus 采集指标,Grafana 渲染面板。整个栈只需要几个 Docker 容器。

第一步:发现 vLLM 内置指标

vLLM 在 http://<服务器>:8391/metrics 暴露 Prometheus 格式的指标。一个简单的 curl 就能发现 386 个独立指标,覆盖:

指标带有完整的 Prometheus 标签,便于在 Grafana 中过滤和聚合。

第二步:部署 Prometheus

Prometheus 以 Docker 容器方式部署,使用 host 网络模式——这很关键,因为容器需要访问宿主机上 vLLM 的 /metrics 端点。

docker run -d \
  --name prometheus \
  --network host \
  -v ~/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
  -v ~/monitoring/prometheus/data:/prometheus \
  prom/prometheus:latest

配置文件 prometheus.yml 指定 vLLM 指标端点:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'vllm'
    static_configs:
      - targets: ['localhost:8391']
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

关键要点:使用 --network host 是必须的。不使用的话,容器无法访问 localhost:8391,因为 Docker 默认桥接网络会将容器与宿主机的回环接口隔离。

第三步:部署 Grafana

Grafana 提供可视化层。我们使用官方 Docker 镜像并挂载持久化存储:

docker run -d \
  --name grafana \
  --network host \
  -v ~/monitoring/grafana:/var/lib/grafana \
  -e GF_SECURITY_ADMIN_PASSWORD=admin \
  grafana/grafana:latest

常见问题:Grafana 数据目录必须有写入权限。如果遇到错误,执行:

chmod 777 ~/monitoring/grafana

端口冲突:在办公室 A100 服务器上,端口 3000 已被 Node.js 进程占用。解决方案是将 Grafana 映射到 3001 端口:

-p 3001:3000

第四步:配置数据源

Grafana 启动后,通过 API 添加 Prometheus 数据源:

curl -s -X POST http://localhost:3000/api/datasources \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Prometheus",
    "type": "prometheus",
    "url": "http://localhost:9090",
    "access": "proxy",
    "isDefault": true
  }'

http://<服务器>:3000 使用 admin/admin 登录验证连接。

第五步:导入 vLLM 监控面板

Grafana 社区有一个优秀的 vLLM 监控面板——ID 24756(vLLM Monitoring V2),开箱即用地提供全面的可视化。

重要:面板必须通过 /api/dashboards/db API 端点导入,而不是已废弃的 /api/dashboards/import。后者会静默失败。

curl -s -X POST http://localhost:3000/api/dashboards/db \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d @vllm-dashboard.json

面板包含以下监控项:

两台服务器的部署实践

我们在两台不同的 A100 服务器上部署了这套监控栈,每台都遇到了独特的挑战:

服务器 1:双 A100 服务器 (172.24.217.58)

服务器 2:办公室 A100 服务器 (172.24.168.225)

关键指标解读

运行面板数天后,我们最关注的指标如下:

指标 为什么重要 告警阈值
GPU 显存使用 及早发现显存泄漏 >85%
请求队列长度 用户体验指标 >10 等待中
Token/秒 吞吐量健康检查 <基线 50%
前缀缓存命中率 优化效果追踪 关注趋势
请求延迟 P99 尾部延迟监控 >中位数 2 倍
KV Cache Block 数 显存压力信号 >90% 已分配

常见陷阱与解决方案

1. Docker 网络模式

问题:容器无法访问 localhost 上的 vLLM。 解决:Prometheus 和 Grafana 容器都使用 --network host

2. Grafana 数据目录权限

问题:Grafana 启动时权限拒绝报错。 解决:启动前对挂载的数据目录执行 chmod 777

3. 面板导入 API

问题:面板导入静默失败。 解决:使用 /api/dashboards/db 端点,而非已废弃的 /api/dashboards/import

4. Docker Hub 超时

问题docker pull 在 GFW 后超时挂起。 解决:在 /etc/docker/daemon.json 中配置镜像加速器。

5. 端口冲突

问题:默认端口(3000、9090)已被占用。 解决:用 ss -tlnp | grep <端口> 检查,改用其他端口。

部署成果

监控面板提供了 vLLM 运行的实时可见性。部署后的关键观察:

vLLM 监控面板

总结

在生产环境中监控 vLLM 并不需要复杂的架构。借助内置的 /metrics 端点、Prometheus 和 Grafana 社区面板,你可以在 30 分钟内部署一套完整的监控栈。

关键经验:

  1. 使用 Docker host 网络模式实现容器与宿主机通信
  2. 通过 /api/dashboards/db API 导入面板
  3. 设置正确的 Grafana 数据目录权限
  4. 防火墙后配置 Docker 镜像加速器
  5. 关注核心指标——GPU 显存、队列深度和 token 吞吐量是你的北极星指标

有了这套方案,你可以在问题变成故障之前发现它们,并有数据支撑 LLM 服务基础设施的持续优化。


故障排除:Datasource ${DS_PROMETHEUS} was not found

导入 Grafana Dashboard(ID 24756)后,如果面板报错:

PanelQueryRunner Error {message: 'Datasource ${DS_PROMETHEUS} was not found'}

原因:Dashboard JSON 中的数据源引用使用了变量 ${DS_PROMETHEUS},但 Grafana 实例中没有定义该模板变量,导致无法解析为实际的数据源 UID。

解决方案:将 Dashboard JSON 中的 ${DS_PROMETHEUS} 替换为 Prometheus 数据源的实际 UID。

# 1. 获取 Prometheus 数据源 UID
curl -s -u admin:your_password http://localhost:3000/api/datasources | python3 -m json.tool
# 找到 "uid": "xxxxxxxxx" 的值

# 2. 导出 Dashboard JSON
curl -s -u admin:your_password http://localhost:3000/api/dashboards/uid/vllm-master-v2 > dashboard.json

# 3. 替换变量(用 python3 处理 JSON)
python3 -c "
import json
with open('dashboard.json', 'r') as f:
    data = json.load(f)
json_str = json.dumps(data)
json_str = json_str.replace('\${DS_PROMETHEUS}', 'YOUR_PROMETHEUS_UID')
with open('dashboard_fixed.json', 'w') as f:
    f.write(json_str)
print('Replaced successfully')
"

# 4. 重新导入
curl -s -u admin:your_password -X POST http://localhost:3000/api/dashboards/db \
  -H 'Content-Type: application/json' \
  -d @dashboard_fixed.json

预防:导入 Dashboard 前,确保 Grafana 中已配置 Prometheus 数据源,且 Dashboard JSON 中的变量引用能正确解析。如果使用的是 Grafana 官方 Dashboard(如 ID 24756),建议导入后检查数据源引用是否正确。


有问题或建议?欢迎在 GitHub 上交流,或加入我们的 Discord 社区