From n00b to ZeroCool / Profesionalización

Autoscaling sin quemar dinero: crecer con cabeza (y métricas)

Guía práctica de autoscaling en Kubernetes: HPA, requests/limits, métricas, costos, errores comunes y checklist para crecer sin tirar lana.

Lo que vale la pena leer aquí

Te pasa así: es quincena, sube el tráfico, el PM te suelta un “salimos en una nota” y tu backend empieza a toser. Tú con una laptop ya medio guerrera abres Grafana con una mano y con la otra haces cuentas mentales: ¿cuánto va a doler este pico en la factura?

Te pasa así: es quincena, sube el tráfico, el PM te suelta un “salimos en una nota” y tu backend empieza a toser. Tú con una laptop ya medio guerrera abres Grafana con una mano y con la otra haces cuentas mentales: ¿cuánto va a doler este pico en la factura?

Si escalas a lo bruto, la app vive… pero el presupuesto se muere. Si no escalas, cae el incidente, y el cliente cae encima de ti.

Autoscaling bien hecho es ese punto medio: que el sistema respire cuando lo necesita y que regrese a su tamaño real cuando el hype baja.

Qué vas a sacar de esto

  • Cómo decidir qué sí autoscalea y qué no (porque no todo debe moverse solo).
  • Cómo configurar un HPA en Kubernetes con bases sanas: requests/limits, métricas y límites.
  • Cómo evitar el clásico “escaló, salió más caro… y ni sirvió”.
  • Qué mirar en observabilidad para que el autoscaling no sea magia negra.
  • Errores comunes en production (los que pasan con Slack ardiendo y el deploy tarde).

Contexto práctico: autoscaling no es “más pods y ya”

Autoscaling es una decisión de ingeniería y de negocio. Del lado técnico quieres latencia estable. Del lado lana quieres pagar por capacidad útil, no por pánico.

Tres verdades incómodas:

  1. Si tu app no está bien instrumentada, el autoscaling es una ruleta.
  2. Si tus requests están inflados, el cluster te cobra como si fueras Netflix aunque seas “MiTiendaMX”.
  3. Si escalas por CPU cuando el cuello de botella es la DB, solo creas más pods formados, esperando turno.

Escena muy real: campaña de e-commerce con promo “hasta agotar existencias”. El tráfico se concentra en 15–20 minutos porque la banda entra desde el metro, el Oxxo o el camión con datos medio chafas. Eso genera patrones raros: muchas conexiones cortas, retries, latencia subiendo por la red. Si no entiendes ese comportamiento, el HPA puede reaccionar tarde… o de más.

Guía principal: autoscaling en Kubernetes sin incendiar el presupuesto

1) Decide el objetivo: ¿qué estás protegiendo?

Antes de tocar YAML, define esto en corto:

  • ¿Qué métrica representa “la app está sufriendo”? (p95 latency, cola, RPS por pod, errores 5xx)
  • ¿Cuál es tu SLO realista? (ej. p95 < 300ms en /checkout)
  • ¿Cuál es tu límite de costo aceptable por pico? (ej. “tolero +30% durante 2 horas”)

Decisión con filo:

  • Si tu dolor principal es latencia, CPU suele ser un proxy medio chafa.
  • Si tu dolor principal es throughput, RPS por pod suele pegar mejor.

2) Asegura los cimientos: requests y limits decentes

HPA funciona mejor cuando Kubernetes puede programar pods con información real. Si tus requests son fantasía, el scheduler y el autoscaling toman decisiones igual de fantasiosas.

  • requests muy altos → menos densidad por nodo → más nodos → más $$$.
  • requests muy bajos → el scheduler mete de más → contention → latencia → escalas tarde.

Regla práctica para iniciar (y luego afinas):

  • Pon requests cerca del p50/p60 de consumo real.
  • Pon limits con margen, pero no infinito, para no pegarle a los vecinos.

Ejemplo de Deployment (simplificado):

resources:
  requests:
    cpu: "200m"
    memory: "256Mi"
  limits:
    cpu: "800m"
    memory: "512Mi"

Tip de guerra: si no tienes historial, corre un load test corto en staging (k6/Locust), observa consumo y ajusta. No es perfecto, pero es mejor que cotizar recursos “porque me late”. Ese “me late” luego llega en forma de factura o de rollback.

3) Empieza simple: HPA por CPU… pero con guardrails

CPU-based HPA no es lo más fino, pero sirve para una primera versión si:

  • tu app es CPU-bound
  • o necesitas una solución rápida para dejar de apagar fuegos

Ejemplo de HPA (autoscaling/v2):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 12
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Decisiones que sí pegan en production:

  • minReplicas: si lo pones en 1 “para ahorrar”, prepárate para cold starts y picos que llegan antes que el pod.
  • maxReplicas: te protege de un bug que dispara CPU y te escala hasta quebrarte la cuenta.
  • scaleDown lento: evita el efecto acordeón (sube/baja/sube/baja) que mata estabilidad.

4) No te cases con CPU: escala por lo que realmente te duele

Cuando ya sobreviviste el primer round, el siguiente paso “pro” es escalar por señales más cercanas al usuario y al negocio:

  • RPS por pod (si tienes métricas HTTP)
  • latencia p95 (con cuidado: puede subir por dependencia externa)
  • cola (Kafka/Rabbit/SQS): backlog por consumidor

Normalmente lo resuelves con:

  • Prometheus + Prometheus Adapter (custom metrics)
  • o KEDA (event-driven autoscaling)

Ejemplo conceptual (custom metric con Prometheus Adapter):

metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "20"

Tradeoff honesto:

  • Custom metrics te dan autoscaling más inteligente, pero suben la complejidad del setup de observabilidad. Si tu Prometheus anda frágil, tu autoscaling también.

5) El costo no se controla solo con HPA: se controla con el combo completo

HPA escala pods. Pero lo que pega en la factura son los nodos (y el overprovisioning). Para “no quemar dinero”, revisa también:

  • Cluster Autoscaler (o autoscaling de node pools) para ajustar nodos
  • VPA (Vertical Pod Autoscaler) para recomendar requests (y a veces aplicar)
  • PodDisruptionBudgets para evitar que un scale-down te rompa disponibilidad
  • Scheduling con intención: taints/tolerations, topology spread, anti-affinity

Checklist mental de supervivencia:

  • Si HPA sube pods pero no hay capacidad, solo verás pods en Pending y cero mejora.
  • Si sí hay capacidad pero tus requests están inflados, vas a forzar más nodos de los necesarios.

6) Observabilidad mínima que sí necesitas

Si no puedes ver esto, vas a operar a ciegas:

  • CPU/mem por pod (y por container)
  • p95/p99 latency por endpoint crítico
  • RPS por pod (o al menos por servicio)
  • errores 4xx/5xx
  • saturación de dependencias: DB connections, pool, timeouts
  • eventos del HPA (por qué escaló)

Comandos útiles cuando todo se pone raro:

kubectl get hpa
kubectl describe hpa api-hpa
kubectl top pods -n tu-namespace
kubectl get events -n tu-namespace --sort-by=.lastTimestamp | tail -n 30
Autoscaling sin quemar dinero: crecer con cabeza (y métricas) - visual explicativa 1
Visual de apoyo: Qué vas a sacar de esto

Screenshots sugeridos

  • Grafana: panel con RPS, p95 latency, CPU por pod y réplicas actuales.
  • kubectl describe hpa: sección de Events donde se ve el motivo del scaling.
  • Vista del cluster: nodos vs pods (para notar si el cuello es capacity).
  • Billing (AWS/GCP/Azure): gráfica por hora del cómputo durante un pico.

Errores comunes (y cómo salir del hoyo)

Error 1: “Escalamos y la latencia siguió igual”

Síntoma: más pods, misma p95.

Causa típica: el cuello está en DB, cache o un API externo. Solo creaste más competidores.

Arreglo:

  • mide saturación (conexiones, locks, pool, timeouts)
  • agrega caching donde duele
  • aplica rate limiting/backpressure
  • escala la dependencia (o particiona workload)

Error 2: requests inflados “por si acaso”

Síntoma: el cluster escala nodos de más y la factura se siente como tarjeta en diciembre.

Causa típica: alguien puso cpu: 2000m porque una vez en staging vio un spike y se paniqueó.

Arreglo:

  • baja requests por iteraciones (10–20%) y valida SLO
  • usa recomendaciones de VPA (aunque no lo apliques automático)
  • separa endpoints pesados a otro deployment (no castigues a todo el API)

Error 3: minReplicas = 1 y “pero funciona en mi laptop”

Síntoma: cada pico pega primero con errores, luego ya se estabiliza.

Causa típica: cold start + warming de cache + JIT + conexiones.

Arreglo:

  • sube minReplicas (aunque duela)
  • mejora tiempos de arranque, readiness/liveness
  • pre-warm de cache si aplica

Error 4: flapping (sube y baja cada minuto)

Síntoma: réplicas bailando, logs explotando, métricas inestables.

Causa típica: target agresivo y sin behavior.

Arreglo:

  • scaleDown con stabilizationWindowSeconds de 300–600s
  • limita la tasa de scale up/down

Error 5: escalas por CPU pero tu app es I/O-bound

Síntoma: CPU “tranquila”, pero latencia alta, threads bloqueados.

Arreglo:

  • escala por RPS, cola o latencia (bien medida)
  • revisa timeouts y pools (HTTP, DB)
Autoscaling sin quemar dinero: crecer con cabeza (y métricas) - visual explicativa 2
Visual de apoyo: Contexto práctico: autoscaling no es “más pods y ya”

Checklist final (para crecer sin tirar lana)

  • Tengo requests/limits basados en datos (aunque sea aproximado, pero medido).
  • Definí minReplicas pensando en experiencia de usuario, no solo en costo.
  • Puse maxReplicas con límite razonable (protección anti-bug y anti-bill-shock).
  • Configuré behavior para evitar flapping.
  • Tengo métricas de latencia y errores, no solo CPU.
  • Validé que la dependencia crítica (DB/cola/cache) aguanta el incremento.
  • Mi cluster puede crecer (Cluster Autoscaler / node pool autoscaling) o tengo capacity planeada.
  • Probé un pico controlado (load test) y vi si el autoscaling llega a tiempo.
  • Revisé el costo por hora del pico y lo comparé contra el impacto al negocio.

FAQ

1) ¿HPA o VPA? ¿Cuál uso?

HPA para escalar horizontal (más réplicas) según carga. VPA para ajustar requests/limits. En la práctica: arranca con HPA y usa VPA en modo recomendación para afinar recursos sin andar adivinando.

2) ¿Escalo por CPU o por memoria?

CPU es más estable como señal, pero no siempre representa el dolor. Memoria sirve si eres memory-bound (ej. procesos que cachean mucho). Si tu problema es throughput/latencia, mejor usa métricas de app (RPS, cola) con Prometheus/KEDA.

3) ¿Qué valor pongo en averageUtilization?

Para empezar, 60–75% suele funcionar. Menos te da colchón (más costo), más te arriesga a saturación y latencia. Ajusta viendo SLO y comportamiento real.

4) ¿Por qué mis pods se quedan en Pending cuando escala?

Porque no hay capacidad en nodos, o tus requests no caben. Revisa autoscaling de nodos, cuotas del cloud y si tus requests están inflados.

5) ¿Cómo evito que un spike me destruya el presupuesto?

Pon maxReplicas, limita el scale-up rate y define budgets/alerts de costo. Y ojo: si el spike es por bots o scraping, ataja con WAF/rate limit antes de pagar cómputo por tráfico basura.

Siguiente episodio

La siguiente parada es el primo incómodo del autoscaling: capacity planning y FinOps en serio. Cómo poner números, alertas y políticas para que “escaló” no signifique “ya valimos en la factura”.