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.requestsdefinidos (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.

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:
- Lee métricas del Metrics Server (CPU, memoria) o de adaptadores de métricas custom.
- Calcula cuántas réplicas necesita usando esta fórmula:
réplicas deseadas = ⌈ réplicas actuales × (métrica actual / métrica objetivo) ⌉- Escala el Deployment si la diferencia supera la tolerancia (10% por defecto).
- 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éplicasEl HPA escalaría de 2 a 4 Pods.

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.yamlEn 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=90sVerifica que funciona:
kubectl top nodesNAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
mi-cluster-control-plane 250m 12% 512Mi 26%
mi-cluster-worker 120m 6% 256Mi 13%kubectl top podsSi 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.yamlClave: 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=50Verifica:
kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
mi-app Deployment/mi-app 10%/50% 2 10 2 30sCreando 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: 50kubectl apply -f hpa-cpu.yamlDesglosemos:
| Campo | Qué hace |
|---|---|
scaleTargetRef | A qué Deployment apuntar |
minReplicas: 2 | Nunca bajar de 2 réplicas (alta disponibilidad) |
maxReplicas: 10 | Nunca pasar de 10 (control de costos) |
averageUtilization: 50 | Escalar cuando el promedio de CPU supere 50% de los requests |
Importante: Usamos
autoscaling/v2— la versión estable y actual. Las versionesv2beta1yv2beta2ya 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: 70Cuidado 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: 70Si 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
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: MaxDesglosemos:
Scale-up:
| Campo | Qué hace |
|---|---|
stabilizationWindowSeconds: 30 | Espera 30s antes de escalar (evita reacciones a picos instantáneos) |
Percent: 50 | Puede aumentar hasta 50% de las réplicas actuales por minuto |
Pods: 4 | O agregar hasta 4 Pods por minuto (el que sea mayor) |
selectPolicy: Max | Elige la política más agresiva (la que agregue más Pods) |
Scale-down:
| Campo | Qué hace |
|---|---|
stabilizationWindowSeconds: 300 | Espera 5 minutos antes de reducir (evita el “efecto yoyo”) |
Percent: 25 | Reduce máximo 25% cada 2 minutos |
selectPolicy: Max | Elige 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 reducirEsto 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 --watchNAME 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 7mVerá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
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 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?
| Escenario | Usa |
|---|---|
| App stateless con tráfico variable | HPA |
| App con carga estable pero recursos mal dimensionados | VPA |
| Bases de datos, caches (difíciles de escalar horizontalmente) | VPA |
| Apps con picos de tráfico impredecibles | HPA (o ambos) |
Modos del VPA
El VPA no es parte del core de Kubernetes — necesitas instalarlo por separado desde el repositorio oficial.
| Modo | Comportamiento |
|---|---|
| Off | Solo recomienda, no aplica cambios (ideal para empezar) |
| Initial | Aplica recursos solo cuando el Pod se crea |
| Recreate | Elimina y recrea el Pod con los nuevos recursos |
| InPlaceOrRecreate | Intenta 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-vpaRecommendation:
Container Recommendations:
Container Name: mi-app
Lower Bound:
Cpu: 25m
Memory: 50Mi
Target:
Cpu: 50m
Memory: 80Mi
Upper Bound:
Cpu: 200m
Memory: 256MiEsto 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í.
- Sí funciona bien: HPA por CPU/métricas custom + VPA por memoria (en modo
InitialoOff). - La recomendación más segura: usa VPA en modo
Offpara 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-app5. 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
- Horizontal Pod Autoscaling — Guía completa del HPA
- HPA Walkthrough — Tutorial paso a paso
- autoscaling/v2 API — Referencia del API
- Vertical Pod Autoscaler — Repositorio oficial del VPA
- Metrics Server — Instalación y configuración
- Prometheus Adapter — Métricas custom para HPA
- In-Place Pod Resource Resize — Redimensionar Pods sin reiniciarlos (GA en v1.35)
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
behaviorte 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!