Contenido

Kubernetes: Autoescalado con HPA y una intro al VPA

Actualizado en marzo 2026: Este artículo usa autoscaling/v2 (la versión estable actual) e incluye features recientes como tolerancia configurable (v1.33) e In-Place Pod Resize (GA en v1.35).

Pre-requisitos

Este post es la continuación directa de la serie de Kubernetes. Necesitas:

  • Todo lo del primer capítulo: Docker (u OrbStack), kubectl y Kind con un cluster activo.
  • Lo del segundo capítulo: saber crear Deployments, Services y manifiestos YAML.
  • Un Deployment corriendo con resources.requests definidos (lo hicimos en el post anterior).

Si no tienes esto listo, revisa los posts anteriores primero. Aquí arrancamos directo al autoescalado.


Introducción

En el capítulo anterior aprendimos a escalar manualmente con kubectl scale. Eso funciona, pero tiene un problema obvio: necesitas a un humano pendiente. ¿Qué pasa un viernes a las 11 PM cuando tu app sale en redes y el tráfico se multiplica por 10? No quieres estar ahí escalando a mano.

Para eso existe el Horizontal Pod Autoscaler (HPA): le defines reglas y Kubernetes escala tus Pods automáticamente basándose en métricas reales. Es como ponerle piloto automático a tu infraestructura.

HPA Overview

El HPA monitorea métricas y ajusta automáticamente el número de réplicas de tu Deployment


¿Cómo funciona el HPA por dentro?

El HPA no es magia — es un loop de control que se ejecuta cada 15 segundos (por defecto) y hace lo siguiente:

  1. Lee métricas del Metrics Server (CPU, memoria) o de adaptadores de métricas custom.
  2. Calcula cuántas réplicas necesita usando esta fórmula:
réplicas deseadas = ⌈ réplicas actuales × (métrica actual / métrica objetivo) ⌉
  1. Escala el Deployment si la diferencia supera la tolerancia (10% por defecto).
  2. Repite el ciclo.

Por ejemplo: tienes 2 réplicas, el uso de CPU actual es 80% y tu objetivo es 50%. El cálculo sería:

réplicas = ⌈ 2 × (80 / 50) ⌉ = ⌈ 3.2 ⌉ = 4 réplicas

El HPA escalaría de 2 a 4 Pods.

HPA control loop

El loop de control del HPA: leer métricas → calcular → escalar → repetir


Instalando Metrics Server en Kind

El HPA necesita un Metrics Server para leer métricas de CPU y memoria. En clusters gestionados (EKS, GKE, AKS) suele venir preinstalado, pero en Kind hay que instalarlo manualmente.

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

En Kind necesitas un parche porque los certificados no son válidos. Edita el Deployment del Metrics Server:

kubectl patch deployment metrics-server -n kube-system \
  --type='json' \
  -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]'

Espera a que esté listo:

kubectl wait --namespace kube-system \
  --for=condition=ready pod \
  --selector=k8s-app=metrics-server \
  --timeout=90s

Verifica que funciona:

kubectl top nodes
NAME                       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
mi-cluster-control-plane   250m         12%    512Mi           26%
mi-cluster-worker          120m         6%     256Mi           13%
kubectl top pods

Si ves métricas, estás listo para el HPA.


HPA por CPU: ejemplo práctico

Vamos con el caso más común. Primero, asegúrate de tener un Deployment con resources.requests definidos. Si seguiste el post anterior, ya lo tienes. Si no, crea deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-app
  labels:
    app: mi-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mi-app
  template:
    metadata:
      labels:
        app: mi-app
    spec:
      containers:
        - name: mi-app
          image: nginx:latest
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "50m"
            limits:
              memory: "128Mi"
              cpu: "100m"
kubectl apply -f deployment.yaml

Clave: Sin resources.requests, el HPA no puede calcular porcentajes y no funcionará. Siempre define requests en tus contenedores.

Creando el HPA con comando

La forma rápida:

kubectl autoscale deployment mi-app --min=2 --max=10 --cpu-percent=50

Verifica:

kubectl get hpa
NAME     REFERENCE           TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
mi-app   Deployment/mi-app   10%/50%   2         10        2          30s

Creando el HPA con manifiesto YAML

Para producción, siempre usa YAML. Crea hpa-cpu.yaml:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mi-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
kubectl apply -f hpa-cpu.yaml

Desglosemos:

CampoQué hace
scaleTargetRefA qué Deployment apuntar
minReplicas: 2Nunca bajar de 2 réplicas (alta disponibilidad)
maxReplicas: 10Nunca pasar de 10 (control de costos)
averageUtilization: 50Escalar cuando el promedio de CPU supere 50% de los requests

Importante: Usamos autoscaling/v2 — la versión estable y actual. Las versiones v2beta1 y v2beta2 ya fueron eliminadas de Kubernetes. Si ves tutoriales que usan esas versiones, actualízalos.


HPA por memoria

El escalado por CPU es el más común, pero a veces tu app es memory-intensive (cachés, procesamiento de datos, etc.). Puedes escalar por memoria de la misma forma.

Crea hpa-memory.yaml:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mi-app-hpa-memory
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  minReplicas: 2
  maxReplicas: 8
  metrics:
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 70

Cuidado con memoria: A diferencia de CPU, la memoria no siempre baja cuando el tráfico baja (muchas apps no liberan memoria fácilmente). Esto puede causar que el HPA escale hacia arriba pero no hacia abajo. Tenlo en cuenta al elegir tus thresholds.


Combinando métricas: CPU + memoria

¿Por qué elegir una sola métrica si puedes usar ambas? Cuando defines múltiples métricas, el HPA calcula las réplicas necesarias para cada una y elige el número más alto.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mi-app-hpa-multi
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 70

Si por CPU necesitas 4 réplicas y por memoria necesitas 6, el HPA pondrá 6 réplicas. Siempre gana el número más alto para garantizar que ambas métricas estén bajo control.


Métricas custom con Prometheus

Las métricas de CPU y memoria son un buen punto de partida, pero en producción quieres escalar por métricas de negocio: requests por segundo, mensajes en cola, latencia, etc. Para esto necesitas un adaptador de métricas custom.

La combinación más popular es Prometheus + Prometheus Adapter.

Arquitectura

Pods  exportan métricas  Prometheus (scraping)  Prometheus Adapter  HPA
HPA con métricas custom

Flujo de métricas custom: App → Prometheus → Adapter → HPA

Ejemplo: escalar por requests por segundo

Supongamos que tu app expone una métrica http_requests_total y quieres escalar cuando supere los 100 requests/s por Pod:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mi-app-hpa-custom
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "100"

Nota: Configurar Prometheus + Adapter desde cero es un tema extenso. En el siguiente post te explico cómo montar Prometheus y Grafana en tu cluster paso a paso. Aquí lo importante es que sepas que el HPA puede escalar por cualquier métrica, no solo CPU y memoria.


Behavior: controlando cómo escala el HPA

Por defecto, el HPA escala hacia arriba rápido y hacia abajo lento (5 minutos de estabilización). Pero puedes personalizar este comportamiento con el campo behavior.

¿Por qué importa?

  • Scale-up demasiado agresivo: creas 50 Pods en un pico de 10 segundos y luego los tienes ociosos.
  • Scale-down demasiado agresivo: bajas réplicas y llega otro pico, causando latencia mientras los nuevos Pods arrancan.

Ejemplo con behavior personalizado

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mi-app-hpa-behavior
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
        - type: Pods
          value: 4
          periodSeconds: 60
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 120
      selectPolicy: Max

Desglosemos:

Scale-up:

CampoQué hace
stabilizationWindowSeconds: 30Espera 30s antes de escalar (evita reacciones a picos instantáneos)
Percent: 50Puede aumentar hasta 50% de las réplicas actuales por minuto
Pods: 4O agregar hasta 4 Pods por minuto (el que sea mayor)
selectPolicy: MaxElige la política más agresiva (la que agregue más Pods)

Scale-down:

CampoQué hace
stabilizationWindowSeconds: 300Espera 5 minutos antes de reducir (evita el “efecto yoyo”)
Percent: 25Reduce máximo 25% cada 2 minutos
selectPolicy: MaxElige la política que más reduzca

Tolerancia configurable (K8s v1.33+)

A partir de Kubernetes v1.33 (alpha), puedes configurar la tolerancia del HPA por recurso. Por defecto, el HPA ignora diferencias menores al 10% para evitar escalados innecesarios. Ahora puedes personalizarlo:

spec:
  behavior:
    scaleUp:
      tolerance: 0.0    # Escalar inmediatamente ante cualquier cambio
    scaleDown:
      tolerance: 0.05   # Tolerar 5% antes de reducir

Esto es útil cuando necesitas reacciones ultra-rápidas al scale-up (zero-tolerance) pero quieres ser conservador al scale-down.

Nota: Esta feature está en alpha en v1.33 y requiere habilitar el feature gate HPAConfigurableTolerance.


Probando el autoescalado

¿Cómo verificas que tu HPA funciona? Generando carga artificial.

Generando carga con un Pod temporal

kubectl run load-generator --image=busybox --rm -it -- /bin/sh -c \
  "while true; do wget -q -O- http://mi-app-svc; done"

En otra terminal, observa el HPA en acción:

kubectl get hpa mi-app-hpa --watch
NAME         REFERENCE           TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
mi-app-hpa   Deployment/mi-app   10%/50%    2         10        2          5m
mi-app-hpa   Deployment/mi-app   68%/50%    2         10        2          5m30s
mi-app-hpa   Deployment/mi-app   68%/50%    2         10        3          6m
mi-app-hpa   Deployment/mi-app   52%/50%    2         10        4          6m30s
mi-app-hpa   Deployment/mi-app   35%/50%    2         10        4          7m

Verás cómo las réplicas suben cuando la carga aumenta. Cuando detengas el generador (Ctrl+C), después de la ventana de estabilización las réplicas bajarán de vuelta.

Comandos útiles para monitorear

# Ver estado detallado del HPA
kubectl describe hpa mi-app-hpa

# Ver eventos del HPA
kubectl get events --field-selector involvedObject.name=mi-app-hpa

# Ver métricas de los Pods en tiempo real
kubectl top pods -l app=mi-app --containers
HPA escalando Pods

El HPA detecta alta carga de CPU y crea nuevas réplicas automáticamente


VPA: Vertical Pod Autoscaler (introducción)

Hasta ahora hablamos de escalar horizontalmente (más Pods). Pero hay otra dimensión: escalar verticalmente — darle más CPU y memoria a cada Pod individual.

HPA vs VPA

HPA escala horizontalmente (más Pods), VPA escala verticalmente (más recursos por Pod)

¿Qué es el VPA?

El Vertical Pod Autoscaler (VPA) analiza el uso real de recursos de tus Pods y ajusta automáticamente los requests y limits de CPU/memoria. Es como tener un asistente que dice: “hey, este Pod está pidiendo 500Mi de memoria pero solo usa 120Mi — vamos a ajustarlo”.

¿Cuándo usar VPA en vez de HPA?

EscenarioUsa
App stateless con tráfico variableHPA
App con carga estable pero recursos mal dimensionadosVPA
Bases de datos, caches (difíciles de escalar horizontalmente)VPA
Apps con picos de tráfico impredeciblesHPA (o ambos)

Modos del VPA

El VPA no es parte del core de Kubernetes — necesitas instalarlo por separado desde el repositorio oficial.

ModoComportamiento
OffSolo recomienda, no aplica cambios (ideal para empezar)
InitialAplica recursos solo cuando el Pod se crea
RecreateElimina y recrea el Pod con los nuevos recursos
InPlaceOrRecreateIntenta ajustar sin reiniciar (K8s v1.35+), si no puede, recrea el Pod

Ejemplo básico del VPA

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: mi-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mi-app
  updatePolicy:
    updateMode: "Off"  # Solo recomendaciones, no aplica cambios
  resourcePolicy:
    containerPolicies:
      - containerName: mi-app
        minAllowed:
          cpu: "25m"
          memory: "32Mi"
        maxAllowed:
          cpu: "500m"
          memory: "512Mi"

Con el modo Off, puedes consultar las recomendaciones:

kubectl describe vpa mi-app-vpa
Recommendation:
  Container Recommendations:
    Container Name: mi-app
    Lower Bound:
      Cpu:     25m
      Memory:  50Mi
    Target:
      Cpu:     50m
      Memory:  80Mi
    Upper Bound:
      Cpu:     200m
      Memory:  256Mi

Esto te dice: “tus Pods deberían tener 50m CPU y 80Mi de memoria como target”. Genial para afinar tus resources.requests sin adivinar.

Novedad K8s v1.35: La feature In-Place Pod Resource Resize es ahora GA (estable). Esto permite que el VPA (en modo InPlaceOrRecreate) ajuste CPU y memoria de tus Pods sin reiniciarlos. Un game-changer para apps que no pueden permitirse downtime.

¿HPA + VPA juntos?

Sí se puede, pero con cuidado:

  • No uses ambos para escalar por la misma métrica (ej: ambos por CPU). Van a pelear entre sí.
  • funciona bien: HPA por CPU/métricas custom + VPA por memoria (en modo Initial o Off).
  • La recomendación más segura: usa VPA en modo Off para obtener recomendaciones y ajustar tus requests manualmente. Deja el escalado activo al HPA.

Buenas prácticas para autoescalado en producción

Después de pelear con HPAs en producción, estas son las lecciones que quedan:

1. Siempre define resources.requests

Sin requests, el HPA no puede calcular porcentajes. Define requests realistas basándose en el uso real de tu app (el VPA en modo Off te ayuda con esto).

2. No pongas minReplicas: 1

Si tu Pod se reinicia, tienes cero réplicas por unos segundos. Mínimo 2 para alta disponibilidad.

3. Configura el behavior

Los defaults son razonables, pero cada app es diferente. Una API que necesita respuesta inmediata necesita un scale-up agresivo. Un worker de background puede escalar más lento.

4. Usa Pod Disruption Budgets (PDB)

Cuando el HPA reduce réplicas, asegúrate de que no baje de un mínimo seguro:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: mi-app-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: mi-app

5. Monitorea los eventos del HPA

kubectl describe hpa mi-app-hpa | grep -A 5 "Events"

Si ves mensajes como failed to get cpu utilization, revisa que Metrics Server esté corriendo y que tus Pods tengan resources.requests.

6. Cuidado con el “efecto yoyo”

Si tu scale-down es muy agresivo, el HPA baja réplicas → los Pods restantes se sobrecargan → el HPA sube réplicas → se estabilizan → el HPA baja réplicas… y así infinitamente. Solución: ventana de estabilización generosa en scale-down (300-600 segundos).


Referencias oficiales


Resumen

Hoy dominamos el autoescalado en Kubernetes:

  • El HPA escala Pods horizontalmente basándose en métricas (CPU, memoria, custom).
  • Usa autoscaling/v2 — las versiones beta ya no existen.
  • El campo behavior te da control granular sobre cómo sube y baja el escalado.
  • Las métricas custom con Prometheus te permiten escalar por métricas de negocio reales.
  • El VPA complementa al HPA ajustando recursos por Pod (escala vertical).
  • In-Place Pod Resize (GA en K8s v1.35) permite al VPA ajustar recursos sin reiniciar Pods.
  • Siempre define resources.requests, configura behavior y monitorea los eventos del HPA.

Con HPA + VPA + buenas prácticas, tu infraestructura se adapta sola al tráfico. Menos ops manuales, más café tranquilo.

En el próximo capítulo montaremos Prometheus y Grafana en nuestro cluster para tener observabilidad completa: métricas, dashboards y todo lo que necesitas para ver qué está pasando dentro de tu cluster.


¿Te gustó este artículo? Compártelo con alguien que esté empezando con Kubernetes. Y si tienes dudas, ¡déjame un comentario!