Medir la energía en producción: Kepler, DCGM y el stack práctico (precisión y overhead)

Notación: importes en euros (N €), decimales con coma. No se usa el símbolo de dólar (en este sitio es delimitador de fórmula).

Qué cubre este artículo

Tercer artículo del track de energía (C3). En C1 se hizo el inventario conceptual de las herramientas; aquí entramos en lo práctico: cómo se despliega el stack de medición en Kubernetes, de dónde saca el dato cada pieza, qué precisión tiene frente a un vatímetro, cuánto overhead añade y cómo se atribuye la energía por pod y por instancia MIG. Es el equivalente del B2/B3 para energía: dejar de hablar de qué mide cada herramienta y empezar a montar el banco que lo mide. Con comandos; sin recomendaciones, solo la mecánica.


El stack de medición en Kubernetes

Para medir energía por carga en un cluster GPU, el stack práctico tiene cuatro piezas, y la buena noticia es que tres de ellas ya las tienes si haces observabilidad:

DCGM exporterpotencia de GPUKepler (eBPF)energía por pod/nodoPrometheusseries temporalesGrafanapaneles · alertasVatímetrocalibraciónDCGM, Prometheus y Grafana ya están si haces observabilidad GPU; Kepler añade la atribución por pod.El vatímetro físico (puntual) calibra las estimaciones: no es parte del stack continuo, pero es la verdad-terreno.

La pieza nueva frente a un stack de observabilidad normal es Kepler, que añade la atribución de energía por pod. DCGM ya da la potencia de la GPU; Prometheus la almacena; Grafana la pinta. Es infraestructura reutilizada, no un sistema nuevo, en línea con la observabilidad GPU por DCGM.


DCGM exporter: la base de la potencia de GPU

La potencia de la GPU —la fuente dominante en inferencia LLM— sale del dcgm-exporter de NVIDIA (parte del GPU Operator), que expone métricas DCGM a Prometheus. La métrica clave es DCGM_FI_DEV_POWER_USAGE (potencia instantánea en W), junto con utilización (DCGM_FI_DEV_GPU_UTIL) y memoria. Con eso, la energía es la integral de la potencia en el tiempo sobre la ventana de la carga. DCGM da la potencia a nivel de tarjeta; para repartirla por pod o por instancia MIG hace falta una capa encima —Kepler—.


Kepler a fondo: eBPF y las fuentes

Kepler (Kubernetes-based Efficient Power Level Exporter) es un exporter de Prometheus que mide energía a nivel de contenedor, pod y nodo, usando eBPF para reducir el overhead y métodos científicos para mejorar la precisión (Kepler · GitHub). Su fortaleza es que combina varias fuentes de potencia:

FuenteQué cubre
RAPLCPU y DRAM
NVMLpotencia de GPU NVIDIA
ACPIgestión de energía de la plataforma
Redfish / IPMIpotencia de plataforma (BMC)
Modelos de regresiónestimación cuando no hay métrica real

Para la GPU, el parámetro dcgmEndpoint apunta a la URL de métricas del dcgm-exporter, lo que permite la atribución de potencia por instancia MIG (Kepler · configuración). Es decir: DCGM da la potencia de la tarjeta, Kepler la reparte entre los pods (y las particiones MIG) que la usan, vía eBPF y modelos. El resultado son métricas de energía por pod en Prometheus, listas para Grafana, usables para scheduling o scaling, reporte de consumo y visualización (Kepler · GitHub).

RAPL (CPU/DRAM)NVML/DCGM (GPU)ACPI · Redfish/IPMIKepler (eBPF + modelo)reparte la potenciaEnergía por pod / MIG→ PrometheusDCGM da la potencia de la tarjeta; Kepler la atribuye a cada pod y partición MIG, igual que OpenCost atribuye el coste.

Despliegue: DaemonSet sobre Prometheus

El despliegue es un DaemonSet (un Kepler por nodo), apuntando al Prometheus existente y al dcgm-exporter:

# Helm, apuntando al dcgm-exporter para la atribución de GPU
helm install kepler kepler/kepler \
  --namespace kepler --create-namespace \
  --set dcgmEndpoint="http://dcgm-exporter:9400/metrics"

Prerrequisitos: un kernel con eBPF, el dcgm-exporter corriendo (del GPU Operator) y Prometheus scrapeando. Una vez desplegado, Kepler expone métricas como kepler_container_joules_total por pod, que se consultan con PromQL y se pintan en Grafana. La energía por carga deja de ser una estimación manual y pasa a ser una serie temporal continua, igual que el coste con OpenCost.


Consultas PromQL: energía por pod

Con Kepler exportando, las consultas que construyen los paneles. Los nombres exactos varían por versión; comprueba en tu /metrics:

# Energía acumulada por pod (julios)
kepler_container_joules_total

# Potencia media por namespace en la última hora (W)
sum by (namespace) (rate(kepler_container_joules_total[1h]))

# Potencia de GPU por nodo (de DCGM, W)
sum by (node) (DCGM_FI_DEV_POWER_USAGE)

# Energía por token: potencia ÷ throughput (con métrica del gateway/motor)
sum(rate(kepler_container_joules_total[5m])) / sum(rate(vllm_generation_tokens_total[5m]))

La última es la que cierra el círculo con el track de coste: cruza la energía de Kepler con los tokens del motor/gateway para dar J/token en tiempo real, por carga. Con eso en Grafana, tienes energía por pod, por namespace y por token, en continuo —el equivalente energético del informe de coste de OpenCost—.


Un procedimiento de calibración

Como Kepler estima, conviene calibrar una vez por tipo de nodo. Un procedimiento simple:

  1. Vatímetro a la entrada del nodo (o lectura del PDU/BMC), midiendo la potencia real del nodo.
  2. Carga controlada: un sweep estable a una potencia conocida (p. ej. el motor al 80 % de GPU).
  3. Comparar: la suma de kepler_container_joules_total del nodo + el idle frente a la lectura del vatímetro en la misma ventana.
  4. Ajustar el factor o el modelo de Kepler si la desviación es notable, y documentar el error residual.
  5. Repetir por cada tipo de nodo (la calibración no es transferible entre hardware distinto).

Con ese error residual documentado, el dato continuo de Kepler tiene una barra de error conocida, que es justo lo que un auditor de CSRD o un comité técnico va a pedir. Medir sin calibrar da un número sin credibilidad; calibrar una vez convierte la estimación continua en un dato defendible.


Precisión: estimación vs vatímetro

La honestidad de datos del track de energía: Kepler estima (eBPF + modelos + contadores), no mide con un vatímetro. La precisión depende de la fuente: con NVML/DCGM y RAPL disponibles, el dato es bastante fiel (lee contadores de hardware); cuando cae en modelos de regresión (sin métrica real), es una estimación con más error. La buena práctica: calibrar contra un vatímetro físico al menos una vez por tipo de nodo, ajustar, y usar Kepler como proxy continuo. El vatímetro es la verdad-terreno puntual; Kepler, la serie continua calibrada contra ella. Para un reporte CSRD que un auditor pueda cuestionar, esa calibración inicial es lo que da credibilidad al dato continuo.


Overhead: el coste de medir

Medir energía tiene su propio coste de cómputo, que conviene presupuestar. En un entorno Kubernetes, el overhead del sistema de monitorización lo reparten el servidor de Prometheus y los targets: el Prometheus server consume ~150 % de CPU (1,5 cores) y ~15 GB de RAM, mientras que los targets (node exporter y dcgm-exporter) usan en conjunto ~35 % de CPU y ~300 MB de RAM (arXiv · Container-level Energy Observability). Kepler usa eBPF precisamente para mantener bajo su propio overhead. La lectura: el grueso del coste de medir está en el Prometheus (que probablemente ya tienes para observabilidad), no en los exporters de energía. Añadir Kepler a un stack de observabilidad existente es marginal; montar Prometheus solo para esto sería caro. Por eso medir energía es casi gratis si ya observas, y un proyecto si no.


Zeus: para banco y optimización, no para producción continua

Zeus (ml.energy) juega un papel distinto: no es un exporter continuo de producción, es la herramienta de banco para medir y optimizar la energía de una carga concreta (un benchmark, un entrenamiento), con power capping y selección de frecuencia. Encaja en el flujo de benchmarking (junto a GuideLLM/AIPerf): durante un sweep, Zeus mide la energía por configuración y permite probar el efecto del power capping en el rendimiento. La división práctica: Kepler/DCGM en producción (atribución continua por pod), Zeus en el banco (medición fina + optimización de una carga), CodeCarbon si quieres el contexto de carbono integrado en un pipeline de ML.


CodeCarbon y Scaphandre en la práctica

Las otras dos piezas, con su rol concreto:

  • CodeCarbon se integra dentro del código de un pipeline de ML (un decorador o un context manager que envuelve el entrenamiento o la inferencia), y devuelve la energía y el CO₂eq de ese bloque, usando nvidia-smi para la GPU y estimando CPU/RAM. Encaja cuando quieres el dato de carbono por experimento o por job de ML, integrado en el flujo de datos, no a nivel de infraestructura. Es el más cercano al vatímetro entre los Python.
  • Scaphandre es un agente de metrología en Rust que lee RAPL y da la potencia de proceso/VM con precisión cruda de hardware. Para CPU es muy preciso; para GPU hay que combinarlo con NVML. Encaja cuando necesitas la metrología de CPU más fiel, o un agente ligero independiente de Kubernetes.

La división con Kepler: Kepler atribuye energía por pod en el cluster (la vista de infraestructura); CodeCarbon da el carbono por job de ML (la vista de pipeline); Scaphandre da la metrología cruda de CPU. No compiten; cubren capas distintas, y una plataforma madura usa la que corresponde a cada pregunta.


Elegir el stack por objetivo

ObjetivoHerramientaPor qué
Atribución continua por pod (producción)Kepler + DCGMeBPF, por pod/MIG, a Prometheus
Medición fina + optimización (banco)Zeusmide y optimiza (power capping)
Carbono en un pipeline de MLCodeCarbonCO₂eq integrado, cercano al vatímetro
Precisión cruda de CPU (RAPL)Scaphandremetrología de hardware
Calibración / verdad-terrenovatímetroel patrón contra el que calibrar todo

No es “una herramienta”, es un stack por objetivo: producción continua (Kepler/DCGM), banco (Zeus), reporte (CodeCarbon), calibración (vatímetro). Mezclarlos según la fase es lo que da una medición de energía completa y creíble.


Del dato a la acción

Medir es el principio; el valor está en actuar. Con la energía por pod en Prometheus:

  • Reporte (CSRD): la huella de carbono por carga, calibrada, lista para el reporte regulatorio.
  • Scheduling por energía/carbono: mover carga diferible a horas o nodos de menor intensidad (conecta con el carbono horario de la red española).
  • Power capping: con Zeus, recortar la potencia de pico con poca pérdida de rendimiento, bajando los W del numerador de J/token.
  • Optimización: identificar pods con mala eficiencia energética (mucha energía por token) y atacarlos con cuantización o batching.

El stack de medición no es un panel para mirar: es la entrada de decisiones de scheduling, optimización y reporte. Igual que OpenCost cierra el bucle del coste, Kepler+DCGM cierra el de la energía.


De la energía al carbono: la capa final

El dato de energía (kWh) se convierte en carbono multiplicando por la intensidad de red, y aquí el stack práctico se conecta con la fuente horaria. En España, la intensidad la publica Red Eléctrica (esios) y la agrega ElectricityMaps, hora a hora. El patrón:

# Carbono por carga: energía (kWh) × intensidad de red (gCO2/kWh)
(sum by (namespace) (increase(kepler_container_joules_total[1h])) / 3.6e6)
  * on() group_left() grid_carbon_intensity_gco2_kwh

donde grid_carbon_intensity_gco2_kwh es una métrica que alimentas desde esios/ElectricityMaps (un exporter o un push periódico). Con eso, el panel de Grafana muestra gCO₂ por namespace y por token, hora a hora, no una media anual. Y como la intensidad española varía mucho (de ~80 en horas solares a ~250 en noches de gas), esa resolución horaria es la que habilita el scheduling consciente del carbono: mover lo diferible a las horas limpias baja el carbono medido. El stack de medición no solo reporta el carbono; con el dato horario, permite reducirlo.


KPIs de eficiencia energética a vigilar

KPIQué indicaDe dónde
J/token por modeloeficiencia energéticaKepler ÷ tokens del motor
Potencia media por podconsumo por cargarate(kepler_container_joules_total)
gCO₂/token (horario)huella realenergía × intensidad de red
Energía en idledesperdiciopotencia de pods sin tráfico
PUE efectivooverhead del DCenergía total ÷ energía de cómputo

Estos cinco convierten el panel de energía en una herramienta de decisión: qué modelo es más eficiente, qué pod desperdicia, cuándo conviene schedulear. Sin ellos, el stack es un panel bonito; con ellos, es la entrada de optimización y reporte.


Ejemplo: atribución de energía en un cluster multi-tenant

Con Kepler repartiendo por pod, un informe de energía por equipo sobre el nodo de ejemplo (8×H100, 5,6 kW de placa, ~7,84 kW con PUE), un mes:

EquipoEnergía/mes (kWh)gCO₂ (red ES ~160)J/token medio
A · chat-prod~3.100~496 kg~2,2 J/tok
B · batch~1.400~224 kg~2,8 J/tok
C · experimentación~900~144 kg~6,5 J/tok
idle~250~40 kg

Lo que revela: A es eficiente (FP8, alta utilización → 2,2 J/token); C es ineficiente (6,5 J/token, GPU infrautilizada), igual que arrastraba el coste de idle en el track de FinOps. La columna de carbono lleva el dato al reporte CSRD por equipo. Y la fila idle (~250 kWh, ~40 kg CO₂) es energía pura desperdiciada. Sin Kepler, ese reparto no existe: solo verías el consumo total del nodo, sin saber de quién es cada vatio. Con él, la energía es atribuible y accionable, exactamente como el coste con OpenCost —de hecho, los dos paneles juntos (coste de OpenCost + energía de Kepler) dan la imagen completa de eficiencia por equipo.


Scheduling consciente de energía y carbono

La acción de mayor retorno que habilita el stack: schedulear según energía y carbono. Con la energía por pod y la intensidad horaria de red en Prometheus, dos políticas:

  • Por carbono: mover la carga diferible (entrenamiento, ingestión, batch nocturno) a las horas de menor intensidad de red —en España, las horas de máxima solar (~80 gCO₂/kWh) frente a las noches de gas (~250)—. La misma carga emite ~3× menos schedulada a la hora correcta.
  • Por eficiencia: consolidar cargas para eliminar el idle (la GPU encendida sin trabajo es energía tirada), y dirigir cada carga al hardware más eficiente para ella.

Ambas usan el mismo dato (energía por pod + intensidad horaria) que el stack ya expone, y conectan con el scheduling del cluster (Kueue/Volcano). Es la diferencia entre medir el carbono (y reportarlo) y reducirlo (schedulando). El stack de C3 habilita las dos.


Checklist de despliegue

PasoAcciónVerificación
1dcgm-exporter (GPU Operator)DCGM_FI_DEV_POWER_USAGE en Prometheus
2Kepler DaemonSet, kernel con eBPFkepler_container_joules_total por pod
3dcgmEndpoint apuntando al exporteratribución de GPU/MIG funciona
4Métrica de intensidad de red (esios)grid_carbon_intensity en Prometheus
5Calibración vs vatímetro (1 vez/tipo nodo)barra de error documentada
6Paneles Grafana (J/token, carbono, idle)KPIs visibles

El paso 5 (calibración) es el que más se salta y el que da credibilidad al dato; el paso 3 (dcgmEndpoint) es el que más se olvida y sin el cual la GPU no se atribuye.


Energía por fase: prefill vs decode

Un nivel de detalle que el stack permite y que orienta la optimización: la potencia no es constante durante una petición. El prefill (procesar el prompt) es compute-bound y tira mucha potencia en ráfaga; el decode (generar tokens) es memory-bound y consume menos pico pero durante más tiempo. Con un muestreo fino de DCGM se ve ese perfil bimodal, y eso tiene consecuencias:

  • Una carga con prompts largos (mucho prefill) tiene un perfil de potencia distinto a una de generación larga (mucho decode), aunque produzcan los mismos tokens.
  • El power capping afecta distinto a cada fase: recortar la potencia de pico toca sobre todo el prefill.

Para la mayoría de casos basta el J/token medio; para optimizar la eficiencia energética a fondo, separar el consumo de prefill y decode (alineando la ventana de DCGM con cada fase) revela dónde está el gasto. Es el equivalente energético de separar TTFT y TPOT en el benchmarking —el mismo principio, medir las dos fases por separado porque se comportan distinto—.


Estado del arte: el hueco de la resolución LLM

Una nota honesta sobre los límites del tooling actual. Las herramientas generalistas (Kepler, CodeCarbon, Scaphandre) miden bien a nivel de máquina, proceso y pod, pero carecen de resolución específica de LLM: no distinguen de serie la energía de prefill vs decode, ni la energía por token por fase, ni el coste energético de un bloque de razonamiento frente a una respuesta normal. Es un hueco abierto del campo: la atribución por pod existe, pero la atribución por token y por fase de inferencia hay que construirla cruzando la potencia (DCGM/Kepler) con las métricas del motor (vLLM expone tokens por fase). El estado del arte de 2026 es un stack maduro para la infraestructura y emergente para la resolución específica de LLM —exactamente el cruce que el harness reproducible (S4) tiene que montar a mano—. Reconocerlo evita prometer una precisión por token que las herramientas generalistas no dan solas.


Límites y trampas (data-driven)

  1. Estimación sin calibrar. Kepler estima; sin calibrar contra un vatímetro al menos una vez, el dato puede tener error notable, especialmente cuando cae en modelos de regresión.
  2. Olvidar el dcgmEndpoint. Sin apuntar Kepler al dcgm-exporter, la atribución de GPU (y MIG) no funciona; es el parámetro clave para GPU.
  3. Contabilizar el overhead mal. El grueso del coste de medir está en Prometheus; si lo montas solo para energía, presupuéstalo (1,5 cores, 15 GB).
  4. Confundir banco y producción. Zeus mide fino una carga; Kepler atribuye continuo en producción. Usar uno por el otro da el dato equivocado.
  5. Ventanas desalineadas. Para energía por token, la ventana de potencia y la de tokens deben coincidir; si no, el J/token no corresponde.

Con el stack de medición montado, el track de energía tiene su instrumentación; los artículos siguientes (C6 carbono, C8 TCO y regulación) construyen sobre estos datos. El cimiento es este: una medición continua por pod, calibrada, sobre la infraestructura de observabilidad que ya tienes.

Cierre

Medir la energía de un cluster de IA en producción no requiere un sistema nuevo: requiere añadir Kepler —atribución por pod vía eBPF— al stack de DCGM, Prometheus y Grafana que ya tienes para observabilidad. Esa es la idea central de C3: el coste de medir está casi todo en el Prometheus que ya pagas, y Kepler lo convierte de “potencia de la tarjeta” en “energía por pod, por equipo y por token”. Con dos cautelas que separan un dato creíble de un panel decorativo: calibrar una vez contra un vatímetro (para tener una barra de error que aguante una auditoría) y apuntar el dcgmEndpoint (sin el cual la GPU no se atribuye). Hecho así, la energía deja de ser el eje blando de la sostenibilidad y pasa a ser una serie temporal accionable: el J/token por modelo que delata la ineficiencia, el carbono horario que habilita el scheduling limpio, el idle que se recupera, y el reporte CSRD que se sostiene. Para la plataforma soberana, el stack Kepler+DCGM cierra el bucle de la energía igual que OpenCost cierra el del coste —y juntos, con el goodput de GuideLLM, dan las tres caras del cuadro de mando medidas sobre el mismo hierro, en euros, julios y gramos de CO₂.

Ver también

Fuentes