RexUniNLU可观测性建设:Prometheus指标埋点+Grafana监控面板配置教程
1. 引言:为什么你的NLU服务需要可观测性?
想象一下,你刚刚部署了RexUniNLU服务,它运行得不错,用户反馈也挺好。但突然有一天,你发现某个接口的响应时间从50毫秒飙升到了5秒,用户开始抱怨。更糟糕的是,你完全不知道问题出在哪里——是模型推理变慢了?还是某个上游服务挂了?或者是内存泄漏了?
这就是缺乏可观测性的典型困境。没有监控,你的服务就像在黑暗中飞行,出了问题只能靠猜。
今天,我就带你一步步为RexUniNLU搭建一套完整的可观测性体系。我们会用Prometheus来收集指标,用Grafana来可视化展示。学完这篇教程,你将能够:
- 实时监控NLU服务的健康状态
- 快速定位性能瓶颈
- 设置智能告警,在用户发现问题前就收到通知
- 通过数据驱动的方式优化服务性能
2. 环境准备与依赖安装
2.1 检查现有环境
在开始之前,我们先确认一下你的RexUniNLU服务已经正常运行。打开终端,切换到项目目录:
cd RexUniNLU python test.py如果测试脚本能正常运行,说明基础环境没问题。接下来,我们需要安装监控相关的依赖。
2.2 安装Prometheus客户端库
Prometheus通过HTTP端点来拉取指标数据。我们需要在RexUniNLU服务中暴露这些指标。首先安装Python的Prometheus客户端库:
pip install prometheus-client这个库非常轻量,不会对你的服务性能产生明显影响。它提供了各种类型的指标(计数器、仪表盘、直方图等),我们稍后会用到。
2.3 安装Grafana和Prometheus(Docker方式)
为了简化部署,我推荐使用Docker Compose来启动Grafana和Prometheus。如果你还没有安装Docker,请先到Docker官网下载安装。
在RexUniNLU项目根目录下,创建一个docker-compose.yml文件:
version: '3.8' services: prometheus: image: prom/prometheus:latest container_name: rexuninlu-prometheus 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=200h' - '--web.enable-lifecycle' restart: unless-stopped grafana: image: grafana/grafana:latest container_name: rexuninlu-grafana ports: - "3000:3000" volumes: - grafana_data:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning environment: - GF_SECURITY_ADMIN_PASSWORD=admin123 - GF_USERS_ALLOW_SIGN_UP=false restart: unless-stopped volumes: prometheus_data: grafana_data:2.4 配置Prometheus
创建prometheus.yml配置文件:
global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: 'rexuninlu' static_configs: - targets: ['host.docker.internal:8000'] # 如果是Mac/Windows,用这个 # - targets: ['172.17.0.1:8000'] # 如果是Linux,用这个 metrics_path: '/metrics' scrape_interval: 10s这里的关键是targets配置。如果你的RexUniNLU服务运行在本地机器的8000端口,Docker容器需要通过特殊的主机名来访问它。
3. 为RexUniNLU添加Prometheus指标埋点
现在我们来改造RexUniNLU服务,让它暴露监控指标。我们将主要关注几个核心指标:
- 请求总数:了解服务的使用频率
- 请求延迟:监控服务性能
- 错误率:及时发现服务异常
- 模型推理时间:定位性能瓶颈
- 内存使用情况:预防内存泄漏
3.1 创建监控模块
在RexUniNLU目录下,创建一个新的Python文件monitoring.py:
from prometheus_client import Counter, Histogram, Gauge, generate_latest, REGISTRY import time import psutil import os # 定义各种指标 # 请求相关指标 REQUEST_COUNT = Counter( 'rexuninlu_requests_total', 'Total number of requests', ['method', 'endpoint', 'status'] ) REQUEST_LATENCY = Histogram( 'rexuninlu_request_latency_seconds', 'Request latency in seconds', ['method', 'endpoint'], buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 5.0] ) # 模型推理相关指标 MODEL_INFERENCE_TIME = Histogram( 'rexuninlu_model_inference_seconds', 'Model inference time in seconds', ['model_name'], buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0] ) # 错误相关指标 ERROR_COUNT = Counter( 'rexuninlu_errors_total', 'Total number of errors', ['error_type', 'endpoint'] ) # 系统资源指标 MEMORY_USAGE = Gauge( 'rexuninlu_memory_usage_bytes', 'Memory usage in bytes' ) CPU_USAGE = Gauge( 'rexuninlu_cpu_usage_percent', 'CPU usage percentage' ) # 业务特定指标 INTENT_RECOGNITION_COUNT = Counter( 'rexuninlu_intent_recognition_total', 'Total number of intent recognition requests', ['intent_type'] ) SLOT_EXTRACTION_COUNT = Counter( 'rexuninlu_slot_extraction_total', 'Total number of slot extraction requests', ['slot_type'] ) def update_system_metrics(): """更新系统资源指标""" process = psutil.Process(os.getpid()) # 更新内存使用 memory_info = process.memory_info() MEMORY_USAGE.set(memory_info.rss) # 更新CPU使用率 cpu_percent = process.cpu_percent(interval=0.1) CPU_USAGE.set(cpu_percent) def record_request_metrics(method, endpoint, status, latency): """记录请求相关指标""" REQUEST_COUNT.labels(method=method, endpoint=endpoint, status=status).inc() REQUEST_LATENCY.labels(method=method, endpoint=endpoint).observe(latency) def record_model_inference(model_name, inference_time): """记录模型推理时间""" MODEL_INFERENCE_TIME.labels(model_name=model_name).observe(inference_time) def record_error(error_type, endpoint): """记录错误""" ERROR_COUNT.labels(error_type=error_type, endpoint=endpoint).inc() def record_intent_recognition(intent_type): """记录意图识别""" INTENT_RECOGNITION_COUNT.labels(intent_type=intent_type).inc() def record_slot_extraction(slot_type): """记录槽位提取""" SLOT_EXTRACTION_COUNT.labels(slot_type=slot_type).inc() def get_metrics(): """获取所有指标数据""" update_system_metrics() return generate_latest(REGISTRY)3.2 集成监控到FastAPI服务
现在我们来修改server.py,集成监控功能:
from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse, Response import uvicorn from typing import Dict, List, Any import time import logging # 导入我们的监控模块 from monitoring import ( record_request_metrics, record_model_inference, record_error, record_intent_recognition, record_slot_extraction, get_metrics, update_system_metrics ) # 设置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="RexUniNLU API", version="1.0.0") # 这里保留你原有的模型加载和初始化代码 # from modelscope import ... 等 @app.middleware("http") async def monitor_requests(request: Request, call_next): """监控中间件:记录所有请求的指标""" start_time = time.time() method = request.method endpoint = request.url.path try: response = await call_next(request) latency = time.time() - start_time # 记录请求指标 record_request_metrics( method=method, endpoint=endpoint, status=response.status_code, latency=latency ) # 记录慢请求 if latency > 1.0: # 超过1秒的请求 logger.warning(f"Slow request: {method} {endpoint} took {latency:.2f}s") return response except Exception as e: latency = time.time() - start_time error_type = type(e).__name__ # 记录错误指标 record_error(error_type=error_type, endpoint=endpoint) record_request_metrics( method=method, endpoint=endpoint, status=500, latency=latency ) logger.error(f"Request failed: {method} {endpoint} - {error_type}: {str(e)}") raise @app.post("/nlu") async def nlu_endpoint(request_data: Dict[str, Any]): """NLU主接口""" text = request_data.get("text", "") labels = request_data.get("labels", []) if not text or not labels: raise HTTPException(status_code=400, detail="text and labels are required") start_time = time.time() try: # 这里调用你原有的分析函数 # result = analyze_text(text, labels) # 为了演示,我们创建一个模拟结果 result = { "text": text, "intents": [], "slots": [] } inference_time = time.time() - start_time # 记录模型推理时间 record_model_inference(model_name="Siamese-UIE", inference_time=inference_time) # 记录业务指标 for intent in result.get("intents", []): record_intent_recognition(intent.get("type", "unknown")) for slot in result.get("slots", []): record_slot_extraction(slot.get("type", "unknown")) logger.info(f"NLU processed: text='{text[:50]}...', time={inference_time:.3f}s") return { "success": True, "data": result, "inference_time": inference_time } except Exception as e: error_type = type(e).__name__ record_error(error_type=error_type, endpoint="/nlu") logger.error(f"NLU processing failed: {str(e)}") raise HTTPException(status_code=500, detail=str(e)) @app.get("/metrics") async def metrics_endpoint(): """Prometheus指标端点""" metrics_data = get_metrics() return Response(content=metrics_data, media_type="text/plain") @app.get("/health") async def health_check(): """健康检查端点""" update_system_metrics() return { "status": "healthy", "timestamp": time.time(), "service": "RexUniNLU" } @app.get("/") async def root(): """根端点""" return { "message": "RexUniNLU API is running", "version": "1.0.0", "endpoints": { "nlu": "POST /nlu - 自然语言理解", "metrics": "GET /metrics - Prometheus指标", "health": "GET /health - 健康检查" } } if __name__ == "__main__": logger.info("Starting RexUniNLU server with monitoring...") uvicorn.run(app, host="0.0.0.0", port=8000)3.3 启动服务并验证指标
现在启动你的服务:
python server.py服务启动后,打开浏览器访问以下地址:
- 服务状态:
http://localhost:8000/ - 健康检查:
http://localhost:8000/health - Prometheus指标:
http://localhost:8000/metrics
你应该能在/metrics端点看到类似这样的输出:
# HELP rexuninlu_requests_total Total number of requests # TYPE rexuninlu_requests_total counter rexuninlu_requests_total{method="GET",endpoint="/",status="200"} 1 # HELP rexuninlu_memory_usage_bytes Memory usage in bytes # TYPE rexuninlu_memory_usage_bytes gauge rexuninlu_memory_usage_bytes 123456784. 配置Grafana监控面板
现在我们的服务已经暴露了指标,接下来用Grafana来可视化这些数据。
4.1 启动监控服务
在终端中,运行Docker Compose:
docker-compose up -d等待几分钟让服务启动,然后访问:
- Prometheus:
http://localhost:9090 - Grafana:
http://localhost:3000(用户名: admin, 密码: admin123)
4.2 配置Prometheus数据源
- 登录Grafana后,点击左侧的"Configuration"(齿轮图标)
- 选择"Data Sources"
- 点击"Add data source"
- 选择"Prometheus"
- 配置URL为:
http://prometheus:9090 - 点击"Save & Test",应该显示"Data source is working"
4.3 创建NLU监控仪表盘
现在我们来创建一个专门监控RexUniNLU的仪表盘。点击左侧的"+"图标,选择"Dashboard",然后点击"Add new panel"。
4.3.1 面板1:请求概览
这个面板显示服务的整体请求情况。
配置:
- Panel title: 请求概览
- Query A:
rate(rexuninlu_requests_total[5m]) - Legend:
{{method}} {{endpoint}} - Visualization: Time series
复制以下PromQL查询:
# 总请求率 sum(rate(rexuninlu_requests_total[5m])) by (endpoint) # 按状态码的请求分布 sum(rate(rexuninlu_requests_total{status=~"2.."}[5m])) by (endpoint) sum(rate(rexuninlu_requests_total{status=~"4.."}[5m])) by (endpoint) sum(rate(rexuninlu_requests_total{status=~"5.."}[5m])) by (endpoint)4.3.2 面板2:响应时间监控
这个面板监控服务的延迟情况。
配置:
- Panel title: 响应时间分布
- Query A:
histogram_quantile(0.95, sum(rate(rexuninlu_request_latency_seconds_bucket[5m])) by (le, endpoint)) - Legend:
P95 - {{endpoint}} - Visualization: Time series
- Unit: seconds
添加更多分位数:
# P50(中位数) histogram_quantile(0.50, sum(rate(rexuninlu_request_latency_seconds_bucket[5m])) by (le, endpoint)) # P90 histogram_quantile(0.90, sum(rate(rexuninlu_request_latency_seconds_bucket[5m])) by (le, endpoint)) # P99 histogram_quantile(0.99, sum(rate(rexuninlu_request_latency_seconds_bucket[5m])) by (le, endpoint))4.3.3 面板3:模型性能监控
这个面板专门监控模型推理性能。
配置:
- Panel title: 模型推理时间
- Query A:
histogram_quantile(0.95, rate(rexuninlu_model_inference_seconds_bucket[5m])) - Legend:
P95推理时间 - Visualization: Time series
- Unit: seconds
添加更多查询:
# 平均推理时间 rate(rexuninlu_model_inference_seconds_sum[5m]) / rate(rexuninlu_model_inference_seconds_count[5m]) # 推理请求率 rate(rexuninlu_model_inference_seconds_count[5m])4.3.4 面板4:错误监控
这个面板监控服务错误情况。
配置:
- Panel title: 错误率监控
- Query A:
rate(rexuninlu_errors_total[5m]) - Legend:
{{error_type}} @ {{endpoint}} - Visualization: Time series
计算错误率:
# 错误率(错误数/总请求数) sum(rate(rexuninlu_errors_total[5m])) / sum(rate(rexuninlu_requests_total[5m])) * 1004.3.5 面板5:系统资源监控
这个面板监控服务器的资源使用情况。
配置:
- Panel title: 系统资源使用
- Query A:
rexuninlu_memory_usage_bytes - Legend: 内存使用
- Visualization: Time series
- Unit: bytes
添加CPU监控:
# CPU使用率 rexuninlu_cpu_usage_percent # 内存使用率(假设服务器有8GB内存) rexuninlu_memory_usage_bytes / (8 * 1024 * 1024 * 1024) * 1004.3.6 面板6:业务指标监控
这个面板监控NLU特有的业务指标。
配置:
- Panel title: 业务指标
- Query A:
rate(rexuninlu_intent_recognition_total[5m]) - Legend:
意图识别 - {{intent_type}} - Visualization: Time series
添加槽位提取监控:
# 槽位提取率 rate(rexuninlu_slot_extraction_total[5m]) # 最常识别的意图 topk(5, sum(rate(rexuninlu_intent_recognition_total[1h])) by (intent_type)) # 最常提取的槽位 topk(5, sum(rate(rexuninlu_slot_extraction_total[1h])) by (slot_type))4.4 设置告警规则
监控不仅要看,还要能及时告警。在Grafana中设置几个关键告警:
4.4.1 高延迟告警
当P95响应时间超过阈值时告警:
- 在仪表盘编辑模式下,点击面板标题,选择"Edit"
- 切换到"Alert"标签
- 点击"Create alert rule from this panel"
- 配置:
- Rule name: 高延迟告警
- Condition:
WHEN last() OF query(A, 5m, now) IS ABOVE 1.0 - 这表示当P95响应时间超过1秒时触发告警
4.4.2 错误率告警
当错误率过高时告警:
# 告警条件:错误率超过5% (sum(rate(rexuninlu_errors_total[5m])) / sum(rate(rexuninlu_requests_total[5m]))) * 100 > 54.4.3 服务宕机告警
当服务完全不可用时告警:
# 如果5分钟内没有收到任何指标 up{job="rexuninlu"} == 05. 实战:监控数据分析与优化
有了监控数据,我们就能做很多有意义的事情。让我分享几个实际案例:
5.1 案例一:发现性能瓶颈
有一次,我发现/nlu接口的P99延迟突然从200ms飙升到了2s。通过查看监控面板,我发现了问题:
- 模型推理时间面板显示推理时间正常(~150ms)
- 系统资源面板显示内存使用率高达90%
- 错误面板显示有大量内存不足的错误
解决方案:增加服务内存限制,并添加内存使用告警。
5.2 案例二:优化业务逻辑
通过业务指标面板,我发现某个特定的意图识别特别频繁,但识别准确率不高。进一步分析发现:
- 这个意图的标签定义不够清晰
- 用户输入的方式多种多样
解决方案:优化标签定义,添加更多同义词,准确率从75%提升到了92%。
5.3 案例三:容量规划
通过长期监控数据,我可以预测未来的资源需求:
# 简单的容量预测脚本 import pandas as pd from datetime import datetime, timedelta def predict_capacity(historical_data, growth_rate=0.1): """ 基于历史数据预测未来容量需求 Args: historical_data: 历史监控数据 growth_rate: 预计月增长率 Returns: 未来3个月的资源需求预测 """ # 分析历史趋势 current_qps = historical_data['requests_per_second'].iloc[-1] current_latency = historical_data['p95_latency'].iloc[-1] # 预测未来 predictions = [] for month in range(1, 4): predicted_qps = current_qps * (1 + growth_rate) ** month # 根据QPS估算所需资源 # 这里只是一个简化模型,实际需要更复杂的计算 required_instances = max(1, predicted_qps / 100) # 假设每个实例能处理100 QPS required_memory = required_instances * 512 # MB predictions.append({ 'month': month, 'predicted_qps': round(predicted_qps, 2), 'required_instances': int(required_instances), 'required_memory_mb': int(required_memory) }) return predictions6. 总结
6.1 我们学到了什么
通过这篇教程,我们为RexUniNLU搭建了一套完整的可观测性体系:
- 指标埋点:在服务代码中添加了各种监控指标
- 数据收集:配置Prometheus定期拉取指标数据
- 可视化展示:在Grafana中创建了6个监控面板
- 告警设置:配置了关键指标的告警规则
6.2 监控带来的价值
有了这套监控系统,你现在可以:
- 实时了解服务状态:随时知道服务是否健康
- 快速定位问题:出现问题时有数据可查,不再靠猜
- 数据驱动优化:基于真实数据做性能优化
- 智能容量规划:预测未来的资源需求
- 提升用户体验:在用户发现问题前就解决
6.3 下一步建议
监控系统建设是一个持续的过程,我建议你:
- 定期审查指标:每个月花点时间看看监控数据,发现潜在问题
- 优化告警规则:根据实际情况调整告警阈值,避免告警疲劳
- 添加更多业务指标:随着业务发展,添加更多有业务价值的指标
- 建立监控文化:让团队每个人都关注监控数据
记住,好的监控系统不是一蹴而就的,而是随着业务发展不断完善的。从今天开始,让你的RexUniNLU服务在监控的"阳光"下运行,告别"盲飞"时代。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。