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:
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:
| Fuente | Qué cubre |
|---|---|
| RAPL | CPU y DRAM |
| NVML | potencia de GPU NVIDIA |
| ACPI | gestión de energía de la plataforma |
| Redfish / IPMI | potencia de plataforma (BMC) |
| Modelos de regresión | estimació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).
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:
- Vatímetro a la entrada del nodo (o lectura del PDU/BMC), midiendo la potencia real del nodo.
- Carga controlada: un sweep estable a una potencia conocida (p. ej. el motor al 80 % de GPU).
- Comparar: la suma de
kepler_container_joules_totaldel nodo + el idle frente a la lectura del vatímetro en la misma ventana. - Ajustar el factor o el modelo de Kepler si la desviación es notable, y documentar el error residual.
- 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-smipara 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
| Objetivo | Herramienta | Por qué |
|---|---|---|
| Atribución continua por pod (producción) | Kepler + DCGM | eBPF, por pod/MIG, a Prometheus |
| Medición fina + optimización (banco) | Zeus | mide y optimiza (power capping) |
| Carbono en un pipeline de ML | CodeCarbon | CO₂eq integrado, cercano al vatímetro |
| Precisión cruda de CPU (RAPL) | Scaphandre | metrología de hardware |
| Calibración / verdad-terreno | vatímetro | el 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
| KPI | Qué indica | De dónde |
|---|---|---|
| J/token por modelo | eficiencia energética | Kepler ÷ tokens del motor |
| Potencia media por pod | consumo por carga | rate(kepler_container_joules_total) |
| gCO₂/token (horario) | huella real | energía × intensidad de red |
| Energía en idle | desperdicio | potencia de pods sin tráfico |
| PUE efectivo | overhead del DC | energí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:
| Equipo | Energí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
| Paso | Acción | Verificación |
|---|---|---|
| 1 | dcgm-exporter (GPU Operator) | DCGM_FI_DEV_POWER_USAGE en Prometheus |
| 2 | Kepler DaemonSet, kernel con eBPF | kepler_container_joules_total por pod |
| 3 | dcgmEndpoint apuntando al exporter | atribución de GPU/MIG funciona |
| 4 | Métrica de intensidad de red (esios) | grid_carbon_intensity en Prometheus |
| 5 | Calibración vs vatímetro (1 vez/tipo nodo) | barra de error documentada |
| 6 | Paneles 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)
- 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.
- 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. - 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).
- 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.
- 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
- MLPerf Power — la medición a la pared estándar (PTDaemon), complementaria a la instrumentación software (DCGM/RAPL/Kepler).
- Leaderboards de eficiencia energética de LLMs — dónde van a parar los J/token que estas herramientas producen: los rankings públicos y cómo interpretarlos sin dejarse engañar por el setup.
- Del vatio al carbono: PUE, intensidad de la red y el coste real de un token — el paso de conversión de vatios medidos a gCO₂eq y a euros, aplicando PUE del datacenter e intensidad de la red eléctrica del país.
- Palancas de eficiencia energética en inferencia LLM — las optimizaciones que reducen el consumo que estas herramientas miden: quantization, batching, motor y schedulers de potencia.
Fuentes
- Kepler · GitHub (CNCF, eBPF, por pod/nodo) — https://github.com/sustainable-computing-io/kepler
- Kepler · configuración (fuentes, dcgmEndpoint para MIG) — https://github.com/sustainable-computing-io/kepler/blob/main/docs/user/configuration.md
- CNCF · explorando Kepler (consumo de aplicaciones cloud) — https://www.cncf.io/blog/2023/10/11/exploring-keplers-potentials-unveiling-cloud-application-power-consumption/
- Red Hat · Kepler: monitorización eficiente de energía para Kubernetes — https://next.redhat.com/2023/08/22/introducing-kepler-efficient-power-monitoring-for-kubernetes/
- arXiv · Container-level Energy Observability in Kubernetes (overhead) — https://arxiv.org/pdf/2504.10702
- NVIDIA · dcgm-exporter — https://github.com/NVIDIA/dcgm-exporter
- Zeus Project (ml.energy) — https://ml.energy/zeus/