Langchain-Chatchat 如何监控 GPU 使用率?Prometheus 集成实践
在企业级大模型应用日益普及的今天,本地化部署的智能问答系统正成为保障数据安全与合规性的首选方案。Langchain-Chatchat 作为开源社区中广受关注的知识库问答框架,凭借其对私有文档的支持、模块化的架构设计以及与主流大语言模型(如 ChatGLM、Qwen)的良好兼容性,已被广泛应用于内部知识管理、智能客服和辅助决策等场景。
但随着业务负载的增长,一个现实问题逐渐浮现:当多个用户并发提问时,系统响应变慢甚至出现 OOM(内存溢出)错误——而这些异常往往发生在 GPU 资源被耗尽之后。更棘手的是,在缺乏监控手段的情况下,我们只能“事后诸葛亮”地去翻日志、查进程,却难以提前预警或定位瓶颈根源。
这正是可观测性缺失带来的典型痛点。AI 服务不同于传统 Web 应用,它的性能瓶颈常常隐藏在 GPU 利用率、显存占用和推理延迟背后。如果我们不能像观察 CPU 和内存那样直观地看到 GPU 的运行状态,就很难做到真正的可运维、可优化。
那么,如何让 AI 系统“透明”起来?
答案是引入 Prometheus —— 这个云原生生态中的明星监控工具。它不仅擅长采集时间序列数据,还具备强大的查询语言(PromQL)、灵活的服务发现机制,并能与 Grafana 无缝集成实现可视化。更重要的是,它的轻量级特性和 Python 客户端支持,使其非常适合嵌入到 Langchain-Chatchat 这类基于 FastAPI 构建的服务中。
让 GPU “说话”:从黑盒推理到指标暴露
Langchain-Chatchat 的核心流程包括文档解析、文本分块、向量嵌入、语义检索和 LLM 回答生成。其中,向量模型(如 BGE、m3e)和生成模型(如 ChatGLM3-6B)通常运行在 GPU 上,以满足低延迟的交互需求。一旦并发请求增多,GPU 成为性能瓶颈几乎是必然的。
但我们不能等到服务卡顿才去排查问题。理想的状态是:实时掌握每张 GPU 的利用率、显存使用情况,并结合请求量分析是否存在资源争用或泄漏风险。
这就需要将原本“沉默”的 GPU 变成“会说话”的监控对象。幸运的是,NVIDIA 提供了nvidia-smi工具,可以命令行方式获取详细的 GPU 状态信息:
nvidia-smi --query-gpu=index,name,utilization.gpu,memory.used,memory.total --format=csv输出示例:
0, NVIDIA A100, 78 %, 14520 MiB, 40960 MiB虽然这是文本格式,但结构清晰、字段明确,完全可以通过脚本定期采集并转换为标准监控指标。而 Prometheus 正好提供了一种简单的方式:通过 HTTP 暴露/metrics接口,返回符合其文本格式的时间序列数据。
于是,我们的思路就很清晰了:
- 在 Langchain-Chatchat 主进程中启动一个独立线程;
- 该线程定时执行
nvidia-smi命令,解析结果; - 将关键指标(GPU 利用率、显存使用量等)注册为 Prometheus 的 Gauge 类型;
- 启动内置 HTTP Server,暴露
/metrics端点供 Prometheus 抓取。
下面是具体的实现代码:
from prometheus_client import start_http_server, Gauge, Counter import subprocess import time import threading # 定义监控指标 gpu_utilization = Gauge('nvidia_gpu_utilization_percent', 'GPU Utilization (%)', ['gpu_id', 'model_name']) gpu_memory_used = Gauge('nvidia_gpu_memory_used_bytes', 'Used GPU Memory (bytes)', ['gpu_id']) gpu_memory_total = Gauge('nvidia_gpu_memory_total_bytes', 'Total GPU Memory (bytes)', ['gpu_id']) request_counter = Counter('chatchat_requests_total', 'Total number of requests processed', ['endpoint']) def collect_gpu_metrics(): try: result = subprocess.run([ 'nvidia-smi', '--query-gpu=index,name,utilization.gpu,memory.used,memory.total', '--format=csv,noheader,nounits' ], stdout=subprocess.PIPE, check=True, text=True) for line in result.stdout.strip().split('\n'): if not line.strip(): continue parts = [p.strip() for p in line.split(',')] gpu_id, model_name, util, mem_used, mem_total = parts # 单位转换:MiB -> bytes mem_used_bytes = int(mem_used) * 1024**2 mem_total_bytes = int(mem_total) * 1024**2 gpu_utilization.labels(gpu_id=gpu_id, model_name=model_name).set(float(util)) gpu_memory_used.labels(gpu_id=gpu_id).set(mem_used_bytes) gpu_memory_total.labels(gpu_id=gpu_id).set(mem_total_bytes) except Exception as e: print(f"Failed to collect GPU metrics: {e}") def metrics_collector_loop(interval=10): while True: collect_gpu_metrics() time.sleep(interval) # 启动 Prometheus 指标服务器(端口 8001) start_http_server(8001) threading.Thread(target=metrics_collector_loop, daemon=True).start() # 示例:记录一次 API 请求 request_counter.labels(endpoint="/chat").inc()这段代码可以直接集成进 Langchain-Chatchat 的 FastAPI 启动逻辑中。注意以下几点工程细节:
- 非阻塞采集:使用后台线程执行
nvidia-smi,避免影响主服务响应。 - 采集频率:建议设置为 10~15 秒。过于频繁(如 1s)会增加系统开销;太稀疏则可能错过瞬时峰值。
- 异常处理:若 GPU 驱动未安装或命令执行失败,应捕获异常并记录日志,但不影响主服务启动。
- 单位统一:Prometheus 推荐使用 base unit(如 bytes),便于后续计算和告警判断。
- 标签设计:通过
gpu_id和model_name标签区分多卡环境下的不同设备,支持精细化分析。
启动后,访问http://<server>:8001/metrics即可看到类似如下内容:
# HELP nvidia_gpu_utilization_percent GPU Utilization (%) # TYPE nvidia_gpu_utilization_percent gauge nvidia_gpu_utilization_percent{gpu_id="0",model_name="NVIDIA A100"} 78.0 # HELP nvidia_gpu_memory_used_bytes Used GPU Memory (bytes) # TYPE nvidia_gpu_memory_used_bytes gauge nvidia_gpu_memory_used_bytes{gpu_id="0"} 15225792000这些数据就是 Prometheus 的“食物”。
构建完整的监控闭环
接下来,我们需要搭建一套完整的监控体系,把原始指标变成有价值的洞察。
典型的架构如下:
graph TD A[Langchain-Chatchat] -->|HTTP /metrics| B(Prometheus Server) B --> C[Grafana] B --> D[Alertmanager] C --> E[可视化仪表盘] D --> F[钉钉/邮件通知]Prometheus 配置抓取任务
在prometheus.yml中添加 scrape job:
scrape_configs: - job_name: 'langchain-chatchat' static_configs: - targets: ['chatchat-server:8001'] scrape_interval: 15sPrometheus 会每隔 15 秒主动拉取一次/metrics接口的数据,并存储在其本地 TSDB 中。
Grafana 可视化:一眼看清系统状态
通过 Grafana 添加 Prometheus 为数据源后,即可创建丰富的仪表盘。例如:
- GPU 利用率趋势图:查看过去一小时各 GPU 的负载变化;
- 显存使用率饼图:按 GPU ID 分组展示当前显存占用比例;
- 请求速率与延迟对比图:将业务指标与资源指标联动分析,识别高负载下的性能拐点。
你还可以使用 PromQL 编写表达式,动态计算显存使用百分比:
(nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes) * 100将其绘制成折线图,就能直观看出哪张卡接近满载。
告警规则:从被动响应到主动防御
真正的价值在于“预防”。我们可以设置一些关键告警规则,及时发现问题:
- alert: HighGPUMemoryUsage expr: (nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes) > 0.9 for: 5m labels: severity: warning annotations: summary: "GPU {{ $labels.gpu_id }} 显存使用过高" description: "GPU {{ $labels.gpu_id }} 显存使用已达 {{ $value | printf \"%.2f\" }}%,持续超过5分钟,请检查是否有模型加载异常或泄漏。"当某张 GPU 的显存使用连续 5 分钟超过 90% 时,Alertmanager 就会触发通知,发送到企业微信、钉钉或邮件,提醒运维人员介入。
类似的规则还可以扩展至:
- GPU 温度过高(>85°C)
- 长时间低利用率(可能是服务卡死)
- 请求成功率下降伴随 GPU 异常
实际收益:不只是“看图表”
这套监控方案上线后,带来的改变远不止多了一个仪表盘那么简单。
快速定位性能瓶颈
曾经有一次,用户反馈问答响应越来越慢。以前我们要登录服务器逐条查日志、看进程,而现在只需打开 Grafana:
- 发现 GPU 0 的利用率长期处于 98% 以上;
- 显存使用率也稳定在 95% 左右;
- 同时请求队列长度持续增长。
结论立即明确:GPU 已成为系统瓶颈。进一步分析历史趋势,发现是从某个新模型上线后开始恶化的。最终确认是 embedding 模型 batch size 设置过大导致资源争用。调整参数后,问题迎刃而解。
支撑资源调度与成本控制
在多团队共用一台 GPU 服务器的场景下,如何公平分配资源?现在有了数据支撑:
- 通过标签区分不同服务实例;
- 统计各团队的日均 GPU 占用时长;
- 结合业务优先级制定配额策略;
- 为未来采购或扩容提供依据。
甚至可以推动建立“AI 资源账单”,让资源消耗变得可衡量、可管理。
提升系统的可维护性
监控不仅是“救火”,更是“防火”。通过长期观测,我们能回答这些问题:
- 当前硬件能否支撑明年预期的用户增长?
- 是否有必要引入模型卸载(offloading)或量化技术?
- 哪些接口最容易引发高负载?是否需要限流?
这些都让 AI 系统从“能跑”走向“好管”。
一点思考:AI 工程化的必经之路
Langchain-Chatchat 本身是一个优秀的工具,但它代表的是一类典型的 AI 应用:功能强大、开发便捷,但在生产环境中仍面临稳定性、可观测性和可维护性的挑战。
将 Prometheus 集成进去,看似只是一个技术动作,实则是向AI 工程化迈出的关键一步。它意味着我们不再把模型当作黑箱,而是将其纳入统一的运维体系,用标准化的方式去度量、分析和优化。
这种转变的价值在于:
- 降低运维门槛:新手也能通过看板快速理解系统状态;
- 提升交付质量:监控成为 CI/CD 流水线的一部分,确保每次发布都不会引发资源异常;
- 积累领域经验:长期数据沉淀有助于形成最佳实践,比如“XX 模型在 batch_size=4 时 GPU 利用率最优”。
未来,这条路径还可以继续延伸:结合 cAdvisor 监控容器资源、使用 DCGM 获取更精细的 GPU 性能事件、基于历史负载预测自动扩缩容……每一步都在让 AI 系统变得更可靠、更智能。
这种高度集成的设计思路,正引领着智能问答系统向更可管、更可控的方向演进。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考