目 录CONTENT

文章目录

AI 平台的可观测性:指标、日志、链路三位一体

PySuper
2025-07-12 / 0 评论 / 0 点赞 / 4 阅读 / 0 字
温馨提示:
本文最后更新于2026-05-25,若内容或图片失效,请留言反馈。 所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

一、为什么 AI 平台需要专门的可观测性方案

传统 Web 服务的可观测性已经相对成熟:监控 QPS、Latency、Error Rate 三大指标,配合 ELK 日志和分布式链路追踪,一套经典方案可以覆盖 90% 的问题排查场景。

但 AI 平台不一样。AI 平台的"请求"是 Token 序列,"响应"也是 Token 序列,整个过程发生在 GPU 的黑箱里。传统的可观测性工具对大模型场景几乎是"睁眼瞎":

  1. 指标层缺失:GPU 利用率、显存占用、KV Cache 命中率、Token 吞吐……这些是 AI 平台独有的关键指标

  2. 日志层不匹配:LLM 的推理日志包含 Prompt、Completion、Token 统计,这些信息量巨大且格式特殊

  3. 链路层更复杂:一次用户请求可能触发 Prompt Enrichment → RAG Retrieval → Model Inference → Response Formatting 多个环节

本文将介绍如何构建一套面向 AI 平台的可观测性方案,覆盖指标(Metrics)日志(Logs)链路(Traces) 三大支柱。

二、AI 平台 vs 传统可观测性

┌─────────────────────────────────────────────────────────────────────────────────┐
│                    AI 平台可观测性 vs 传统可观测性                                 │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│  ┌─────────────────────────────────────────────────────────────────────────┐    │
│  │                        传统 Web 服务                                     │    │
│  │                                                                         │    │
│  │  请求 ──▶ 网关 ──▶ 服务 A ──▶ 服务 B ──▶ 数据库                         │    │
│  │           │                                              │              │    │
│  │           ▼                                              ▼              │    │
│  │  指标: QPS, Latency, Error Rate              指标: CPU, Memory, 连接数   │    │
│  │  日志: HTTP Access Log, Error Log            日志: SQL Log, Slow Query   │    │
│  │  链路: HTTP Header Trace ID                 链路: Dubbo/gRPC Trace    │    │
│  │                                                                         │    │
│  │  特点: 结构清晰,依赖明确,单次请求耗时可控                                │    │
│  └─────────────────────────────────────────────────────────────────────────┘    │
│                                    │                                            │
│                                    ▼                                            │
│  ┌─────────────────────────────────────────────────────────────────────────┐    │
│  │                        AI 平台                                           │    │
│  │                                                                         │    │
│  │     用户请求 ──▶ Prompt Enrich ──▶ RAG ──▶ LLM ──▶ 后处理 ──▶ 响应     │    │
│  │                        │          │       │         │                   │    │
│  │                        ▼          ▼       ▼         ▼                   │    │
│  │  指标: TTFT, TPOT,     指标:      指标:   GPU利用率  指标:               │    │
│  │        Token吞吐       召回率     命中率   KV Cache  格式正确率          │    │
│  │                        │          │       │                             │    │
│  │                        ▼          ▼       ▼                             │    │
│  │  日志: Prompt文       日志:       日志:   日志:  模型权重                  │    │
│  │        Completion      检索结果   向量距离  梯度日志                     │    │
│  │        Token统计                                                      │    │
│  │                        │          │       │                             │    │
│  │                        ▼          ▼       ▼                             │    │
│  │  链路: 用户请求 ──▶ 检索请求 ──▶ 模型调用 ──▶ 采样请求                  │    │
│  │        (span 跨多服务,包含 tokenize/detokenize/forward 等子操作)        │    │
│  │                                                                         │    │
│  │  特点: GPU 是黑箱,Token 粒度追踪,KV Cache 动态变化,批量调度复杂        │    │
│  └─────────────────────────────────────────────────────────────────────────┘    │
│                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────┘

三、三大支柱在 AI 场景的具体化

3.1 指标(Metrics)

AI 平台的核心指标与传统服务有显著不同:

指标类型

指标名称

说明

采集方式

推理延迟

TTFT (Time To First Token)

首 Token 延迟

vLLM/TGI 内置

推理延迟

TPOT (Time Per Output Token)

每 Token 生成时间

vLLM/TGI 内置

推理延迟

E2E Latency

端到端响应时间

API 网关

吞吐量

Token Throughput

Token 吞吐率

vLLM/TGI 内置

吞吐率

Request Throughput

请求并发处理能力

API 网关

显存

GPU Memory Usage

GPU 显存占用

DCGM Exporter

显存

KV Cache Utilization

KV Cache 利用率

vLLM API

GPU

GPU Utilization

GPU 计算利用率

DCGM Exporter

GPU

GPU Temperature

GPU 温度

DCGM Exporter

模型

Prefix Cache Hit Rate

前缀缓存命中率

vLLM API

3.2 日志(Logs)

AI 平台的日志有其独特结构:

{
  "timestamp": "2025-12-01T10:30:00.000Z",
  "log_type": "llm_inference",
  "request_id": "req-uuid-12345",
  "trace_id": "trace-uuid-67890",
  
  "request": {
    "model": "Qwen2.5-72B-Instruct",
    "prompt": "请介绍一下北京的历史...",
    "prompt_tokens": 28,
    "max_tokens": 1024,
    "temperature": 0.7,
    "top_p": 0.9
  },
  
  "response": {
    "completion": "北京是中华人民共和国的首都...",
    "completion_tokens": 156,
    "finish_reason": "stop",
    "generated_tokens": [234, 567, 890, ...]
  },
  
  "metrics": {
    "ttft_ms": 45.2,
    "tpot_ms": 12.3,
    "e2e_latency_ms": 2018.6,
    "prompt_tokens": 28,
    "completion_tokens": 156,
    "total_tokens": 184
  },
  
  "system": {
    "gpu_ids": [0, 1, 2, 3],
    "gpu_memory_used_gb": 280.5,
    "batch_size": 32,
    "kv_cache_hit": false
  }
}

3.3 链路(Traces)

AI 平台的分布式链路需要追踪从用户请求到模型推理的完整路径:

┌─────────────────────────────────────────────────────────────────────────────────┐
│                         LLM 请求完整链路                                         │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│  trace_id: abc123                                                               │
│                                                                                  │
│  ├─ span: api_gateway                                                          │
│  │    └─ 用户请求进来,记录 metadata                                            │
│  │         │                                                                    │
│  │         ▼                                                                    │
│  │    ├─ span: prompt_enrichment                                               │
│  │    │    └─ 添加 system prompt、历史对话、few-shot examples                   │
│  │    │         │                                                               │
│  │    │         ▼                                                               │
│  │    │    ├─ span: template_rendering                                        │
│  │    │    │    └─ 模板变量替换                                                 │
│  │    │    │                                                                    │
│  │    │    └─ span: conversation_history                                        │
│  │    │         └─ 从存储加载对话历史                                            │
│  │    │                                                                           │
│  │    └─ span: rag_retrieval                                                    │
│  │         │                                                                    │
│  │         ├─ span: embedding                                                   │
│  │         │    └─ 将 prompt 向量化                                              │
│  │         │                                                                    │
│  │         ├─ span: vector_search                                              │
│  │         │    └─ 向量数据库检索                                               │
│  │         │                                                                    │
│  │         └─ span: rerank                                                      │
│  │              └─ 重排序                                                       │
│  │                                                                            │
│  │    └─ span: model_inference                                                 │
│  │         │                                                                    │
│  │         ├─ span: tokenize                                                   │
│  │         │    └─ 分词                                                        │
│  │         │                                                                    │
│  │         ├─ span: prefill                                                    │
│  │         │    └─ Prefill 阶段(计算密集)                                     │
│  │         │                                                                    │
│  │         ├─ span: decode (×N tokens)                                         │
│  │         │    └─ Decode 阶段(显存带宽密集)                                   │
│  │         │                                                                    │
│  │         └─ span: detokenize                                                 │
│  │              └─ 反分词                                                      │
│  │                                                                            │
│  └─ span: response_postprocess                                                 │
│       └─ 后处理:安全检查、格式转换、敏感词过滤                                  │
│                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────┘

四、监控架构图

┌─────────────────────────────────────────────────────────────────────────────────┐
│                        AI 平台可观测性架构图                                      │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│    ┌─────────────────────────────────────────────────────────────────────────┐ │
│    │                           数据采集层                                     │ │
│    │                                                                         │ │
│    │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │ │
│    │  │ vLLM/TGI     │  │ DCGM Exporter│  │ FastAPI/uvicorn│                │ │
│    │  │ 内置指标      │  │ GPU 指标     │  │ 应用日志      │                  │ │
│    │  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘                  │ │
│    │         │                  │                  │                        │ │
│    │         └──────────────────┴──────────────────┘                        │ │
│    │                            │                                             │ │
│    └────────────────────────────┼─────────────────────────────────────────────┘ │
│                                 ▼                                                │
│    ┌─────────────────────────────────────────────────────────────────────────┐ │
│    │                          指标存储层                                      │ │
│    │                                                                         │ │
│    │  ┌─────────────────────────────────────────────────────────────────┐   │ │
│    │  │                    Prometheus / Mimir                            │   │ │
│    │  │   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │   │ │
│    │  │   │ GPU指标  │  │ 推理指标 │  │ 业务指标 │  │ 系统指标 │       │   │ │
│    │  │   └──────────┘  └──────────┘  └──────────┘  └──────────┘       │   │ │
│    │  └─────────────────────────────────────────────────────────────────┘   │ │
│    │                                                                         │ │
│    └────────────────────────────────────┬────────────────────────────────────┘ │
│                                         │                                        │
│    ┌────────────────────────────────────┴────────────────────────────────────┐ │
│    │                          日志存储层                                      │ │
│    │                                                                         │ │
│    │  ┌─────────────────────────────────────────────────────────────────┐   │ │
│    │  │                      Loki / Elasticsearch                         │   │ │
│    │  │   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │   │ │
│    │  │   │ 推理日志 │  │ 训练日志 │  │ 访问日志 │  │ 错误日志 │       │   │ │
│    │  │   └──────────┘  └──────────┘  └──────────┘  └──────────┘       │   │ │
│    │  └─────────────────────────────────────────────────────────────────┘   │ │
│    │                                                                         │ │
│    └────────────────────────────────────┬────────────────────────────────────┘ │
│                                         │                                        │
│    ┌────────────────────────────────────┴────────────────────────────────────┐ │
│    │                           链路存储层                                      │ │
│    │                                                                         │ │
│    │  ┌─────────────────────────────────────────────────────────────────┐   │ │
│    │  │                      Jaeger / Tempo                               │   │ │
│    │  │   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │   │ │
│    │  │   │ API层Span│  │ RAG链路 │  │ 模型Span │  │ 后处理Span│       │   │ │
│    │  │   └──────────┘  └──────────┘  └──────────┘  └──────────┘       │   │ │
│    │  └─────────────────────────────────────────────────────────────────┘   │ │
│    │                                                                         │ │
│    └────────────────────────────────────┬────────────────────────────────────┘ │
│                                         │                                        │
│    ┌────────────────────────────────────┴────────────────────────────────────┐ │
│    │                           可视化层                                        │ │
│    │                                                                         │ │
│    │  ┌─────────────────────────────────────────────────────────────────┐   │ │
│    │  │                    Grafana 仪表盘                                 │   │ │
│    │  │   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │   │ │
│    │  │   │ GPU监控  │  │ 推理监控 │  │ 日志查询 │  │ 链路追踪 │       │   │ │
│    │  │   └──────────┘  └──────────┘  └──────────┘  └──────────┘       │   │ │
│    │  └─────────────────────────────────────────────────────────────────┘   │ │
│    │                                                                         │ │
│    └─────────────────────────────────────────────────────────────────────────┘ │
│                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────┘

五、GPU 指标采集

5.1 DCGM Exporter 部署

DCGM (Data Center GPU Manager) 是 NVIDIA 提供的 GPU 监控工具,DCGM Exporter 可以将 GPU 指标暴露给 Prometheus:

# dcgm-exporter.yaml
# DCGM Exporter Kubernetes 部署配置

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: dcgm-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: dcgm-exporter
  template:
    metadata:
      labels:
        app: dcgm-exporter
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9400"
        prometheus.io/path: "/metrics"
    spec:
      containers:
        - name: exporter
          image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.7-3.1.5-ubuntu22.04
          securityContext:
            privileged: true
          env:
            - name: DCGM_EXPORTER_INTERVAL
              value: "15"  # 采集间隔(秒)
            - name: DCGM_EXPORTER_COLLECTORS
              value: "/etc/dcgm-exporter/dcgm-fields.csv"
          ports:
            - name: metrics
              containerPort: 9400
              protocol: TCP
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          volumeMounts:
            - name: dcgm-socket
              mountPath: /var/lib/dcgm
            - name: dcgm-config
              mountPath: /etc/dcgm-exporter
      volumes:
        - name: dcgm-socket
          hostPath:
            path: /var/lib/dcgm
        - name: dcgm-config
          configMap:
            name: dcgm-exporter-config
      tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"

---
# DCGM Exporter Service
apiVersion: v1
kind: Service
metadata:
  name: dcgm-exporter
  namespace: monitoring
spec:
  type: ClusterIP
  selector:
    app: dcgm-exporter
  ports:
    - name: metrics
      port: 9400
      targetPort: metrics

5.2 Prometheus 配置

# prometheus-config.yaml
# Prometheus 抓取配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      evaluation_interval: 15s

    scrape_configs:
      # DCGM GPU 指标
      - job_name: 'dcgm-exporter'
        kubernetes_sd_configs:
          - role: endpoints
            namespaces:
              names:
                - monitoring
        relabel_configs:
          - source_labels: [__meta_kubernetes_endpoint_port_name]
            action: keep
            regex: metrics
          - source_labels: [__meta_kubernetes_pod_name]
            target_label: pod

      # vLLM 推理指标
      - job_name: 'vllm'
        static_configs:
          - targets: ['vllm-service:8000']
        metrics_path: '/metrics'
        relabel_configs:
          - source_labels: [__address__]
            regex: '([^:]+):(\d+)'
            target_label: __address__
            replacement: '${1}:8000'

      # API Server 指标
      - job_name: 'api-server'
        static_configs:
          - targets: ['api-server:8000']
        metrics_path: '/metrics'

      # 自定义 LLM 指标
      - job_name: 'llm-custom-metrics'
        static_configs:
          - targets: ['llm-metrics-sidecar:9090']

六、自定义 LLM 指标采集

6.1 Prometheus 指标 SDK 封装

#!/usr/bin/env python3
"""
自定义 LLM 指标采集模块
为 LLM 推理服务添加 Prometheus 指标暴露
"""

from prometheus_client import Counter, Histogram, Gauge, Info, CollectorRegistry, REGISTRY
from prometheus_client.multiprocess import MultiProcessCollector
from prometheus_client.exposition import generate_latest, CONTENT_TYPE_LATEST
from typing import Optional, Dict, Any, List
import time
import logging
from functools import wraps
from contextlib import contextmanager

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class LLMMetrics:
    """LLM 推理指标收集器"""
    
    def __init__(self, namespace: str = "llm", service_name: str = "inference"):
        """
        初始化指标收集器
        
        Args:
            namespace: 指标命名空间
            service_name: 服务名称
        """
        self.namespace = namespace
        self.service_name = service_name
        
        # 1. 请求相关指标
        self.request_total = Counter(
            f"{namespace}_requests_total",
            "LLM 请求总数",
            ["model", "status", "finish_reason"]
        )
        
        self.request_in_progress = Gauge(
            f"{namespace}_requests_in_progress",
            "正在处理的请求数",
            ["model"]
        )
        
        # 2. Token 相关指标
        self.prompt_tokens_total = Counter(
            f"{namespace}_prompt_tokens_total",
            "Prompt Token 总数",
            ["model"]
        )
        
        self.completion_tokens_total = Counter(
            f"{namespace}_completion_tokens_total",
            "Completion Token 总数",
            ["model"]
        )
        
        self.tokens_per_request = Histogram(
            f"{namespace}_tokens_per_request",
            "每个请求的 Token 数量分布",
            ["model", "type"],  # type: prompt/completion/total
            buckets=[16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]
        )
        
        # 3. 延迟相关指标
        self.latency_seconds = Histogram(
            f"{namespace}_latency_seconds",
            "请求延迟分布",
            ["model", "stage"],  # stage: total/ttft/tpot/prefill/decode
            buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0]
        )
        
        # 4. 吞吐量指标
        self.throughput_tokens = Gauge(
            f"{namespace}_throughput_tokens_per_second",
            "Token 吞吐率(tokens/s)",
            ["model"]
        )
        
        self.throughput_requests = Gauge(
            f"{namespace}_throughput_requests_per_second",
            "请求吞吐率(requests/s)",
            ["model"]
        )
        
        # 5. 错误相关指标
        self.error_total = Counter(
            f"{namespace}_errors_total",
            "错误总数",
            ["model", "error_type"]  # error_type: timeout/validation/oom/timeout
        )
        
        # 6. GPU 相关指标
        self.gpu_memory_used = Gauge(
            f"{namespace}_gpu_memory_used_bytes",
            "GPU 显存使用量(字节)",
            ["model", "gpu_id"]
        )
        
        self.gpu_utilization = Gauge(
            f"{namespace}_gpu_utilization_ratio",
            "GPU 利用率",
            ["model", "gpu_id"]
        )
        
        # 7. KV Cache 指标(vLLM 特有)
        self.kv_cache_usage = Gauge(
            f"{namespace}_kv_cache_usage_ratio",
            "KV Cache 利用率",
            ["model"]
        )
        
        self.kv_cache_hits = Counter(
            f"{namespace}_kv_cache_hits_total",
            "KV Cache 命中次数",
            ["model"]
        )
        
        # 8. 模型信息
        self.model_info = Info(
            f"{namespace}_model_info",
            "模型信息"
        )
        
        # 内部状态
        self._total_tokens_last_check = 0
        self._last_check_time = time.time()
    
    def record_request_start(
        self,
        model: str,
        request_id: str,
        prompt_tokens: int
    ):
        """
        记录请求开始
        
        Args:
            model: 模型名称
            request_id: 请求 ID
            prompt_tokens: Prompt 的 Token 数
        """
        self.request_in_progress.labels(model=model).inc()
        self.prompt_tokens_total.labels(model=model).inc(prompt_tokens)
        self.tokens_per_request.labels(model=model, type="prompt").observe(prompt_tokens)
    
    def record_request_end(
        self,
        model: str,
        request_id: str,
        status: str,
        finish_reason: str,
        prompt_tokens: int,
        completion_tokens: int,
        latency_seconds: float,
        ttft_seconds: Optional[float] = None,
        tpot_seconds: Optional[float] = None
    ):
        """
        记录请求结束
        
        Args:
            model: 模型名称
            request_id: 请求 ID
            status: 请求状态 (success/error)
            finish_reason: 结束原因 (stop/length/error)
            prompt_tokens: Prompt Token 数
            completion_tokens: Completion Token 数
            latency_seconds: 总延迟(秒)
            ttft_seconds: 首 Token 延迟(秒)
            tpot_seconds: 每 Token 延迟(秒)
        """
        # 更新请求状态
        self.request_in_progress.labels(model=model).dec()
        self.request_total.labels(
            model=model,
            status=status,
            finish_reason=finish_reason
        ).inc()
        
        # 记录 Token 统计
        self.completion_tokens_total.labels(model=model).inc(completion_tokens)
        self.tokens_per_request.labels(model=model, type="completion").observe(completion_tokens)
        self.tokens_per_request.labels(model=model, type="total").observe(
            prompt_tokens + completion_tokens
        )
        
        # 记录延迟
        self.latency_seconds.labels(model=model, stage="total").observe(latency_seconds)
        
        if ttft_seconds is not None:
            self.latency_seconds.labels(model=model, stage="ttft").observe(ttft_seconds)
        
        if tpot_seconds is not None:
            self.latency_seconds.labels(model=model, stage="tpot").observe(tpot_seconds)
    
    def record_error(
        self,
        model: str,
        error_type: str,
        error_message: Optional[str] = None
    ):
        """
        记录错误
        
        Args:
            model: 模型名称
            error_type: 错误类型
            error_message: 错误信息(用于调试,不用于指标)
        """
        self.error_total.labels(model=model, error_type=error_type).inc()
        logger.warning(f"LLM 错误: model={model}, type={error_type}, msg={error_message}")
    
    def update_gpu_metrics(
        self,
        model: str,
        gpu_metrics: Dict[str, Dict[str, float]]
    ):
        """
        更新 GPU 指标
        
        Args:
            model: 模型名称
            gpu_metrics: GPU 指标字典 {gpu_id: {"memory_used": bytes, "utilization": ratio}}
        """
        for gpu_id, metrics in gpu_metrics.items():
            if "memory_used" in metrics:
                self.gpu_memory_used.labels(model=model, gpu_id=gpu_id).set(
                    metrics["memory_used"]
                )
            if "utilization" in metrics:
                self.gpu_utilization.labels(model=model, gpu_id=gpu_id).set(
                    metrics["utilization"]
                )
    
    def update_kv_cache_metrics(
        self,
        model: str,
        usage_ratio: float,
        hit: bool = False
    ):
        """
        更新 KV Cache 指标
        
        Args:
            model: 模型名称
            usage_ratio: Cache 使用率
            hit: 是否命中
        """
        self.kv_cache_usage.labels(model=model).set(usage_ratio)
        if hit:
            self.kv_cache_hits.labels(model=model).inc()
    
    def update_throughput(self, model: str, tokens_per_second: float, requests_per_second: float):
        """
        更新吞吐量指标
        
        Args:
            model: 模型名称
            tokens_per_second: Token 吞吐率
            requests_per_second: 请求吞吐率
        """
        self.throughput_tokens.labels(model=model).set(tokens_per_second)
        self.throughput_requests.labels(model=model).set(requests_per_second)
    
    def set_model_info(self, model: str, info: Dict[str, str]):
        """
        设置模型信息
        
        Args:
            model: 模型名称
            info: 模型信息字典
        """
        self.model_info.info({**info, "model": model})
    
    @contextmanager
    def track_latency(self, model: str, stage: str):
        """
        上下文管理器:追踪延迟
        
        Usage:
            with metrics.track_latency("qwen", "prefill"):
                # 执行 prefill 操作
                prefill()
        """
        start_time = time.time()
        try:
            yield
        finally:
            latency = time.time() - start_time
            self.latency_seconds.labels(model=model, stage=stage).observe(latency)


# ============ FastAPI 集成示例 ============

from fastapi import FastAPI, Request, Response
from fastapi.responses import PlainTextResponse
import uuid
import time

# 创建指标收集器
metrics = LLMMetrics(namespace="llm", service_name="inference")

# 创建 FastAPI 应用
app = FastAPI(title="LLM Inference API with Metrics")


@app.middleware("http")
async def track_request_metrics(request: Request, call_next):
    """请求指标追踪中间件"""
    request_id = str(uuid.uuid4())
    request.state.request_id = request_id
    
    # 获取模型名称(从路径或参数)
    model = request.query_params.get("model", "default")
    
    # 记录请求开始
    # 注意:这里只是简化示例,实际需要解析 request body 获取 prompt_tokens
    prompt_tokens = 0  # 需要从请求中提取
    
    metrics.record_request_start(model, request_id, prompt_tokens)
    
    start_time = time.time()
    
    try:
        response = await call_next(request)
        status = "success"
        finish_reason = "stop"
    except Exception as e:
        status = "error"
        finish_reason = "error"
        metrics.record_error(model, type(e).__name__, str(e))
        raise
    finally:
        latency = time.time() - start_time
        
        # 模拟记录(实际需要从响应中获取)
        metrics.record_request_end(
            model=model,
            request_id=request_id,
            status=status,
            finish_reason=finish_reason,
            prompt_tokens=prompt_tokens,
            completion_tokens=0,  # 从响应获取
            latency_seconds=latency
        )
    
    return response


@app.get("/metrics")
async def get_metrics():
    """Prometheus 抓取端点"""
    return Response(
        content=generate_latest(REGISTRY),
        media_type=CONTENT_TYPE_LATEST
    )


@app.get("/health")
async def health():
    """健康检查端点"""
    return {"status": "healthy"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

6.2 自定义指标采集服务

#!/usr/bin/env python3
"""
LLM 自定义指标采集服务
从 vLLM API 获取模型特定指标并暴露给 Prometheus
"""

import requests
import time
import logging
from typing import Dict, List, Optional
from prometheus_client import start_http_server, Gauge, Counter
from threading import Thread
import argparse

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class LLMCustomMetricsCollector:
    """LLM 自定义指标采集器"""
    
    def __init__(
        self,
        vllm_url: str,
        collect_interval: int = 10,
        port: int = 9090
    ):
        self.vllm_url = vllm_url.rstrip("/")
        self.collect_interval = collect_interval
        self.port = port
        
        # 初始化 Prometheus 指标
        self._init_metrics()
        
        # 运行状态
        self.running = False
    
    def _init_metrics(self):
        """初始化 Prometheus 指标"""
        # KV Cache 利用率
        self.kv_cache_usage = Gauge(
            "vllm_kv_cache_usage_ratio",
            "KV Cache 利用率",
            ["model"]
        )
        
        # 前缀缓存命中率
        self.prefix_cache_hit_rate = Gauge(
            "vllm_prefix_cache_hit_rate",
            "前缀缓存命中率",
            ["model"]
        )
        
        # 正在处理的请求数
        self.num_requests_running = Gauge(
            "vllm_num_requests_running",
            "正在处理的请求数",
            ["model"]
        )
        
        # 等待中的请求数
        self.num_requests_waiting = Gauge(
            "vllm_num_requests_waiting",
            "等待中的请求数",
            ["model"]
        )
        
        # GPU 显存使用
        self.gpu_memory_used = Gauge(
            "vllm_gpu_memory_used_bytes",
            "GPU 显存使用量(字节)",
            ["model", "gpu_id"]
        )
        
        # GPU 预估显存(模型加载所需)
        self.gpu_memory_reserved = Gauge(
            "vllm_gpu_memory_reserved_bytes",
            "GPU 显存预留量(字节)",
            ["model", "gpu_id"]
        )
        
        # 分块 Prefill 指标
        self.chunked_prefill_enabled = Gauge(
            "vllm_chunked_prefill_enabled",
            "是否启用分块 Prefill",
            ["model"]
        )
        
        # 自定义计数器
        self.custom_counter = Counter(
            "vllm_custom_requests_total",
            "自定义请求计数",
            ["model", "type"]
        )
    
    def collect(self):
        """从 vLLM API 采集指标"""
        try:
            # 获取模型列表
            models_url = f"{self.vllm_url}/v1/models"
            models_response = requests.get(models_url, timeout=5)
            models_response.raise_for_status()
            models = models_response.json().get("data", [])
            
            for model_info in models:
                model_name = model_info.get("id", "unknown")
                
                # 获取详细统计
                stats_url = f"{self.vllm_url}/stats"
                stats_response = requests.get(
                    stats_url,
                    params={"model": model_name},
                    timeout=5
                )
                
                if stats_response.status_code == 200:
                    stats = stats_response.json()
                    self._update_metrics(model_name, stats)
                else:
                    logger.warning(
                        f"获取模型 {model_name} 统计失败: {stats_response.status_code}"
                    )
        
        except requests.RequestException as e:
            logger.error(f"采集指标失败: {e}")
    
    def _update_metrics(self, model: str, stats: Dict):
        """更新 Prometheus 指标"""
        # KV Cache 使用率
        kv_cache_usage = stats.get("kv_cache_usage_ratio", 0)
        self.kv_cache_usage.labels(model=model).set(kv_cache_usage)
        
        # 前缀缓存命中率
        prefix_hit_rate = stats.get("prefix_cache_hit_rate", 0)
        self.prefix_cache_hit_rate.labels(model=model).set(prefix_hit_rate)
        
        # 请求数
        num_running = stats.get("num_running", 0)
        num_waiting = stats.get("num_waiting", 0)
        self.num_requests_running.labels(model=model).set(num_running)
        self.num_requests_waiting.labels(model=model).set(num_waiting)
        
        # GPU 显存
        gpu_memory = stats.get("gpu_memory", {})
        for gpu_id, memory_info in gpu_memory.items():
            if isinstance(memory_info, dict):
                self.gpu_memory_used.labels(
                    model=model,
                    gpu_id=gpu_id
                ).set(memory_info.get("used", 0))
                self.gpu_memory_reserved.labels(
                    model=model,
                    gpu_id=gpu_id
                ).set(memory_info.get("reserved", 0))
        
        # 分块 Prefill
        chunked = 1 if stats.get("chunked_prefill", False) else 0
        self.chunked_prefill_enabled.labels(model=model).set(chunked)
        
        logger.debug(f"更新指标完成: model={model}")
    
    def start(self):
        """启动采集服务"""
        # 启动 Prometheus HTTP 服务器
        start_http_server(self.port)
        logger.info(f"Prometheus 指标端点已启动: :{self.port}/metrics")
        
        self.running = True
        
        # 定期采集
        while self.running:
            self.collect()
            time.sleep(self.collect_interval)
    
    def stop(self):
        """停止采集服务"""
        self.running = False
        logger.info("指标采集服务已停止")


def main():
    parser = argparse.ArgumentParser(description="LLM 自定义指标采集服务")
    parser.add_argument(
        "--vllm-url",
        type=str,
        default="http://localhost:8000",
        help="vLLM 服务地址"
    )
    parser.add_argument(
        "--port",
        type=int,
        default=9090,
        help="Prometheus 指标端点端口"
    )
    parser.add_argument(
        "--interval",
        type=int,
        default=10,
        help="采集间隔(秒)"
    )
    
    args = parser.parse_args()
    
    collector = LLMCustomMetricsCollector(
        vllm_url=args.vllm_url,
        collect_interval=args.interval,
        port=args.port
    )
    
    try:
        collector.start()
    except KeyboardInterrupt:
        collector.stop()


if __name__ == "__main__":
    main()

七、Grafana Dashboard 配置

7.1 LLM 推理监控 Dashboard

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "id": null,
  "links": [],
  "liveNow": false,
  "panels": [
    {
      "title": "请求延迟 (TTFT)",
      "type": "timeseries",
      "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
      "targets": [
        {
          "expr": "histogram_quantile(0.50, sum(rate(llm_latency_seconds_bucket{stage=\"ttft\"}[5m])) by (le, model))",
          "legendFormat": "p50 - {{model}}",
          "refId": "A"
        },
        {
          "expr": "histogram_quantile(0.95, sum(rate(llm_latency_seconds_bucket{stage=\"ttft\"}[5m])) by (le, model))",
          "legendFormat": "p95 - {{model}}",
          "refId": "B"
        },
        {
          "expr": "histogram_quantile(0.99, sum(rate(llm_latency_seconds_bucket{stage=\"ttft\"}[5m])) by (le, model))",
          "legendFormat": "p99 - {{model}}",
          "refId": "C"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "s",
          "custom": {
            "drawStyle": "Line",
            "lineInterpolation": "Smooth",
            "showPoints": "Never"
          }
        }
      }
    },
    {
      "title": "Token 吞吐率",
      "type": "timeseries",
      "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
      "targets": [
        {
          "expr": "llm_throughput_tokens_per_second",
          "legendFormat": "{{model}}",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "tokens/s",
          "custom": {
            "drawStyle": "Area",
            "lineInterpolation": "Smooth"
          }
        }
      }
    },
    {
      "title": "GPU 显存使用",
      "type": "gauge",
      "gridPos": {"h": 8, "w": 8, "x": 0, "y": 8},
      "targets": [
        {
          "expr": "vllm_gpu_memory_used_bytes / vllm_gpu_memory_reserved_bytes",
          "legendFormat": "{{model}}-GPU{{gpu_id}}",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "max": 1,
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {"value": 0, "color": "green"},
              {"value": 0.7, "color": "yellow"},
              {"value": 0.85, "color": "red"}
            ]
          }
        }
      }
    },
    {
      "title": "KV Cache 利用率",
      "type": "timeseries",
      "gridPos": {"h": 8, "w": 8, "x": 8, "y": 8},
      "targets": [
        {
          "expr": "vllm_kv_cache_usage_ratio",
          "legendFormat": "{{model}}",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "max": 1,
          "custom": {
            "drawStyle": "Line",
            "lineInterpolation": "Smooth"
          }
        }
      }
    },
    {
      "title": "请求队列深度",
      "type": "timeseries",
      "gridPos": {"h": 8, "w": 8, "x": 16, "y": 8},
      "targets": [
        {
          "expr": "vllm_num_requests_running",
          "legendFormat": "Running - {{model}}",
          "refId": "A"
        },
        {
          "expr": "vllm_num_requests_waiting",
          "legendFormat": "Waiting - {{model}}",
          "refId": "B"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "custom": {
            "drawStyle": "Line",
            "lineInterpolation": "Smooth"
          }
        }
      }
    },
    {
      "title": "错误率",
      "type": "timeseries",
      "gridPos": {"h": 8, "w": 12, "x": 0, "y": 16},
      "targets": [
        {
          "expr": "sum(rate(llm_errors_total[5m])) by (model, error_type) / sum(rate(llm_requests_total[5m])) by (model)",
          "legendFormat": "{{model}} - {{error_type}}",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "custom": {
            "drawStyle": "Line"
          }
        }
      }
    },
    {
      "title": "Token 分布",
      "type": "histogram",
      "gridPos": {"h": 8, "w": 12, "x": 12, "y": 16},
      "targets": [
        {
          "expr": "sum(rate(llm_tokens_per_request_bucket[5m])) by (le, model)",
          "legendFormat": "{{model}}",
          "refId": "A"
        }
      ]
    }
  ],
  "refresh": "10s",
  "schemaVersion": 38,
  "tags": ["llm", "inference", "gpu"],
  "templating": {
    "list": []
  },
  "time": {
    "from": "now-1h",
    "to": "now"
  },
  "timepicker": {},
  "timezone": "browser",
  "title": "LLM 推理监控",
  "uid": "llm-inference-dashboard",
  "version": 1,
  "weekStart": ""
}

7.2 GPU 监控 Dashboard

{
  "dashboard": {
    "id": null,
    "uid": "gpu-monitoring",
    "title": "GPU 集群监控",
    "tags": ["gpu", "dcgm", "infrastructure"],
    "timezone": "browser",
    "panels": [
      {
        "title": "GPU 利用率",
        "type": "stat",
        "gridPos": {"h": 6, "w": 6, "x": 0, "y": 0},
        "targets": [
          {
            "expr": "avg(DCGM_FI_DEV_GPU_UTIL)",
            "legendFormat": "平均利用率",
            "refId": "A"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "percent",
            "thresholds": {
              "mode": "absolute",
              "steps": [
                {"value": 0, "color": "red"},
                {"value": 50, "color": "yellow"},
                {"value": 80, "color": "green"}
              ]
            }
          }
        }
      },
      {
        "title": "GPU 显存使用",
        "type": "stat",
        "gridPos": {"h": 6, "w": 6, "x": 6, "y": 0},
        "targets": [
          {
            "expr": "avg(DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_FREE)",
            "legendFormat": "显存使用率",
            "refId": "A"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "percent",
            "thresholds": {
              "mode": "absolute",
              "steps": [
                {"value": 0, "color": "green"},
                {"value": 70, "color": "yellow"},
                {"value": 90, "color": "red"}
              ]
            }
          }
        }
      },
      {
        "title": "GPU 温度",
        "type": "gauge",
        "gridPos": {"h": 6, "w": 6, "x": 12, "y": 0},
        "targets": [
          {
            "expr": "avg(DCGM_FI_DEV_GPU_TEMP)",
            "legendFormat": "",
            "refId": "A"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "celsius",
            "max": 100,
            "thresholds": {
              "mode": "absolute",
              "steps": [
                {"value": 0, "color": "blue"},
                {"value": 70, "color": "green"},
                {"value": 85, "color": "yellow"},
                {"value": 95, "color": "red"}
              ]
            }
          }
        }
      },
      {
        "title": "功耗",
        "type": "stat",
        "gridPos": {"h": 6, "w": 6, "x": 18, "y": 0},
        "targets": [
          {
            "expr": "sum(DCGM_FI_DEV_POWER_USAGE)",
            "legendFormat": "",
            "refId": "A"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "watt"
          }
        }
      },
      {
        "title": "GPU 利用率详情",
        "type": "timeseries",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 6},
        "targets": [
          {
            "expr": "DCGM_FI_DEV_GPU_UTIL",
            "legendFormat": "GPU {{gpu}}",
            "refId": "A"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "percent",
            "custom": {
              "drawStyle": "Line",
              "lineInterpolation": "Smooth"
            }
          }
        }
      },
      {
        "title": "显存使用详情",
        "type": "timeseries",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 6},
        "targets": [
          {
            "expr": "DCGM_FI_DEV_FB_USED",
            "legendFormat": "已用 - GPU {{gpu}}",
            "refId": "A"
          },
          {
            "expr": "DCGM_FI_DEV_FB_FREE",
            "legendFormat": "剩余 - GPU {{gpu}}",
            "refId": "B"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "bytes",
            "custom": {
              "drawStyle": "Area",
              "stacking": {"mode": "normal"}
            }
          }
        }
      }
    ],
    "refresh": "5s",
    "schemaVersion": 38,
    "version": 1
  }
}

八、告警规则配置

8.1 Prometheus 告警规则

# alermanager-rules.yaml
# Prometheus 告警规则

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-alerts
  namespace: monitoring
data:
  llm-alerts.yml: |
    groups:
      - name: llm_inference_alerts
        rules:
          # GPU 显存告警
          - alert: GPU_Memory_High
            expr: DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE > 0.90
            for: 5m
            labels:
              severity: warning
              component: gpu
            annotations:
              summary: "GPU 显存使用率过高"
              description: "GPU {{ $labels.gpu }} 显存使用率超过 90%,当前值: {{ $value | humanizePercentage }}"
          
          - alert: GPU_Memory_Critical
            expr: DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE > 0.95
            for: 1m
            labels:
              severity: critical
              component: gpu
            annotations:
              summary: "GPU 显存即将耗尽"
              description: "GPU {{ $labels.gpu }} 显存使用率超过 95%,即将 OOM"
          
          # GPU 利用率告警
          - alert: GPU_Utilization_Low
            expr: DCGM_FI_DEV_GPU_UTIL < 10
            for: 10m
            labels:
              severity: info
              component: gpu
            annotations:
              summary: "GPU 利用率过低"
              description: "GPU {{ $labels.gpu }} 利用率低于 10%,可能存在资源浪费"
          
          - alert: GPU_Utilization_High
            expr: DCGM_FI_DEV_GPU_UTIL > 95
            for: 5m
            labels:
              severity: warning
              component: gpu
            annotations:
              summary: "GPU 利用率过高"
              description: "GPU {{ $labels.gpu }} 利用率超过 95%"
          
          # GPU 温度告警
          - alert: GPU_Temperature_High
            expr: DCGM_FI_DEV_GPU_TEMP > 85
            for: 5m
            labels:
              severity: warning
              component: gpu
            annotations:
              summary: "GPU 温度过高"
              description: "GPU {{ $labels.gpu }} 温度超过 85°C,当前值: {{ $value }}°C"
          
          - alert: GPU_Temperature_Critical
            expr: DCGM_FI_DEV_GPU_TEMP > 95
            for: 1m
            labels:
              severity: critical
              component: gpu
            annotations:
              summary: "GPU 温度危险"
              description: "GPU {{ $labels.gpu }} 温度超过 95°C,即将触发降频或关机"
          
          # LLM 延迟告警
          - alert: LLM_TTFT_High
            expr: histogram_quantile(0.95, sum(rate(llm_latency_seconds_bucket{stage="ttft"}[5m])) by (le, model)) > 5
            for: 5m
            labels:
              severity: warning
              component: inference
            annotations:
              summary: "LLM 首 Token 延迟过高"
              description: "模型 {{ $labels.model }} 的 p95 TTFT 超过 5 秒"
          
          - alert: LLM_TTFT_Critical
            expr: histogram_quantile(0.99, sum(rate(llm_latency_seconds_bucket{stage="ttft"}[5m])) by (le, model)) > 10
            for: 2m
            labels:
              severity: critical
              component: inference
            annotations:
              summary: "LLM 首 Token 延迟严重"
              description: "模型 {{ $labels.model }} 的 p99 TTFT 超过 10 秒"
          
          # 错误率告警
          - alert: LLM_Error_Rate_High
            expr: sum(rate(llm_errors_total[5m])) by (model) / sum(rate(llm_requests_total[5m])) by (model) > 0.01
            for: 5m
            labels:
              severity: warning
              component: inference
            annotations:
              summary: "LLM 错误率过高"
              description: "模型 {{ $labels.model }} 错误率超过 1%"
          
          - alert: LLM_Error_Rate_Critical
            expr: sum(rate(llm_errors_total[5m])) by (model) / sum(rate(llm_requests_total[5m])) by (model) > 0.05
            for: 1m
            labels:
              severity: critical
              component: inference
            annotations:
              summary: "LLM 错误率严重"
              description: "模型 {{ $labels.model }} 错误率超过 5%"
          
          # KV Cache 告警
          - alert: KV_Cache_Full
            expr: vllm_kv_cache_usage_ratio > 0.95
            for: 5m
            labels:
              severity: warning
              component: inference
            annotations:
              summary: "KV Cache 接近满载"
              description: "模型 {{ $labels.model }} 的 KV Cache 使用率超过 95%"
          
          # 请求积压告警
          - alert: Request_Backlog_High
            expr: vllm_num_requests_waiting > 100
            for: 5m
            labels:
              severity: warning
              component: inference
            annotations:
              summary: "请求积压过多"
              description: "模型 {{ $labels.model }} 等待中的请求超过 100 个"
          
          - alert: Request_Backlog_Critical
            expr: vllm_num_requests_waiting > 500
            for: 2m
            labels:
              severity: critical
              component: inference
            annotations:
              summary: "请求积压严重"
              description: "模型 {{ $labels.model }} 等待中的请求超过 500 个,可能需要扩容"

  gpu-health-alerts.yml: |
    groups:
      - name: gpu_health_alerts
        rules:
          # GPU Xid 错误告警(NVIDIA 驱动错误)
          - alert: GPU_Xid_Error
            expr: DCGM_FI_DEV_XID_ERRORS_COUNT > 0
            for: 0s
            labels:
              severity: critical
              component: gpu
            annotations:
              summary: "GPU Xid 错误"
              description: "GPU {{ $labels.gpu }} 发生 Xid 错误,可能需要重启"
          
          # ECC 错误告警
          - alert: GPU_ECC_Error
            expr: DCGM_FI_DEV_ECC_SBE_VOL_TOTAL + DCGM_FI_DEV_ECC_DBE_VOL_TOTAL > 10
            for: 5m
            labels:
              severity: warning
              component: gpu
            annotations:
              summary: "GPU ECC 错误"
              description: "GPU {{ $labels.gpu }} ECC 错误数过多"
          
          # NVLink 状态告警
          - alert: NVLink_Down
            expr: DCGM_FI_DEV_NVLINK_STATUS == 0
            for: 1m
            labels:
              severity: warning
              component: gpu
            annotations:
              summary: "NVLink 连接断开"
              description: "GPU {{ $labels.gpu }} 的 NVLink 连接可能存在问题"

8.2 AlertManager 配置

# alertmanager-config.yaml
# AlertManager 通知配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: alertmanager-config
  namespace: monitoring
data:
  alertmanager.yml: |
    global:
      resolve_timeout: 5m
    
    route:
      group_by: ['alertname', 'cluster', 'service']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 4h
      receiver: 'default-receiver'
      
      routes:
        # 严重告警直接通知
        - match:
            severity: critical
          receiver: 'critical-receiver'
          group_wait: 0s
          repeat_interval: 1h
        
        # GPU 告警单独分组
        - match:
            component: gpu
          receiver: 'gpu-oncall'
          group_by: ['gpu']
        
        # LLM 推理告警
        - match:
            component: inference
          receiver: 'llm-oncall'
          group_by: ['model']
    
    receivers:
      - name: 'default-receiver'
        webhook_configs:
          - url: 'http://notification-service:8080/webhook'
            send_resolved: true
      
      - name: 'critical-receiver'
        webhook_configs:
          - url: 'http://notification-service:8080/webhook/critical'
            send_resolved: true
        pagerduty_configs:
          - service_key: '<PAGERDUTY_SERVICE_KEY>'
            severity: critical
      
      - name: 'gpu-oncall'
        webhook_configs:
          - url: 'http://notification-service:8080/webhook/gpu'
      
      - name: 'llm-oncall'
        webhook_configs:
          - url: 'http://notification-service:8080/webhook/llm'

九、总结

本文介绍了 AI 平台可观测性的完整方案:

核心要点回顾:

  1. AI 平台 vs 传统可观测性:AI 平台有其独特的指标体系(TTFT、TPOT、KV Cache),传统的 APM 工具无法直接覆盖这些需求。

  2. 三大支柱具体化

  • 指标层:GPU 利用率/显存、推理延迟、Token 吞吐、KV Cache 利用率

  • 日志层:LLM 推理日志(Prompt/Completion/Token 统计)

  • 链路层:从用户请求到模型推理的完整调用链

  1. 监控架构:Prometheus + Loki + Jaeger + Grafana 是当前的主流组合,分别负责指标、日志、链路和可视化。

  2. GPU 监控:DCGM Exporter 是采集 NVIDIA GPU 指标的标准方案,暴露 DCGM_FI_* 系列指标。

  3. 自定义 LLM 指标:通过 Prometheus Client SDK 封装 LLM 特有指标(TTFT、TPOT、Token 吞吐等)。

  4. Grafana Dashboard:预置 LLM 推理监控和 GPU 监控 Dashboard,开箱即用。

  5. 告警规则:分层告警设计(warning → critical),按组件分组通知,确保告警精准触达。

在实际落地时,建议先建立基础监控(GPU 利用率 + 推理延迟),再逐步扩展到完整指标体系链路追踪。可观测性建设的优先级应该是:能看到问题 → 能定位问题 → 能预防问题。


相关技术栈:Prometheus、Grafana、DCGM Exporter、vLLM、Loki、Jaeger、Grafana

参考资料:LLMOps 部署与运维:大模型落地核心指南

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区