Un clúster de Kubernetes medio usa el 20–30% de su capacidad aprovisionada. Es decir, el 70–80% está ocioso — y en la factura de la nube eso se traduce en hasta un 35% de desperdicio evitable. Sobre una factura anual de 1 millón de $, son 350.000 $ que no van a la resiliencia, sino al miedo.
La causa raíz no es negligencia, sino overprovisioning defensivo. Un servicio cayó una vez por OOM, un ingeniero duplicó el memory request — racional en local, derrochador en global. Culpar a las personas no sirve: mientras la baseline de la plantilla esté inflada, cada servicio nuevo hereda el sobrecoste. La solución es sistémica y tiene tres palancas. Ninguna exige reescribir las aplicaciones.
Palanca 1. Right-sizing: ajustar los requests a la realidad
Los requests inflados son el principal motor del desperdicio. El scheduler empaqueta los pods por requests, no por consumo real: un servicio pide 500m de CPU, usa 95m — y el cluster autoscaler levanta nodos de más para sostener aire. La mayoría de los servicios cargan límites de CPU 3–5× por encima de su p99 real, fruto del copy-paste de una plantilla sin revisión.
La regla base es recoger 30 días de percentiles de consumo y recalcular: nuevo límite de CPU = max(p99 × 1,5; max × 1,1; floor), nuevo límite de memoria = max(p99 × 1,3; max × 1,1).
La asimetría clave: recortar la CPU de forma agresiva (el throttling es reversible — el pod solo se ralentiza), la memoria con cautela (un OOMKill mete el pod en un bucle de reinicios). El floor de CPU ronda los 100m para picos de GC y arranque; el floor de memoria depende del runtime (JVM ~256Mi, Go ~32Mi). Los percentiles no se calculan a mano: Robusta KRR toma los datos directamente de Prometheus y distingue los batch jobs bursty de los servicios steady-state, ajustando el conservadurismo de sus recomendaciones. Ahorro reportado: 35–50% de compute en 60 días.
Palanca 2. Scale-to-zero: no pagar por lo ocioso
El right-sizing encoge un pod en marcha; el scale-to-zero elimina el pod por completo cuando no hay trabajo. Tres objetivos típicos:
- Workers asíncronos. El HPA por CPU aquí es inútil: un worker ocioso no consume CPU y nunca escala a la baja. Hay que escalar por demanda — la profundidad de la cola. KEDA lleva los pods de 0 a N según la longitud de SQS / Kafka / RabbitMQ, y
cooldownPeriodamortigua el rebote. - Entornos de preview / PR. El scale-to-zero por tasa de peticiones entrantes da en torno al 60% de ahorro en entornos de vida corta. De noche, un feature namespace cuesta honestamente cero.
- Non-prod programado. Stage/demo en régimen 24/7 frente a 8/5 es una diferencia de 4× en la factura. La herramienta es kube-downscaler (el fork mantenido es py-kube-downscaler): la anotación
downscaler/uptime="Mon-Fri 08:00-20:00 UTC"en el namespace apaga los entornos fuera del horario laboral ydownscaler/excludeconserva lo imprescindible — una base de datos para tests nocturnos, por ejemplo. Resultado real: de 168 a 60 horas de cómputo por semana y $2,700/mes ahorrados con un solo cambio; el scale-up matinal tarda menos de tres minutos. KEDA escala por eventos; esto es puro calendario — para non-prod basta.
El precio de entrada es el cold start (~30 segundos hasta la primera respuesta). Para colas sensibles a la latencia se mantiene un pod caliente (paused-replicas: "1"); para tareas pesadas y largas se usa un ScaledJob en lugar de un worker de larga duración. En Kubernetes 1.36 el scale-to-zero para métricas External/Object llegó también al HPA nativo (alpha, feature gate HPAScaleToZero), pero KEDA sigue siendo la opción por defecto: production-ready y con docenas de scalers de serie.
Palanca 3. Idle a nivel de nodo: bin-packing, consolidation, spot
Incluso con requests honestos queda el idle cost: (capacidad del nodo − suma de requests) × precio del nodo. Un pool de nodos estático fija el tipo de instancia y empaqueta mal. Karpenter le da la vuelta al modelo: aprovisiona nodos a medida de pods concretos (bin-packing), reevalúa el reparto de forma continua y colapsa los nodos infrautilizados (consolidationPolicy: WhenEmptyOrUnderutilized) y mueve las cargas stateless con PDB a spot — un 60–90% más barato que on-demand. El efecto típico frente a Cluster Autoscaler con managed node groups es de menos 30–60%.
El spot no es para todo: bases de datos, singletons sin réplicas y cargas stateful sin graceful shutdown se quedan en on-demand. El patrón que funciona es una baseline en on-demand más burst en spot.
Savings Plans: comprarlos al final
Los descuentos por compromiso llegan al 66% frente a on-demand — y fijan la línea base durante uno o tres años. Comprados antes del right-sizing, cementan el gasto inflado: un descuento sobre aire. El orden correcto: primero las tres palancas, después 30 días de estabilización, después un compromiso al 80% del gasto horario medio — el margen cubre crecimiento y estacionalidad. No-upfront cede un 3–5% de descuento frente a full-upfront pero conserva la liquidez; los Compute Savings Plans cubren EC2, Fargate y Lambda sin atarse a un tipo de instancia.
Cerrar el bucle: un proceso, no un ajuste de una vez
El error cardinal es «montar el autoscaler y olvidarse». La carga y el tráfico cambian, y la optimización sin detección continua se pudre. Por eso las palancas solo funcionan sobre dos cosas. Primero, visibilidad: OpenCost reparte el coste real por namespace/label y convierte «EC2 = X $» en «payments-api = Y $». Segundo, policy: Kyverno o Gatekeeper bloquean los pods sin requests, o si no la historia del overprovisioning se repite en un mes.
Tres hábitos baratos mantienen el ahorro: un informe automático semanal por namespace en Slack — los ingenieros ven su propia tendencia, sin equipo FinOps dedicado; Infracost en los PR — el delta de coste de un cambio de requests se ve antes del merge; la revisión de las recomendaciones de VPA como punto del traspaso de on-call. Un caso real con este esquema: de $67K a $31K al mes (−54%) en ocho semanas — las mismas palancas más disciplina en logging y data transfer.
Las medidas básicas rinden un 15–30% sin un solo cambio arquitectónico. El resto es disciplina, no magia.