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.
Precio del nodocapex amortizado+ opex (€/hora)Reparto + normalizaciónCPU / GPU / RAM / discosuma = precio totalAsignación por USOGPU medio usada = medio costepor pod / namespace / equipoLo que OpenCost garantiza: ni se pierde ni se inventa coste.· On-prem: declaras el coste del nodo; OpenCost lo reparte (no necesita factura cloud).· La GPU se asigna por USO, no por presencia: una GPU al 50 % imputa la mitad de su coste.· Trampa: si el precio base por defecto está mal, TODO el reparto está mal (infra-precio típico on-prem).

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:

FuenteQué aporta
kube-state-metricsestado de objetos K8s (pods, requests/limits)
node-exporterrecursos y capacidad del nodo
cAdvisoruso 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:

PiezaFunción
Cost modelresuelve el precio del nodo y lo reparte/normaliza entre recursos
Allocation APIsirve el coste asignado por dimensión (/allocation y endpoints)
Exporterpublica 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:

ComponenteCá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ónestimació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:

EndpointQué devuelve
/allocationcoste asignado por la dimensión que pidas
/costDataModelel precio de nodo resuelto
/allNodePricingprecios horarios por nodo
/pricingSourceSummaryresumen de la fuente de precios
/metricsmé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ámetroQué controlaEjemplo
windowrango temporal7d, today, fechas concretas
aggregatedimensión de agregaciónnamespace, label:equipo, pod
accumulatesumar el rango o por intervalotrue (total) / false (serie)
idleincluir o no el coste de idletrue / false / separate
filterfiltrar 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-exporter da DCGM_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 < 10 durante 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.

GPU (DCGM)util, memoria, potencia→ a PrometheusCoste USADO → pod / equipoimputado por utilizaciónCoste IDLE → dueño namespacealerta util < 10 % > 15 minMIG: 1 A100 → hasta 7reparte la tarjeta, idle 50%→~0La asignación por uso revela el idle; MIG y el scheduling lo recuperan. OpenCost mide; otras capas actúan.

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:

EquipoGPU-horasUtil. mediaCoste asignado (€)Eficiencia
A · chat-prod2.880 (4 GPU)78 %~3.744alta
B · batch1.440 (2 GPU)55 %~1.872media
C · experimentación1.440 (2 GPU)22 %~1.872baja
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): 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:

  1. Tener Prometheus con kube-state-metrics, node-exporter, cAdvisor y dcgm-exporter.
  2. Instalar OpenCost con Helm, apuntándolo a tu Prometheus.
  3. Configurar el precio del nodo en euros (default.json / values.yaml) — el paso que más se descuida.
  4. Exponer /metrics y 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:

CapacidadOpenCostKubecost
Asignación de coste (CPU/GPU/mem/PV)
GPU vía DCGM✓ (3.0)
Retención larga de históricodepende de tu Prometheusincluida
Rightsizing automático✓ (Turbonomic)
Gobierno, alertas, RBAC enterprisebásico
Soporte comercialcomunidad✓ (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:

PasoAcciónVerificación
1Prometheus con kube-state-metrics, node-exporter, cAdvisorseries presentes
2NVIDIA GPU Operator + dcgm-exporterDCGM_FI_DEV_GPU_UTIL en Prometheus
3Instalar OpenCost (Helm) apuntando a Prometheus/allocation responde
4Configurar el precio del nodo en € (capex+opex)/allNodePricing muestra el real
5Etiquetar pods/namespaces por equipo y productoaggregate=label:equipo reparte bien
6Paneles Grafana de coste, eficiencia e idleel desperdicio se ve
7Alerta de idle (util<10 for: 15m) al dueñola alerta llega al equipo
8(opcional) backend de larga retenciónhistó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)

  1. 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.
  2. 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.
  3. 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”.
  4. 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.
  5. 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

Fuentes