OpenCost a fondo: cómo se asigna el coste de GPU en Kubernetes
Notación: importes en euros (N €), decimales con coma. Referencias europeas (proveedor OVH entre los soportados). No se usa el símbolo de dólar (en este sitio es delimitador de fórmula).
Qué cubre este artículo
Segundo artículo del track de FinOps (A2), y primer deep dive de herramienta. En la introducción de FinOps se vio que OpenCost es el estándar CNCF de asignación de coste; aquí se abre la caja: cómo asigna el coste por dentro, de dónde saca los datos, cómo se configuran los precios on-prem en euros, qué expone su API, y dónde están las trampas. Entender la mecánica importa porque define lo que cualquier herramienta encima (incluido Kubecost) puede y no puede hacer, y porque un precio base mal puesto invalida todo el reparto. Sin recomendaciones; solo la mecánica y la metodología.
Qué es OpenCost
OpenCost es un proyecto vendor-neutral, Apache 2.0, originalmente construido por Kubecost y donado a la CNCF (proyecto en incubación). Es una capa de asignación de coste para Kubernetes: lee el uso de recursos del cluster, lo cruza con un precio, y reparte el coste por las dimensiones de Kubernetes hasta el contenedor (OpenCost · GitHub). Soporta precios dinámicos vía las APIs de facturación de AWS, Azure y GCP, y precios personalizados para clusters on-prem (OpenCost · configuración).
Lo que no es: no es un optimizador, ni un sistema de gobierno, ni una herramienta de unit economics de producto. Es la pieza que responde “¿cuánto cuesta cada namespace/pod/equipo?”, y deja el resto a las capas de arriba. Por eso es el cimiento del track de FinOps: sin asignación correcta, no hay coste por token ni modelo TCO que valga.
El modelo de coste: asignación a nivel de nodo
La clave de OpenCost es que el modelo trabaja a nivel de nodo. Parte de la capacidad de recursos del nodo (CPU, RAM, GPU, almacenamiento) y de su precio total, y reparte ese precio entre los recursos. Cuando el proveedor no da precios explícitos por recurso, OpenCost usa la ratio de unos precios base (tarifas marginales, personalizables) y los normaliza para que la suma de los componentes iguale el precio total del nodo (OpenCost · on-prem).
Esto es lo que hace que funcione on-prem: tú declaras el coste del nodo (capex amortizado
- opex), y OpenCost lo reparte sin necesidad de una factura de cloud. La normalización garantiza que no se “pierda” ni se “invente” coste: lo que pagas por el nodo es exactamente lo que se reparte entre sus recursos.
De dónde saca los datos: Prometheus
OpenCost no instrumenta nada por su cuenta: lee de Prometheus, que es un prerrequisito de la instalación (OpenCost · GitHub). Las fuentes:
| Fuente | Qué aporta |
|---|---|
kube-state-metrics | estado de objetos K8s (pods, requests/limits) |
node-exporter | recursos y capacidad del nodo |
cAdvisor | uso real de CPU/memoria por contenedor |
dcgm-exporter (NVIDIA) | uso, memoria y potencia de GPU |
Con esas series, OpenCost automatiza el join entre el uso de recursos de Kubernetes y el
precio del proveedor, reduciendo la PromQL personalizada que harías a mano (Grafana ·
manage costs).
La señal de GPU viene de DCGM vía el dcgm-exporter (parte del NVIDIA GPU Operator),
la misma base que la observabilidad GPU. Sin
Prometheus y sin DCGM exportando, OpenCost no ve la GPU.
Arquitectura: las piezas
OpenCost no es un monolito; son tres responsabilidades que conviene distinguir:
| Pieza | Función |
|---|---|
| Cost model | resuelve el precio del nodo y lo reparte/normaliza entre recursos |
| Allocation API | sirve el coste asignado por dimensión (/allocation y endpoints) |
| Exporter | publica las métricas de coste en /metrics para Prometheus/Grafana |
El flujo de datos: Prometheus tiene las series de uso (de kube-state-metrics, cAdvisor, dcgm-exporter); el cost model las cruza con el precio resuelto; la Allocation API las sirve agregadas por la dimensión que pidas; y el exporter las devuelve a Prometheus para que Grafana las pinte. Es un ciclo: las métricas entran de Prometheus y el coste vuelve a Prometheus enriquecido. Esa simetría es lo que permite reutilizar tu stack de observabilidad sin montar una base de datos nueva: OpenCost vive encima de Prometheus, no al lado.
Una nota operativa: el análisis histórico depende de la retención de Prometheus. Para informes de coste de meses, conviene un backend de larga duración (Thanos, Mimir, VictoriaMetrics) detrás, o exportar las asignaciones periódicamente a un almacén propio.
Precios on-prem en euros: la pieza que decide todo
En on-prem, el dato más importante —y el que más se descuida— es el precio del nodo. Se
configura por proveedor; las opciones son alibaba, aws, azure, gcp, oracle, ovh
o default (on-prem) (OpenCost · configuración).
Para on-prem se usa default, dando precios base en un default.json (o sobreescribiendo el
values.yaml del Helm); lo que no sobreescribas usa el valor del chart (OpenCost · on-prem).
Para fijar el precio del nodo en euros, el cálculo es el del modelo TCO: capex amortizado + opex. Un ejemplo de nodo 8×H100:
| Componente | Cálculo | €/hora |
|---|---|---|
| Capex (nodo ~240.000 €, 36 meses) | 240.000 ÷ 26.280 h | ~9,13 |
| Energía (5,6 kW × PUE 1,4 × 0,058 €/kWh, Francia) | ~0,455 €/h | ~0,46 |
| Mantenimiento, red, operación | estimación | ~1,5 |
| Total nodo | ~11,1 €/h |
Ese ~11 €/h es el número que pones en OpenCost, y a partir de él se reparte todo. Y aquí está la trampa documentada: con la configuración de precios por defecto, OpenCost infra-precia la CPU y la GPU on-prem (issue #3781). Si no ajustas los precios base a tu coste real, el reparto será sistemáticamente bajo y el coste por token que reportes, irreal. Configurar el precio del nodo no es opcional: es el dato del que cuelga todo lo demás.
Configurar el precio: ejemplo
El precio base se da en el values.yaml del Helm (o un default.json), en euros por hora y
por unidad de recurso, derivados del coste total del nodo:
# values.yaml — precios on-prem en €/hora (nodo 8xH100, Francia)
opencost:
customPricing:
enabled: true
provider: custom
costModel:
description: "Nodo 8xH100 on-prem"
CPU: "0.030" # €/CPU-hora
RAM: "0.004" # €/GB-hora
GPU: "1.30" # €/GPU-hora ← el dato que más mueve el coste/token
storage: "0.0002" # €/GB-hora
OpenCost normaliza esos valores para que, multiplicados por la capacidad del nodo, sumen
el precio total que declaraste (~11 €/h en el ejemplo). El número que más hay que cuidar es
el GPU: a 1,30 €/GPU-hora, las 8 tarjetas son ~10,4 €/h, el grueso del coste del nodo. Un
GPU base mal puesto desplaza todo el coste por token. Revisa siempre los precios resueltos en
/allNodePricing tras configurar, porque el bug del infra-precio por defecto muerde justo
aquí.
La asignación: por uso, no por presencia
El comportamiento que más ha cambiado y más importa para GPU: en las versiones recientes, el coste asignado de una GPU lo determina su uso, no su presencia. Si una GPU de 100 €/mes está usada solo a la mitad, el coste asignado es ~50 € ([búsqueda]). Esto alinea la asignación con la realidad —pagas por lo que usas— pero tiene una consecuencia: la otra mitad no desaparece, es idle que alguien sigue pagando. OpenCost lo expone, y de ahí sale la palanca de optimización.
OpenCost asigna a cualquier dimensión de Kubernetes: cluster, nodo, namespace, controlador (deployment, statefulset…), servicio, pod y contenedor, y por labels (equipo, producto). El acceso es vía la Allocation API y varios endpoints que exponen la mecánica:
| Endpoint | Qué devuelve |
|---|---|
/allocation | coste asignado por la dimensión que pidas |
/costDataModel | el precio de nodo resuelto |
/allNodePricing | precios horarios por nodo |
/pricingSourceSummary | resumen de la fuente de precios |
/metrics | métricas de coste para Prometheus |
El endpoint /metrics es el que convierte OpenCost en un exportador de Prometheus: una
vez ahí, puedes escribir PromQL para calcular el coste y la eficiencia de cualquier concepto
de Kubernetes y montar paneles en Grafana (OpenCost · exporter).
Parámetros de la Allocation API
La /allocation se controla con unos pocos parámetros que conviene conocer porque definen
qué número obtienes:
| Parámetro | Qué controla | Ejemplo |
|---|---|---|
window | rango temporal | 7d, today, fechas concretas |
aggregate | dimensión de agregación | namespace, label:equipo, pod |
accumulate | sumar el rango o por intervalo | true (total) / false (serie) |
idle | incluir o no el coste de idle | true / false / separate |
filter | filtrar por namespace, label… | namespace:"llm-prod" |
El parámetro idle es el más revelador: con idle=separate, OpenCost te devuelve el
coste de idle como una fila aparte, lo que permite ver de un vistazo cuánto se está
pagando por capacidad sin usar. Una consulta de aggregate=label:equipo con
idle=separate sobre window=30d es, literalmente, el informe de chargeback mensual con el
desperdicio destacado.
GPU a fondo: DCGM, uso, MIG e idle
La GPU es el recurso caro, así que su asignación merece detalle:
- Señal de uso:
dcgm-exporterdaDCGM_FI_DEV_GPU_UTIL(utilización),DCGM_FI_DEV_FB_USED(memoria), y la potencia. OpenCost usa la utilización para asignar por uso. - MIG (Multi-Instance GPU): una A100 se parte en hasta 7 instancias aisladas, lo que permite reducir el idle del 50 % a casi 0 % repartiendo una tarjeta entre cargas pequeñas ([búsqueda]; ver compartir GPU: time-slicing, MPS y MIG). La asignación de coste de cada instancia MIG sigue la misma lógica de uso.
- Idle: el patrón fiable de detección combina tres piezas — métricas DCGM en Prometheus,
una alerta que salta cuando
DCGM_FI_DEV_GPU_UTIL < 10durante más de 15 minutos, y un enrutado de esa alerta al equipo dueño del namespace ([búsqueda]).
Un matiz de atribución que conviene conocer: MIG crea instancias aisladas que Kubernetes ve como recursos distintos, así que OpenCost las asigna limpiamente, cada una a su pod. El time-slicing, en cambio, comparte la misma GPU física entre varios pods sin aislamiento de cómputo: ahí la utilización por pod es más difícil de separar, y la atribución de coste se vuelve aproximada (se reparte la GPU entre los pods que la comparten, según su uso medido). La regla práctica: si necesitas chargeback exacto por equipo sobre GPU compartida, MIG da una atribución más limpia que el time-slicing, a cambio de la rigidez de las particiones fijas. Es un trade-off entre exactitud de coste y flexibilidad que conviene decidir antes, no después de montar el reparto.
Eficiencia: el número que dispara la acción
Con el coste asignado por uso, OpenCost permite calcular la eficiencia: cuánto del coste asignado corresponde a trabajo útil frente al total. Una GPU al 30 % de utilización tiene una eficiencia del 30 %: el 70 % restante es coste de idle que alguien paga sin recibir trabajo. Esa cifra, por equipo y por namespace, es la que convierte un panel en una conversación de chargeback: “tu namespace tiene 5 GPUs asignadas con una eficiencia del 25 %; o subes la utilización o liberas tres tarjetas”. Conecta con FinOps y multi-tenancy: la asignación localiza el desperdicio; el scheduling y la co-residencia lo recuperan.
Ejemplo trabajado: el informe de asignación
Sobre el nodo de ~11 €/h (8×H100), tres equipos comparten el cluster un mes. Lo que devuelve
una consulta /allocation?aggregate=label:equipo&idle=separate&window=30d:
| Equipo | GPU-horas | Util. media | Coste asignado (€) | Eficiencia |
|---|---|---|---|---|
| A · chat-prod | 2.880 (4 GPU) | 78 % | ~3.744 | alta |
| B · batch | 1.440 (2 GPU) | 55 % | ~1.872 | media |
| C · experimentación | 1.440 (2 GPU) | 22 % | ~1.872 | baja |
| idle (separate) | — | — | ~1.100 | — |
Lo que revela el informe: el coste asignado a A, B y C sale de sus GPU-horas y el precio del nodo; pero la fila idle (~1.100 €/mes) es capacidad que nadie usó y todos pagan en el prorrateo. Y la columna de eficiencia señala a C: 2 GPUs al 22 % es ~1.560 € al mes de los que solo ~340 € son trabajo útil. Ese es el dato accionable: C no tiene un problema de modelo, tiene un problema de utilización. Sin OpenCost, esos ~1.100 € de idle y la ineficiencia de C quedan diluidos en una factura agregada que nadie cuestiona. Con él, son una fila con nombre y un número en euros.
Consultas PromQL útiles
Como exportador de Prometheus, OpenCost permite construir los paneles a mano. Algunas
consultas de referencia (los nombres exactos de métrica varían por versión; comprueba en tu
/metrics):
# Coste por nodo y hora (precio resuelto)
node_total_hourly_cost
# Coste de GPU asignado por namespace (€/hora)
sum by (namespace) (
container_gpu_allocation * on(node) group_left node_gpu_hourly_cost
)
# Utilización media de GPU por namespace (para la eficiencia)
avg by (namespace) (DCGM_FI_DEV_GPU_UTIL)
# GPUs ociosas: utilización < 10 % sostenida
DCGM_FI_DEV_GPU_UTIL < 10
La última, envuelta en una alerta con for: 15m y enrutada al dueño del namespace, es el
patrón estándar de detección de idle. Con estas series en Grafana tienes coste por equipo,
eficiencia y desperdicio en un panel — el “informe Inform” de la fase FinOps en tiempo real.
Precios dinámicos cloud vs personalizados on-prem
Conviene entender la diferencia, porque cambia la fiabilidad del dato:
- Cloud (aws/azure/gcp/oracle/ovh): OpenCost obtiene el precio dinámicamente de la API de facturación del proveedor, así que el coste asignado refleja la tarifa real (incluidos descuentos, spot, etc.) sin que tú declares nada.
- On-prem (
default/custom): tú declaras el precio, derivado de tu modelo TCO. La ventaja es control total; el riesgo, que un precio mal calculado sesga todo. Por eso el modelo TCO (capex amortizado + opex) no es un ejercicio académico: es literalmente el input de OpenCost.
El proveedor ovh entre los soportados es relevante para una plataforma europea: si parte
de la carga va a un cloud soberano europeo, OpenCost puede asignar su coste con precio
dinámico, y mezclarlo con el on-prem en la misma vista de asignación.
Del coste del pod al coste por token
OpenCost llega hasta “este pod de vLLM costó X €/hora”. El coste por token —la métrica que compara on-prem vs cloud— necesita una pieza más: el gateway (LiteLLM) que cuenta los tokens por petición y por equipo, como se vio en la introducción de FinOps. La cadena completa es: OpenCost da el coste del pod por uso → el gateway da los tokens por equipo → dividiendo, sale el coste por token por equipo. OpenCost es la mitad del hierro de esa ecuación; sin él, sabrías los tokens pero no su coste real. Por eso A2 (la asignación) es el cimiento del que cuelga todo el coste por token de la propuesta.
Despliegue: Helm + Prometheus
El despliegue típico es vía Helm, con Prometheus como prerrequisito (para scraping y almacenamiento de series). Pasos conceptuales:
- Tener Prometheus con
kube-state-metrics,node-exporter,cAdvisorydcgm-exporter. - Instalar OpenCost con Helm, apuntándolo a tu Prometheus.
- Configurar el precio del nodo en euros (
default.json/values.yaml) — el paso que más se descuida. - Exponer
/metricsy construir paneles en Grafana con PromQL sobre las métricas de coste.
Como exportador de Prometheus, OpenCost te deja escribir PromQL para el coste y la eficiencia de cualquier concepto y crear dashboards a medida (OpenCost · exporter). Reutiliza infraestructura que un cluster con GPU ya tiene para observabilidad (DCGM, Prometheus, Grafana): OpenCost solo añade la capa de precio y asignación.
OpenCost vs Kubecost (lo justo)
OpenCost es la base gratis; Kubecost (IBM) es el comercial construido sobre él. En breve, qué añade el comercial:
| Capacidad | OpenCost | Kubecost |
|---|---|---|
| Asignación de coste (CPU/GPU/mem/PV) | ✓ | ✓ |
| GPU vía DCGM | ✓ | ✓ (3.0) |
| Retención larga de histórico | depende de tu Prometheus | incluida |
| Rightsizing automático | — | ✓ (Turbonomic) |
| Gobierno, alertas, RBAC enterprise | básico | ✓ |
| Soporte comercial | comunidad | ✓ (IBM) |
Para la mayoría de clusters on-prem con equipo de plataforma, OpenCost cubre la asignación; Kubecost aporta cuando quieres optimización automatizada y features enterprise sin operarlo tú. El detalle de la comparación —y cuándo merece la pena pagar— es el artículo A3.
Checklist de implementación
Para que OpenCost dé un coste por equipo defendible, el orden importa:
| Paso | Acción | Verificación |
|---|---|---|
| 1 | Prometheus con kube-state-metrics, node-exporter, cAdvisor | series presentes |
| 2 | NVIDIA GPU Operator + dcgm-exporter | DCGM_FI_DEV_GPU_UTIL en Prometheus |
| 3 | Instalar OpenCost (Helm) apuntando a Prometheus | /allocation responde |
| 4 | Configurar el precio del nodo en € (capex+opex) | /allNodePricing muestra el real |
| 5 | Etiquetar pods/namespaces por equipo y producto | aggregate=label:equipo reparte bien |
| 6 | Paneles Grafana de coste, eficiencia e idle | el desperdicio se ve |
| 7 | Alerta de idle (util<10 for: 15m) al dueño | la alerta llega al equipo |
| 8 | (opcional) backend de larga retención | histórico de meses disponible |
El paso 4 es el que más se salta y el que más invalida el resultado. Si solo haces uno bien, que sea ese: sin el precio del nodo correcto en euros, los pasos 5–7 reparten un coste equivocado con mucha precisión.
Límites y trampas (data-driven)
- Infra-precio por defecto. Con la config de precios por defecto, OpenCost subestima CPU y GPU on-prem (#3781). Ajusta los precios base a tu coste real o todo el reparto será bajo.
- Depende de Prometheus y DCGM. Sin las series correctas (especialmente
dcgm-exporter), no hay asignación de GPU. La calidad del dato de entrada manda. - Asignación por uso ≠ coste total. La GPU al 50 % imputa la mitad; la otra mitad es idle que sigue costando. No confundas “coste asignado” con “coste pagado”.
- Retención de Prometheus. El análisis histórico de coste depende de cuánto retiene tu Prometheus; para series largas, plantea un backend de larga duración.
- Asignación, no medición por token. OpenCost llega al pod; el coste por token necesita el gateway (LiteLLM) por encima, como se vio en la intro de FinOps.
Con la mecánica de OpenCost clara, el siguiente artículo (A3) compara qué añade Kubecost y las alternativas comerciales, para decidir qué stack de FinOps adoptar. Pero el cimiento —la asignación correcta, con el precio del nodo bien puesto en euros— es este.
Cierre
OpenCost parece “instalar una herramienta de coste” y es, en realidad, declarar cuánto vale tu hierro y dejar que se reparta solo. Esa simplicidad es su fuerza —vive encima del Prometheus y el DCGM que ya tienes, sin base de datos nueva— y también su trampa: el reparto es tan bueno como el precio del nodo que declaras, y el valor por defecto infra-precia la GPU on-prem. Bien configurado en euros, OpenCost convierte la factura agregada del cluster en un reparto por equipo, por producto y por GPU, separa el coste usado del idle, y expone la eficiencia que dispara las conversaciones de chargeback. Es la mitad del hierro de la ecuación del coste por token —la otra mitad, los tokens, la pone el gateway—, y sin esta mitad no hay modelo TCO ni comparativa on-prem vs cloud que se sostenga. Para una propuesta de arquitectura soberana, OpenCost es la pieza que hace que el coste deje de ser una intuición y pase a ser una tabla en euros, reproducible y auditable, con la GPU ociosa señalada con nombre y apellidos. La fase Inform del FinOps de GPU empieza aquí, y todo lo demás cuelga de que este número esté bien.
Ver también
- Chargeback y showback de GPU — de la asignación de coste de OpenCost al reparto por equipo con presupuestos (Kueue) y atribución de tokens (LiteLLM).
- La utilización de GPU como palanca FinOps — el coste del idle y cómo la ocupación mueve el coste asignado.
- TCO del cluster GPU on-premise: amortización, energía e infraestructura — el CAPEX, la amortización y el €/GPU-hora que OpenCost necesita como precio base cuando el nodo no es cloud sino on-premise propio.
Fuentes
- OpenCost · GitHub (CNCF, Apache 2.0) — https://github.com/opencost/opencost
- OpenCost · configuración (proveedores: aws/azure/gcp/oracle/ovh/default) — https://opencost.io/docs/configuration/
- OpenCost · configuración on-prem (precios personalizados) — https://opencost.io/docs/configuration/on-prem/
- OpenCost · exportador de Prometheus — https://opencost.io/docs/integrations/opencost-exporter/
- OpenCost · issue #3781 (infra-precio CPU/GPU on-prem por defecto) — https://github.com/opencost/opencost/issues/3781
- NVIDIA · dcgm-exporter — https://github.com/NVIDIA/dcgm-exporter
- Grafana · manage costs (OpenCost + Kubernetes) — https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/manage-costs/