Observabilidad GPU para inferencia LLM: las doce métricas DCGM y vLLM que dictan la salud de tu producción

Este post complementa los de Tracing LLM con OpenTelemetry GenAI (la capa de tracing por encima de las métricas), Capacity planning (qué se dimensionó y qué se debe vigilar) y Continuous batching (el mecanismo que explica varias de las métricas del motor).

TL;DR

La observabilidad de un cluster de inferencia LLM se construye sobre dos fuentes complementarias: las métricas del hardware GPU expuestas por DCGM (Data Center GPU Manager) Exporter —parte del NVIDIA GPU Operator— y las métricas del motor de inferencia (vLLM, SGLang, TensorRT-LLM) expuestas en /metrics Prometheus-compatibles. Ninguna de las dos basta sola. La métrica clásica de nvidia-smi llamada GPU utilization es engañosa para LLMs: marca alto cuando hay cualquier kernel ejecutándose, sin distinguir tensor cores ardiendo de SMs esperando por HBM. La cabina de pilotaje completa tiene doce métricas DCGM en cuatro familias (compute: DCGM_FI_PROF_SM_OCCUPANCY, DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, DCGM_FI_PROF_DRAM_ACTIVE; memoria: DCGM_FI_DEV_FB_USED, DCGM_FI_DEV_FB_FREE, DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL; térmico-energético: DCGM_FI_DEV_GPU_TEMP, DCGM_FI_DEV_POWER_USAGE, DCGM_FI_DEV_CLOCK_THROTTLE_REASONS; salud: DCGM_FI_DEV_XID_ERRORS, DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, DCGM_FI_DEV_RETIRED_DBE) y cinco métricas del motor vLLM (vllm:num_requests_running, vllm:num_requests_waiting, vllm:gpu_cache_usage_perc, vllm:time_to_first_token_seconds, vllm:time_per_output_token_seconds). Cada una tiene un umbral verde/ámbar/rojo defendible, una PromQL para alerta, y al menos una falsa lectura habitual que confunde al operador junior. Las seis alertas críticas que cualquier cluster productivo debe disparar son: HBM > 92 %, throttle por térmico o por power, XID error, ECC double-bit, KV cache pool > 95 %, y TTFT P95 fuera de SLO durante 5 minutos. El objetivo de tener este panel: que el operador de turno diagnostique el origen de una degradación en menos de cinco minutos, sin abrir consola SSH a las GPUs. Cuando esto se cumple, el cluster ha pasado a operación profesional; mientras no, se opera por intuición.

Estás aquí: OBSERVE (la otra mitad del tracing)

Estás aquí: OBSERVE · métricas (DCGM + motor) complementan al tracing1 · Data2 · Tune3 · Eval4 · Deploy5 · Observe6 · Retrain

El tracing —ya cubierto en Tracing LLM con OpenTelemetry GenAI— responde qué pasó en esta request concreta. Las métricas responden qué está pasando en el cluster en agregado. Son complementarias: una alerta del lado de métricas te dice “el clúster está degradando”, el tracing te dice “y esta es la traza concreta que te lo demuestra”. Un cluster sin tracing pero con métricas opera; un cluster sin métricas pero con tracing no opera, debuggea.

La analogía: la cabina de un avión moderno

En un avión comercial moderno, el panel de instrumentos del piloto tiene más de 70 indicadores activos. Si solo hubiese uno —el altímetro, por ejemplo— el avión volaría hacia el suelo en el primer momento de baja visibilidad. Hace falta el altímetro y el indicador de actitud, y el de velocidad, y el de viraje, y el de combustible, y los de presión de aceite de cada motor, y las temperaturas de salida de turbina. Cada uno responde una pregunta distinta. Y todos juntos cubren la pregunta operacional: ¿está el avión sano, está donde debe, y va donde queremos?

La observabilidad de un cluster de inferencia LLM funciona igual. Una sola métrica —“GPU utilization 99 %"— no responde nada útil. Es como mirar solo el cuentakilómetros del coche para diagnosticar por qué hace ruido el motor. La cabina completa es doce instrumentos del lado de hardware más cinco del lado del motor de inferencia, organizados en familias que responden preguntas distintas:

  • Compute y eficiencia: ¿están los tensor cores haciendo el trabajo que esperamos o están esperando?
  • Memoria: ¿queda VRAM para nuevas requests o estamos al borde del OOM?
  • Térmico y energético: ¿el hardware está sano o está limitando el throughput silenciosamente?
  • Salud y errores: ¿hay degradación del hardware en curso (ECC, XID, NVLink)?
  • Motor de inferencia: ¿la cola crece, el KV pool está saturado, el SLO se está cumpliendo?

Las cuatro primeras responden a “¿la GPU está bien?”. La quinta responde a “¿está dando el servicio que prometimos?”. Las dos preguntas son distintas y ambas deben tener respuesta a un golpe de vista.

Por qué nvidia-smi GPU-Util engaña en LLMs

La métrica clásica que aparece en nvidia-smi como GPU-Util corresponde a DCGM_FI_DEV_GPU_UTIL. Su definición oficial: “porcentaje del tiempo durante el cual uno o más kernels estuvieron ejecutándose en la GPU”. El problema en LLMs: la fase de decode es memory-bound, no compute-bound. Cuando el motor de inferencia hace decode token a token, la GPU pasa el 90 % del tiempo esperando que la HBM termine de entregar los pesos del modelo y el KV cache. Hay un kernel corriendo (lectura de HBM); por tanto GPU-Util reporta valores cercanos al 100 %. Pero los tensor cores están parados — el cuello de botella es la memoria, no el compute.

Resultado práctico: el operador ve “GPU-Util 99 %” en Grafana y asume “GPU saturada, no se puede meter más carga”. Pero la realidad puede ser “compute al 25 %, HBM saturada al 95 %”, lo que cambia las decisiones operativas (quantization, batch size, paralelismo). La métrica clásica miente por simplificación.

Lo correcto es mirar las tres métricas de profiling DCGM del subsistema _FI_PROF_*:

  • DCGM_FI_PROF_SM_OCCUPANCY — ratio de warps activos sobre máximos por SM. ¿Hay trabajo paralelo?
  • DCGM_FI_PROF_PIPE_TENSOR_ACTIVE — % de ciclos con tensor cores efectivamente activos. ¿Está el compute trabajando?
  • DCGM_FI_PROF_DRAM_ACTIVE — % de ciclos con la HBM transfiriendo. ¿Está la memoria saturada?

Una decode-bound GPU típica de Llama 70B en H100 muestra: SM occupancy 35–55 %, tensor active 15–30 %, DRAM active 80–95 %. Esa es la “GPU saturada” real para LLMs. Las tres juntas distinguen los regímenes; cada una sola no dice nada accionable.

Cómo se montan en producción

La parte de plataforma se cubre en Cinco niveles de madurez (nivel 4 — GPU plane) y Siete fases de despliegue (fase F5). Para el observador, las piezas clave son:

NVIDIA GPU Operator. Manifiestos Helm que despliegan en cada nodo GPU: drivers, container toolkit, MIG manager y DCGM Exporter. Este último expone /metrics en formato Prometheus con todos los DCGM_FI_* listados arriba. Se scrapea desde el Prometheus interno del cluster.

Motor de inferencia. vLLM expone /metrics en el puerto 8000 (default) con métricas vllm:*. SGLang lo expone también con prefijo sglang:. TensorRT-LLM lo expone vía Triton Inference Server con prefijo nv_inference:. La convención básica de nombres es similar entre los tres motores; los umbrales y queries de este post asumen vLLM, pero se traducen.

ServiceMonitor / PodMonitor. Recurso del operador de Prometheus que indica qué scrapear. Ejemplo mínimo:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: vllm-inference
spec:
  selector:
    matchLabels: { app: vllm }
  podMetricsEndpoints:
    - port: metrics
      interval: 15s

Dashboards. El operador de NVIDIA publica dashboards Grafana de referencia para DCGM en nvidia/dcgm-exporter (repo oficial). vLLM publica uno en vllm-project/vllm (carpeta examples/). Ambos sirven como base; cada equipo añade los paneles propios de su SLO.

Las doce métricas DCGM organizadas por familia

Cabina DCGM: 12 métricas en 4 familiasCOMPUTEDCGM_FI_PROF_SM_OCCUPANCYDCGM_FI_PROF_PIPE_TENSOR_ACTIVEDCGM_FI_PROF_DRAM_ACTIVE¿Compute trabaja oespera por HBM?MEMORIADCGM_FI_DEV_FB_USEDDCGM_FI_DEV_FB_FREEDCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL¿Queda VRAM paranuevas requests?TÉRMICO · ENERGÉTICODCGM_FI_DEV_GPU_TEMPDCGM_FI_DEV_POWER_USAGEDCGM_FI_DEV_CLOCK_THROTTLE_REASONS¿Hardware sano olimitando silenciosamente?SALUDDCGM_FI_DEV_XID_ERRORSDCGM_FI_DEV_ECC_DBE_VOL_TOTALDCGM_FI_DEV_RETIRED_DBE¿Hay degradacióndel silicio en curso?Cada familia responde una pregunta distinta · ninguna basta sola

Familia 1 — Compute

DCGM_FI_PROF_SM_OCCUPANCY — Ratio de warps activos por SM sobre el máximo posible. Valor entre 0 y 1.

  • Verde: 0.30–0.70 (régimen típico LLM en decode).
  • Ámbar: < 0.20 sostenido (batch demasiado pequeño, GPU infrautilizada en paralelismo).
  • Rojo: 0.95 sostenido con DRAM_ACTIVE bajo (kernel patológico saturando SMs).

DCGM_FI_PROF_PIPE_TENSOR_ACTIVE — % de ciclos con tensor cores ejecutando. La métrica clave de “¿el compute está produciendo?”.

  • Verde en prefill: 50–80 %.
  • Verde en decode: 15–30 % (decode es memory-bound, no es síntoma de problema).
  • Rojo: < 5 % sostenido en prefill o el motor no usa los tensor cores (mala config, formato incompatible).

DCGM_FI_PROF_DRAM_ACTIVE — % de ciclos con HBM transfiriendo datos. Métrica clave para detectar saturación de memoria.

  • Verde en decode: 60–85 %.
  • Ámbar: > 90 % sostenido (HBM cuello de botella firme — explica la TPOT alta).
  • Rojo: > 95 % sostenido con KV cache pool < 70 % (algo está pidiendo HBM que no es el motor; investigar leaks).

Familia 2 — Memoria

DCGM_FI_DEV_FB_USED — Frame Buffer (HBM) usado en MiB.

  • Verde: 70–85 % del total.
  • Ámbar: 86–92 %.
  • Rojo: > 92 % (riesgo de OOM en el siguiente paged-attention allocation).

PromQL para porcentaje sobre cluster: 100 * sum(DCGM_FI_DEV_FB_USED) / sum(DCGM_FI_DEV_FB_TOTAL).

DCGM_FI_DEV_FB_FREE — Frame Buffer libre. Complementaria de la anterior; útil para alertas absolutas (< 4096 MiB libres).

DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL — Bandwidth NVLink agregado en MB/s. Para topologías TP (tensor parallel) que cruzan GPUs vía NVLink, esta métrica revela si el reparto de paralelismo está saturando el bus.

  • Verde: variable según topología. En 4×H100 SXM con NVLink 4.0, capacidad teórica 450 GB/s por GPU. Régimen TP=4 típico: 50–150 GB/s sostenido.
  • Rojo: > 90 % capacidad sostenido (revisar si el modelo cabría con TP menor o pipeline parallel).

Familia 3 — Térmico y energético

DCGM_FI_DEV_GPU_TEMP — Temperatura del die en °C.

  • Verde: < 75 °C.
  • Ámbar: 75–82 °C.
  • Rojo: > 83 °C (cerca del thermal throttle automático de H100; revisar ventilación, caudal de aire, temperatura de entrada al rack).

DCGM_FI_DEV_POWER_USAGE — Consumo en watts. Para H100 SXM, TDP nominal 700 W. Útil para tres cosas: detectar workload inusualmente bajo (sospechar idle o stall), facturar coste energético real, y disparar alertas si el draw se acerca al límite de la PDU.

DCGM_FI_DEV_CLOCK_THROTTLE_REASONS — Bitmap codificado con las razones de throttle activas. Es la métrica que silenciosamente explica las degradaciones de TPOT.

Bits relevantes:

  • 0x0000000000000001 — Idle (no es problema).
  • 0x0000000000000002 — App clocks setting.
  • 0x0000000000000004 — SW Power Cap (límite de software, p. ej. por nvidia-smi -pl).
  • 0x0000000000000008 — HW Slowdown.
  • 0x0000000000000010 — Sync Boost (NVIDIA Sync).
  • 0x0000000000000020 — SW Thermal Slowdown (límite térmico de software).
  • 0x0000000000000040 — HW Thermal Slowdown (límite térmico de hardware — emergencia).
  • 0x0000000000000080 — HW Power Brake Slowdown (caída de tensión PSU).
  • 0x0000000000000100 — Display Clock Setting.

Cualquier throttle salvo Idle con valor > 0 sostenido es alerta. La degradación de TPOT con DRAM_ACTIVE ya alto y throttle térmico activo es el clásico “el rack está mal ventilado, no es problema del motor”.

Familia 4 — Salud

DCGM_FI_DEV_XID_ERRORS — Contador acumulado de XID errors del driver. Los XID son códigos de evento crítico que NVIDIA documenta exhaustivamente (XID 13: graphics engine exception; XID 31: GPU memory page fault; XID 43: reset channel verif error; XID 79: GPU has fallen off the bus; XID 95: uncontained ECC error; etc.). Cualquier incremento es alerta inmediata: muchos XID requieren reset del nodo o RMA de la GPU.

DCGM_FI_DEV_ECC_DBE_VOL_TOTAL — Errores ECC double-bit volátiles (no corregibles). A diferencia de los single-bit (que ECC corrige silenciosamente y se contabilizan en DCGM_FI_DEV_ECC_SBE_*), los double-bit corrompen datos. Cualquier valor > 0 es alerta crítica: la GPU debe ser drenada y revisada.

DCGM_FI_DEV_RETIRED_DBE — Páginas físicas de HBM retiradas por double-bit errors acumulados. NVIDIA retira páginas defectuosas automáticamente para prevenir corrupción futura. Más de 4–8 páginas retiradas en una GPU sugiere degradación del silicio: documentar y planificar reemplazo en próxima ventana de mantenimiento.

Las cinco métricas del motor de inferencia (vLLM)

Las métricas DCGM responden “¿está sana la GPU?”. Las del motor responden “¿está el servicio cumpliendo el SLO?”. Sin ellas, sabes que el hardware funciona pero no sabes si los clientes están contentos.

vllm:num_requests_running — Requests actualmente en el batch. Si llega al --max-num-seqs configurado y no baja, el motor está saturado en concurrencia (revisar VRAM y rebalancear vía autoscaler — ver Autoscaling LLM en Kubernetes).

vllm:num_requests_waiting — Requests en cola, sin entrar al batch. Cualquier valor > 0 sostenido durante minutos indica que el cluster no escala con la carga. Esta es la métrica primaria para HPA.

vllm:gpu_cache_usage_perc — % del KV cache pool usado.

  • Verde: 50–80 %.
  • Ámbar: 80–92 %.
  • Rojo: > 92 % (riesgo de preempt-on-OOM: vLLM tirará requests para liberar memoria, lo que aumenta TTFT visiblemente).

vllm:time_to_first_token_seconds — Histograma de TTFT por request. Se consume como histogram_quantile(0.95, sum by(le)(rate(vllm:time_to_first_token_seconds_bucket[5m]))). Comparado contra el SLO de TTFT P95 dispara la alerta primaria de servicio.

vllm:time_per_output_token_seconds — Histograma de TPOT. Equivalente al anterior pero para fluidez de streaming. Comparado contra el SLO de TPOT P95 dispara la alerta secundaria.

Las seis alertas que deben pagear en producción

Cualquier cluster productivo serio dispara estas seis alertas a un canal con rotación de guardia. Sin estas, el SLO se cumple por suerte, no por proceso.

groups:
  - name: gpu-llm-critical
    rules:
      - alert: GpuHbmNearOom
        expr: 100 * (DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL) > 92
        for: 2m
        labels: { severity: critical }
        annotations:
          summary: "HBM de {{ $labels.gpu }} en {{ $value }}% — riesgo OOM"

      - alert: GpuThermalOrPowerThrottle
        expr: (DCGM_FI_DEV_CLOCK_THROTTLE_REASONS != 0) and ignoring(reason) (DCGM_FI_DEV_CLOCK_THROTTLE_REASONS != 1)
        for: 1m
        labels: { severity: warning }
        annotations:
          summary: "GPU {{ $labels.gpu }} en throttle (reasons={{ $value }})"

      - alert: GpuXidErrorDetected
        expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
        labels: { severity: critical }
        annotations:
          summary: "XID error en GPU {{ $labels.gpu }} — investigar inmediatamente"

      - alert: GpuEccDoubleBit
        expr: DCGM_FI_DEV_ECC_DBE_VOL_TOTAL > 0
        labels: { severity: critical }
        annotations:
          summary: "ECC double-bit en GPU {{ $labels.gpu }} — drenar nodo"

      - alert: VllmKvCachePoolNearFull
        expr: vllm:gpu_cache_usage_perc > 0.95
        for: 3m
        labels: { severity: warning }
        annotations:
          summary: "KV cache pool > 95% en {{ $labels.instance }}"

      - alert: VllmTtftP95OutOfSlo
        expr: histogram_quantile(0.95, sum by(le, instance)(rate(vllm:time_to_first_token_seconds_bucket[5m]))) > 1.5
        for: 5m
        labels: { severity: warning }
        annotations:
          summary: "TTFT P95 sobre SLO ({{ $value }}s > 1.5s)"

Estas seis cubren el 80 % de los incidentes que afectan a SLO. El 20 % restante exige investigación con tracing (ver Tracing LLM con OpenTelemetry GenAI).

Tabla maestra: umbrales y queries

MétricaVerdeÁmbarRojoQuery base (PromQL)
SM occupancy0.30–0.700.15–0.30< 0.10 sostenidoDCGM_FI_PROF_SM_OCCUPANCY
Tensor active (decode)15–30 %< 10 %< 3 %DCGM_FI_PROF_PIPE_TENSOR_ACTIVE
DRAM active60–85 %85–95 %> 95 % con KV bajoDCGM_FI_PROF_DRAM_ACTIVE
FB used70–85 %86–92 %> 92 %100 * DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL
NVLink BW< 70 % cap70–90 % cap> 90 % capDCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL
GPU temp< 75 °C75–82 °C> 83 °CDCGM_FI_DEV_GPU_TEMP
Power usage< 90% TDP90–98 % TDP> 98 % TDPDCGM_FI_DEV_POWER_USAGE
Throttle reasons0 o IdleApp/SWHW Therm/PowerDCGM_FI_DEV_CLOCK_THROTTLE_REASONS
XID errorssin cambiocualquier deltaincrease(DCGM_FI_DEV_XID_ERRORS[5m])
ECC DBE0> 0DCGM_FI_DEV_ECC_DBE_VOL_TOTAL
Retired pages< 44–8> 8DCGM_FI_DEV_RETIRED_DBE
KV cache used50–80 %80–92 %> 92 %vllm:gpu_cache_usage_perc
Requests waiting01–5 sostenido> 10 sostenidovllm:num_requests_waiting
TTFT P95< SLO80–100 % SLO> SLOver query alerta arriba
TPOT P95< SLO80–100 % SLO> SLOhistogram_quantile(0.95, sum by(le)(rate(vllm:time_per_output_token_seconds_bucket[5m])))

Tres pitfalls que confunden al operador junior

Pitfall 1 — “GPU-Util al 99 % = saturada”. Como se explicó al inicio: DCGM_FI_DEV_GPU_UTIL se enciende con cualquier kernel. Lo correcto es mirar las tres _PROF_* (SM occupancy, tensor active, DRAM active) juntas. GPU util 99 % + tensor active 8 % + DRAM active 92 % = “saturada por memoria, no compute”; GPU util 99 % + tensor active 75 % + DRAM active 50 % = “saturada por compute, prefill heavy”. Las dos situaciones piden palancas distintas.

Pitfall 2 — confundir ECC single-bit (SBE) con double-bit (DBE). Los single-bit se corrigen silenciosamente y son inevitables en cualquier HBM bajo carga (radiación cósmica, fluctuaciones de tensión). Un contador SBE creciendo lentamente no es alerta — es física. El DBE sí: corrompe datos. Distinguir las dos métricas evita falsas alarmas y falsos negativos a partes iguales.

Pitfall 3 — alertar sobre num_requests_waiting > 0 sin contexto. Un valor instantáneo de 1 o 2 durante un pico es normal. Lo que importa es la cola sostenida: usar for: 5m con umbral 3–5. Sin esa ventana, el sistema satura el canal de alertas con ruido.

Aplicado a hardware on-premise típico

Para un cluster genérico de 4×H100 SXM 80 GB con NVLink intra-nodo:

  • DCGM Exporter desplegado vía NVIDIA GPU Operator, un DaemonSet por nodo GPU.
  • Prometheus interno con retención 30 días para métricas de alta frecuencia, 1 año para downsampled (Thanos/Mimir si el volumen lo justifica).
  • Grafana con tres dashboards estándar: hardware GPU (DCGM), motor (vLLM), SLO (TTFT/TPOT/RPS contra objetivos escritos).
  • Alertmanager con rotación de guardia y rate-limiting por silencio agrupado por nodo.
  • Cardinalidad controlada: gpu (id local), node, pod, model — no añadir request_id ni labels de alta cardinalidad a métricas (eso es trabajo del tracing).

Volumen estimado para un cluster de 16 GPUs con scraping cada 15 s: ~2 millones de samples/min, ~25 GB/día de Prometheus crudo. Manejable con un Prometheus por cluster + retention; si el equipo escala a > 64 GPUs, considerar Thanos sidecar o VictoriaMetrics. Ver Catálogo de herramientas OSS LLMOps para alternativas equivalentes.

Lo que no hemos cubierto (próximos artículos)

  • Tracing de cargas LLM: ya cubierto en Tracing LLM con OpenTelemetry GenAI.
  • Autoscaling basado en estas métricas: ver Autoscaling LLM en Kubernetes.
  • Runbooks de incident response: cómo cada una de estas alertas se traduce a acción concreta (drain, restart, RMA, escalado, rollback).
  • Cost accounting: usar DCGM_FI_DEV_POWER_USAGE y vllm:request_success_total para showback de coste por tenant.
  • Monitorización de fairness multi-tenant: cuando varios tenants comparten cluster, qué métricas detectan que uno está acaparando el KV cache.

Ver también

Referencias

  • NVIDIA — DCGM Exporter (repo nvidia/dcgm-exporter, métricas y unidades documentadas).
  • NVIDIA — DCGM Field Identifiers reference (lista completa de DCGM_FI_*).
  • NVIDIA — XID Errors documentation (catálogo de códigos XID y procedimientos de remediación).
  • NVIDIA — NVIDIA GPU Operator (Helm chart oficial).
  • vLLM project — examples/production_monitoring/ (PromQL y dashboards Grafana de referencia).
  • Prometheus — Histogram and summary best practices (para construir queries de percentiles defendibles).
  • NVIDIA — H100 Tensor Core GPU datasheet (TDP, HBM bandwidth, NVLink capacities).