Lychee-Rerank-MM部署教程:Prometheus+Grafana监控GPU利用率与QPS指标

1. 引言

当你成功部署了Lychee多模态重排序模型,看着它在服务器上稳定运行,处理着图文检索任务时,一个现实的问题可能会浮现在脑海:它到底运行得怎么样?

GPU的显存用了多少?模型推理的速度快不快?每秒能处理多少个请求?这些指标就像汽车的仪表盘,没有它们,你就是在“盲开”。你不知道系统是否在高效运转,也不知道何时会遇到性能瓶颈。

这就是为什么我们需要监控。今天,我要带你做的,就是为你的Lychee-Rerank-MM模型装上一个专业的“仪表盘”。我们将使用Prometheus来收集GPU利用率和QPS(每秒查询数)指标,然后用Grafana把这些数据变成直观的图表。这样,你就能一眼看清模型的运行状态,及时发现并解决问题。

2. 监控方案整体设计

在开始动手之前,我们先花几分钟了解一下整个监控系统是怎么工作的。这能帮你更好地理解每个步骤的意义。

我们的监控架构很简单,但很实用,主要包含三个核心组件:

  1. 数据采集器(Prometheus Node Exporter + 自定义Exporter):这是系统的“眼睛”。Node Exporter负责收集服务器的基础指标,比如CPU、内存、磁盘使用率。我们还需要一个自定义的Exporter来专门收集GPU的显存使用、GPU利用率,以及Lychee模型服务的QPS指标。

  2. 时序数据库与抓取器(Prometheus Server):这是系统的“大脑”和“仓库”。Prometheus Server会按照你设定的时间间隔(比如每15秒),主动去各个Exporter“抓取”指标数据,然后把这些时间序列数据存储在自己的数据库中。

  3. 数据可视化平台(Grafana):这是系统的“仪表盘”。Grafana从Prometheus里读取数据,然后让你通过拖拽的方式,创建出各种漂亮的图表和监控面板,比如GPU利用率曲线图、QPS实时统计表。

它们之间的关系,你可以参考下面的流程图:

graph TD
    A[Lychee-Rerank-MM 服务] -->|暴露/metrics端点| B[自定义Python Exporter]
    C[服务器硬件] -->|系统指标| D[Prometheus Node Exporter]
    B --> E[Prometheus Server<br/>抓取并存储指标]
    D --> E
    E -->|作为数据源| F[Grafana]
    F -->|查询与展示| G[可视化监控面板]

整个部署过程,我们会按照“先搭建基础设施,再完善监控指标”的思路进行。准备好了吗?我们开始吧。

3. 基础监控环境搭建

我们先来部署Prometheus和Grafana这两个核心服务。为了保持环境干净和便于管理,我强烈建议使用Docker来运行它们。

3.1 创建监控配置文件目录

首先,我们在服务器上创建一个目录,用来存放所有的配置文件。

mkdir -p /opt/monitoring/lychee
cd /opt/monitoring/lychee

3.2 部署Prometheus

Prometheus需要一个配置文件来告诉它去哪里抓取数据。我们来创建这个文件。

  1. 创建Prometheus配置文件 prometheus.yml
# /opt/monitoring/lychee/prometheus.yml
global:
  scrape_interval: 15s # 每15秒抓取一次数据
  evaluation_interval: 15s # 每15秒评估一次规则

scrape_configs:
  # 监控Prometheus自身
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # 监控服务器节点(通过Node Exporter)
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100'] # 注意这里用了服务名

  # 监控Lychee自定义指标(这是我们后面要创建的)
  - job_name: 'lychee-exporter'
    static_configs:
      - targets: ['lychee-exporter:8000'] # 注意这里用了服务名
  1. 使用Docker Compose一键启动

为了简化管理,我们创建一个 docker-compose.yml 文件,把Prometheus、Grafana和Node Exporter的服务定义都放在里面。

# /opt/monitoring/lychee/docker-compose.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=30d' # 数据保留30天
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123 # 首次登录密码,请务必修改!
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:
  1. 启动服务

在存放 docker-compose.yml 文件的目录下,运行以下命令:

docker-compose up -d

这个命令会在后台启动所有服务。你可以用 docker-compose ps 查看服务状态,应该能看到三个容器都在运行。

  1. 验证Prometheus

打开浏览器,访问 http://你的服务器IP:9090。如果能看到Prometheus的Web界面,并且点击上方菜单栏的 Status -> Targets,能看到 prometheusnode 两个job的状态都是 UP,那就说明基础监控服务已经跑起来了。(lychee-exporter 的状态目前会是 DOWN,因为我们还没部署它,这是正常的)。

3.3 初步配置Grafana

  1. 登录Grafana: 访问 http://你的服务器IP:3000,使用用户名 admin 和密码 admin123 登录。登录后第一件事,就是在提示中修改这个默认密码!

  2. 添加Prometheus数据源

    • 点击左侧导航栏的齿轮图标(Configuration),选择 Data Sources
    • 点击 Add data source,选择 Prometheus
    • 在URL一栏填写 http://prometheus:9090(注意,这里用的是Docker Compose网络内的服务名,不是localhost)。
    • 点击 Save & Test,如果显示“Data source is working”,就说明连接成功了。

好了,监控平台的基础架子已经搭好了。接下来,我们要解决最关键的问题:如何让Prometheus获取到Lychee模型的GPU和QPS数据。

4. 自定义指标采集器开发

Prometheus的Node Exporter能提供通用的系统指标,但对于我们特定的AI模型服务,比如GPU显存使用详情、模型推理的QPS,就需要我们自己来暴露这些指标。我们将编写一个Python程序来充当这个“桥梁”。

4.1 创建自定义Exporter项目

回到我们的监控目录,创建一个子目录来存放这个采集器。

mkdir -p /opt/monitoring/lychee/lychee_exporter
cd /opt/monitoring/lychee/lychee_exporter

4.2 编写指标采集脚本

创建一个名为 exporter.py 的文件,内容如下。这个脚本会做三件事:1. 启动一个Web服务提供/metrics端点;2. 定期收集GPU指标;3. 统计Lychee服务的QPS。

#!/usr/bin/env python3
# /opt/monitoring/lychee/lychee_exporter/exporter.py

import time
import psutil
import subprocess
from datetime import datetime
from prometheus_client import start_http_server, Gauge, Counter, Histogram
import threading
import re
import logging

# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 定义Prometheus指标
# GPU指标
GPU_MEMORY_USED = Gauge('lychee_gpu_memory_used_bytes', 'GPU memory used by Lychee process (bytes)')
GPU_MEMORY_TOTAL = Gauge('lychee_gpu_memory_total_bytes', 'Total GPU memory (bytes)')
GPU_UTILIZATION = Gauge('lychee_gpu_utilization_percent', 'GPU utilization percentage')
GPU_POWER_USAGE = Gauge('lychee_gpu_power_usage_watts', 'GPU power usage in watts')
GPU_TEMPERATURE = Gauge('lychee_gpu_temperature_celsius', 'GPU temperature in Celsius')

# QPS指标
LYCHEE_QPS = Gauge('lychee_queries_per_second', 'Current queries per second')
LYCHEE_TOTAL_QUERIES = Counter('lychee_total_queries', 'Total number of queries processed')
LYCHEE_REQUEST_DURATION = Histogram('lychee_request_duration_seconds', 'Duration of Lychee inference requests')

# 系统指标(补充Node Exporter)
PROCESS_CPU_PERCENT = Gauge('lychee_process_cpu_percent', 'CPU percentage used by Lychee process')
PROCESS_MEMORY_MB = Gauge('lychee_process_memory_mb', 'Memory used by Lychee process (MB)')

class LycheeMonitor:
    def __init__(self):
        self.last_query_count = 0
        self.last_update_time = time.time()
        self.qps_window = []  # 用于计算滑动窗口QPS
        self.lychee_pid = self._find_lychee_pid()
        
    def _find_lychee_pid(self):
        """查找Lychee服务的进程ID"""
        try:
            # 根据你的启动方式调整进程名
            for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
                try:
                    cmdline = ' '.join(proc.info['cmdline'] or [])
                    if 'app.py' in cmdline and 'lychee' in cmdline.lower():
                        logger.info(f"Found Lychee process: PID {proc.info['pid']}")
                        return proc.info['pid']
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
        except Exception as e:
            logger.error(f"Error finding Lychee PID: {e}")
        return None
    
    def get_gpu_metrics(self):
        """使用nvidia-smi获取GPU指标"""
        try:
            result = subprocess.run(
                ['nvidia-smi', '--query-gpu=memory.used,memory.total,utilization.gpu,power.draw,temperature.gpu',
                 '--format=csv,noheader,nounits'],
                capture_output=True,
                text=True,
                timeout=5
            )
            
            if result.returncode == 0:
                # 解析输出,假设只有一块GPU,多GPU需要调整
                values = result.stdout.strip().split(', ')
                if len(values) >= 5:
                    # 内存单位转换:MB -> Bytes
                    memory_used_mb = float(values[0])
                    memory_total_mb = float(values[1])
                    GPU_MEMORY_USED.set(memory_used_mb * 1024 * 1024)  # MB to Bytes
                    GPU_MEMORY_TOTAL.set(memory_total_mb * 1024 * 1024) # MB to Bytes
                    GPU_UTILIZATION.set(float(values[2]))
                    GPU_POWER_USAGE.set(float(values[3]))
                    GPU_TEMPERATURE.set(float(values[4]))
                    logger.debug(f"GPU Metrics updated: {memory_used_mb}/{memory_total_mb} MB, {values[2]}% util")
        except Exception as e:
            logger.error(f"Error getting GPU metrics: {e}")
            # 设置指标为-1表示获取失败
            GPU_MEMORY_USED.set(-1)
            GPU_UTILIZATION.set(-1)
    
    def get_process_metrics(self):
        """获取Lychee进程的CPU和内存使用情况"""
        if self.lychee_pid:
            try:
                process = psutil.Process(self.lychee_pid)
                PROCESS_CPU_PERCENT.set(process.cpu_percent(interval=0.1))
                PROCESS_MEMORY_MB.set(process.memory_info().rss / 1024 / 1024)  # Bytes to MB
            except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
                logger.warning(f"Could not get process metrics: {e}")
                self.lychee_pid = self._find_lychee_pid()  # 尝试重新查找PID
    
    def update_qps_metric(self):
        """更新QPS指标(这里需要根据你的实际日志或接口来统计)"""
        # 方法1:通过分析日志文件(示例)
        current_time = time.time()
        try:
            # 假设你的Lychee服务日志在/tmp/lychee_server.log
            # 这里统计包含特定关键词(如“Query processed”)的行数
            log_count = 0
            try:
                with open('/tmp/lychee_server.log', 'r') as f:
                    for line in f:
                        if 'Query processed' in line or 'inference_time' in line: # 根据你的日志调整
                            log_count += 1
            except FileNotFoundError:
                pass
            
            # 计算当前窗口的QPS(简单示例)
            self.qps_window.append((current_time, log_count))
            # 只保留最近10秒的数据
            self.qps_window = [(t, c) for t, c in self.qps_window if current_time - t < 10]
            
            if len(self.qps_window) > 1:
                time_span = self.qps_window[-1][0] - self.qps_window[0][0]
                query_span = self.qps_window[-1][1] - self.qps_window[0][1]
                if time_span > 0:
                    current_qps = query_span / time_span
                    LYCHEE_QPS.set(current_qps)
                    logger.debug(f"Current QPS: {current_qps:.2f}")
            
            # 更新总查询数(这是一个Counter,只增不减)
            if log_count > self.last_query_count:
                LYCHEE_TOTAL_QUERIES.inc(log_count - self.last_query_count)
                self.last_query_count = log_count
                
        except Exception as e:
            logger.error(f"Error updating QPS: {e}")
    
    def collect_all_metrics(self):
        """收集所有指标"""
        logger.info("Collecting metrics...")
        self.get_gpu_metrics()
        self.get_process_metrics()
        self.update_qps_metric()
        logger.info("Metrics collection completed")

def main():
    # 启动Prometheus指标的HTTP服务器,端口8000
    start_http_server(8000)
    logger.info("Lychee Exporter started on port 8000")
    
    monitor = LycheeMonitor()
    
    # 每5秒收集一次指标
    while True:
        try:
            monitor.collect_all_metrics()
        except Exception as e:
            logger.error(f"Error in main collection loop: {e}")
        time.sleep(5)

if __name__ == '__main__':
    main()

4.3 创建依赖文件与启动脚本

  1. 创建 requirements.txt
# /opt/monitoring/lychee/lychee_exporter/requirements.txt
prometheus-client>=0.20.0
psutil>=5.9.0
  1. 创建 Dockerfile(用于容器化部署):
# /opt/monitoring/lychee/lychee_exporter/Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 安装nvidia-smi命令(如果需要从容器内调用宿主机的nvidia-smi,通常不需要,采用主机挂载方式)
# RUN apt-get update && apt-get install -y --no-install-recommends nvidia-utils-535 && rm -rf /var/lib/apt/lists/*

COPY exporter.py .

EXPOSE 8000

CMD ["python", "exporter.py"]
  1. 更新 docker-compose.yml

我们需要把自定义的Exporter也加入到Docker Compose的管理中。编辑之前的 docker-compose.yml 文件,在 services: 部分添加以下内容:

  lychee-exporter:
    build: ./lychee_exporter  # 指向我们刚创建的目录
    container_name: lychee-exporter
    restart: unless-stopped
    ports:
      - "8000:8000"
    # 关键:将宿主机的GPU工具和日志挂载到容器内
    volumes:
      - /usr/bin/nvidia-smi:/usr/bin/nvidia-smi:ro
      - /tmp/lychee_server.log:/tmp/lychee_server.log:ro
      - /proc:/host/proc:ro # 用于psutil读取进程信息
    # 让容器可以访问宿主机的GPU设备(如果需要)
    # devices:
    #   - /dev/nvidia0:/dev/nvidia0
    #   - /dev/nvidiactl:/dev/nvidiactl
    #   - /dev/nvidia-uvm:/dev/nvidia-uvm
    # 或者使用runtime(推荐):
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    networks:
      - monitoring

重要说明:上面的 volumes 挂载是将宿主机的 nvidia-smi 命令和Lychee的日志文件映射到容器内,这样容器里的Python脚本才能调用它们。/proc 的挂载是为了让 psutil 能读取到宿主机的进程信息。

4.4 启动自定义Exporter

现在,回到 /opt/monitoring/lychee 目录,重新启动所有服务,这会包含我们新建的Exporter。

docker-compose down  # 先停止旧服务
docker-compose up -d --build lychee-exporter  # 构建并启动新的exporter容器
docker-compose up -d  # 启动所有服务

使用 docker-compose logs -f lychee-exporter 查看Exporter的日志,确认没有报错,并且看到“Lychee Exporter started on port 8000”的信息。

4.5 验证指标暴露

访问 http://你的服务器IP:8000/metrics,你应该能看到Prometheus格式的指标数据,例如:

# HELP lychee_gpu_memory_used_bytes GPU memory used by Lychee process (bytes)
# TYPE lychee_gpu_memory_used_bytes gauge
lychee_gpu_memory_used_bytes 1.23456789e+09
# HELP lychee_gpu_utilization_percent GPU utilization percentage
# TYPE lychee_gpu_utilization_percent gauge
lychee_gpu_utilization_percent 45.0
...

同时,再次访问Prometheus的Targets页面 (http://IP:9090/targets),现在应该能看到 lychee-exporter 的状态也变成了 UP

至此,数据采集的“桥梁”已经架设完毕,Prometheus正在定期抓取我们模型的专属指标。最后一步,就是在Grafana上打造一个美观实用的仪表盘了。

5. Grafana监控面板配置

现在,所有数据都已流入Prometheus。我们将在Grafana中创建一个专属面板,将这些数据可视化。

5.1 创建新的Dashboard

  1. 在Grafana左侧导航栏,点击 Dashboards -> New dashboard
  2. 点击 Add visualization 来添加第一个图表。

5.2 添加关键监控图表

我们将添加几个核心图表来监控Lychee服务的健康度。

图表1:GPU显存使用率

  • 面板类型:选择 StatGauge(仪表盘)来显示实时百分比。
  • 查询语句
    (lychee_gpu_memory_used_bytes / lychee_gpu_memory_total_bytes) * 100
    
  • 设置
    • Title: GPU显存使用率
    • Unit: Percent (0-100)
    • Thresholds: 可以设置绿色(<80%),黄色(80-90%),红色(>90%)来预警。

图表2:GPU利用率折线图

  • 面板类型:选择 Time series(时间序列图)。
  • 查询语句
    lychee_gpu_utilization_percent
    
  • 设置
    • Title: GPU利用率 (%)
    • Unit: Percent (0-100)
    • 可以添加一条 avg_over_time(lychee_gpu_utilization_percent[5m]) 的查询来显示5分钟平均线,让曲线更平滑。

图表3:QPS(每秒查询数)折线图

  • 面板类型:选择 Time series
  • 查询语句
    lychee_queries_per_second
    
    *如果QPS指标波动较大,可以使用 rate() 函数从Counter计算(如果lychee_total_queries是Counter的话):
    rate(lychee_total_queries[5m])
    
  • 设置
    • Title: 请求QPS
    • Unit: Queries/sec (QPS)

图表4:总处理查询数

  • 面板类型:选择 Stat
  • 查询语句
    lychee_total_queries
    
  • 设置
    • Title: 总处理查询数
    • Unit: 无(或选择 none

图表5:进程内存使用

  • 面板类型:选择 Time series
  • 查询语句
    lychee_process_memory_mb
    
  • 设置
    • Title: Lychee进程内存 (MB)
    • Unit: megabytes (MB)

5.3 优化面板布局与告警

  1. 拖拽调整:将上述图表在面板上合理布局,例如将GPU相关指标放在一行,QPS和请求数放在另一行。
  2. 添加告警(可选但建议):
    • 点击图表标题 -> Edit -> Alert -> Create alert
    • 例如,为“GPU显存使用率”创建告警规则:当 (lychee_gpu_memory_used_bytes / lychee_gpu_memory_total_bytes) * 100 > 90 持续5分钟时,触发告警。
    • 配置告警通知渠道(需要在Grafana中提前配置好Email、Slack、钉钉、企业微信等)。
  3. 保存Dashboard:点击顶部 Save dashboard,给它起个名字,比如 Lychee-Rerank-MM 服务监控

现在,你的专属监控仪表盘就创建完成了!它应该类似下图所示,让你对服务的运行状态一目了然。

graph TD
    subgraph “Grafana 监控面板”
        A[GPU显存使用率<br/>仪表盘] --> B[整体服务健康视图]
        C[GPU利用率折线图] --> B
        D[请求QPS折线图] --> B
        E[总处理查询数<br/>统计值] --> B
        F[进程内存使用折线图] --> B
    end
    B --> G{状态判断}
    G -->|指标异常| H[触发告警通知]
    G -->|指标正常| I[持续稳定运行]

6. 总结

通过这篇教程,我们完成了一件非常重要的事情:为Lychee-Rerank-MM模型服务装上了“眼睛”和“仪表盘”。

我们来回顾一下关键步骤和收获:

  1. 搭建了监控基础设施:用Docker Compose快速部署了Prometheus和Grafana,这是所有监控数据的存储和展示中心。
  2. 开发了自定义指标采集器:这是本次实践的核心。我们写了一个Python程序,它能够读取 nvidia-smi 的输出获取GPU指标,并通过分析服务日志(或未来可集成更精确的API)来统计QPS,最后以Prometheus能理解的格式暴露出来。
  3. 配置了可视化仪表盘:在Grafana中,我们将冰冷的数字变成了直观的图表。GPU利用率曲线、显存使用仪表、QPS波动图,这些都能让你瞬间把握服务的运行脉搏。

这套监控系统能帮你做什么?

  • 性能评估:清晰看到模型在不同负载下的GPU利用率和QPS,为资源扩容或优化提供数据支撑。
  • 故障预警:设置告警规则后,能在显存即将耗尽或QPS异常下跌时收到通知,避免服务不可用。
  • 成本优化:观察资源使用率,判断当前服务器配置是否合理,是否存在资源浪费。
  • 瓶颈分析:当QPS上不去时,可以结合GPU利用率和进程CPU使用率,快速判断瓶颈是在计算、内存还是I/O。

下一步可以做什么?

  • 更精确的QPS统计:目前我们通过分析日志文件来统计,这可能不够实时和准确。更优的方案是在Lychee服务的代码中直接埋点,在每次处理请求时递增一个计数器,并在一个独立的HTTP端点暴露这个指标。
  • 监控推理延迟:在自定义Exporter中增加对请求耗时的统计和分桶(Histogram),监控P50、P90、P99等延迟指标,这对评估用户体验至关重要。
  • 业务指标监控:除了系统指标,还可以考虑暴露一些业务指标,比如不同指令(Instruction)下的请求分布、重排序得分的分布等。
  • 部署优化:将自定义Exporter、Prometheus、Grafana的配置和部署脚本化、自动化,方便在其他环境快速复制这套监控体系。

监控不是一次性的任务,而是一个持续的过程。希望这套方案能成为你运维Lychee-Rerank-MM乃至其他AI模型服务的得力助手,让技术运维变得更加可观测、可掌控。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐