El Horizontal Pod Autoscaler (HPA) ajusta automáticamente el número de réplicas de un Deployment o StatefulSet según el uso de recursos de sus pods: añade réplicas cuando la carga sube y las retira cuando baja. En este tutorial instalarás metrics-server, desplegarás una aplicación de ejemplo, crearás un HPA con la API autoscaling/v2 que escala por CPU y memoria, ajustarás su comportamiento para evitar oscilaciones y lo verás actuar con una prueba de carga.
Requisitos previos
Para seguir esta guía necesitas:
- Un clúster de Kubernetes 1.29 o superior con al menos dos nodos worker, por ejemplo desplegado con kubeadm sobre VPS de CubePath.
kubectlconfigurado contra el clúster con permisos de administrador.- Espacio libre en el clúster para unas 10 réplicas de un pod que pide 200m de CPU (unos 2 vCPU en total).
Cómo decide el HPA el número de réplicas
El controlador del HPA consulta las métricas cada 15 segundos y calcula las réplicas deseadas con esta fórmula:
réplicas deseadas = ceil(réplicas actuales × valor actual / valor objetivo)
Por ejemplo, con 2 réplicas al 100 % de CPU y un objetivo del 50 %, el HPA pide ceil(2 × 100 / 50) = 4 réplicas. El porcentaje se calcula sobre las requests del contenedor, no sobre sus limits, así que un pod sin requests de CPU no se puede escalar por CPU. Si la diferencia con el objetivo es menor del 10 %, el HPA no hace nada, para evitar cambios constantes.
Paso 1: Instalar metrics-server
El HPA obtiene el uso de CPU y memoria de la API metrics.k8s.io, que publica metrics-server a partir de los datos del kubelet de cada nodo. Comprueba si ya lo tienes (algunas distribuciones como k3s lo incluyen):
kubectl get deployment metrics-server -n kube-system
Si no existe, instala la última versión publicada por el proyecto:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Espera un minuto y comprueba que devuelve datos:
kubectl top nodes
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
master-1 182m 9% 1320Mi 35%
worker-1 95m 4% 980Mi 26%
worker-2 88m 4% 1012Mi 27%
Si en su lugar ves error: Metrics API not available y el pod de metrics-server no llega a Ready, revisa sus logs:
kubectl logs -n kube-system deploy/metrics-server
En clústeres kubeadm es habitual el error x509: cannot validate certificate, porque los kubelets usan certificados autofirmados. La solución correcta es que los kubelets pidan certificados firmados por la CA del clúster (serverTLSBootstrap: true en la configuración del kubelet y aprobar los CSR). En un clúster de pruebas puedes desactivar la verificación añadiendo el argumento --kubelet-insecure-tls:
kubectl patch deployment metrics-server -n kube-system --type=json \
-p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
Paso 2: Desplegar una aplicación de ejemplo
Usarás la imagen hpa-example del proyecto Kubernetes, un servidor PHP que realiza un cálculo costoso en cada petición y así consume CPU con facilidad. Crea el manifiesto:
nano php-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
replicas: 1
selector:
matchLabels:
app: php-apache
template:
metadata:
labels:
app: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
requests:
cpu: 200m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
spec:
selector:
app: php-apache
ports:
- port: 80
Las requests son imprescindibles: el objetivo del 50 % de CPU que usarás después significa 100m por pod. Aplica el manifiesto:
kubectl apply -f php-apache.yaml
kubectl rollout status deployment/php-apache
deployment "php-apache" successfully rolled out
Paso 3: Crear el HPA
Define el HPA con la API autoscaling/v2, que permite combinar varias métricas y configurar el comportamiento del escalado:
nano php-apache-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Con varias métricas, el HPA calcula las réplicas necesarias para cada una y aplica la mayor. Aplícalo y consulta su estado:
kubectl apply -f php-apache-hpa.yaml
kubectl get hpa php-apache
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 1%/50%, memory: 14%/80% 1 10 1 30s
Si en TARGETS aparece <unknown>, espera un minuto: el HPA necesita un par de ciclos para leer las primeras métricas.
Notaescalar por memoria solo tiene sentido si la aplicación libera memoria cuando baja la carga. Muchos runtimes (JVM, Node.js) no la devuelven, y el HPA nunca reduciría réplicas. En ese caso escala solo por CPU.
Paso 4: Generar carga y ver el escalado
Abre una segunda terminal y lanza un pod que hace peticiones continuas al servicio:
kubectl run load-generator --rm -it --image=busybox:1.36 --restart=Never -- \
/bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
En la primera terminal, observa el HPA:
kubectl get hpa php-apache --watch
En uno o dos minutos la CPU supera el objetivo y las réplicas aumentan:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 1%/50%, memory: 14%/80% 1 10 1 3m
php-apache Deployment/php-apache cpu: 248%/50%, memory: 15%/80% 1 10 1 4m
php-apache Deployment/php-apache cpu: 248%/50%, memory: 15%/80% 1 10 4 4m
php-apache Deployment/php-apache cpu: 71%/50%, memory: 16%/80% 1 10 6 5m
Detén el generador de carga con Ctrl+C en la segunda terminal. El HPA no reduce réplicas de inmediato: por defecto espera una ventana de estabilización de 5 minutos para no deshacer el escalado ante una bajada puntual. Pasado ese tiempo volverá a 1 réplica.
Para ver las decisiones que ha tomado el HPA, revisa sus eventos:
kubectl describe hpa php-apache
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 4m horizontal-pod-autoscaler New size: 4; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 3m horizontal-pod-autoscaler New size: 6; reason: cpu resource utilization (percentage of request) above target
Paso 5: Ajustar el comportamiento del escalado
El campo behavior controla la velocidad a la que el HPA añade y retira réplicas. Una configuración habitual para servicios web es escalar hacia arriba rápido y hacia abajo despacio. Edita el HPA:
nano php-apache-hpa.yaml
Añade el bloque behavior dentro de spec, al mismo nivel que metrics:
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
selectPolicy: Min
Qué significa cada parte:
| Campo | Efecto |
|---|---|
scaleUp.policies | Cada 15 segundos puede duplicar las réplicas o añadir 4, lo que sea mayor (selectPolicy: Max). |
scaleUp.stabilizationWindowSeconds: 0 | Escala hacia arriba en cuanto la métrica lo pide. |
scaleDown.stabilizationWindowSeconds: 300 | Usa la recomendación más alta de los últimos 5 minutos antes de reducir. |
scaleDown.policies | Retira como máximo el 50 % de las réplicas por minuto. |
Aplica el cambio y comprueba que el HPA lo ha aceptado:
kubectl apply -f php-apache-hpa.yaml
kubectl get hpa php-apache -o jsonpath='{.spec.behavior.scaleDown}{"\n"}'
{"policies":[{"periodSeconds":60,"type":"Percent","value":50}],"selectPolicy":"Min","stabilizationWindowSeconds":300}
Repite la prueba de carga del paso 4 para ver cómo ahora las réplicas suben más deprisa y bajan de forma escalonada.
HPA y Vertical Pod Autoscaler
El Vertical Pod Autoscaler (VPA) no añade réplicas: cambia las requests de CPU y memoria de cada pod según su consumo real. No se instala con Kubernetes; es un proyecto aparte del repositorio kubernetes/autoscaler. No combines HPA y VPA sobre la misma métrica (por ejemplo, ambos sobre CPU), porque el VPA modifica las requests que el HPA usa como referencia y los dos se contradicen. Un uso seguro del VPA es el modo Off, que solo recomienda valores de requests para que los ajustes tú.
Solución de problemas
TARGETSmuestra<unknown>de forma permanente. Ejecutakubectl describe hpa php-apache. El eventoFailedGetResourceMetricindica que metrics-server no funciona (vuelve al paso 1) o que algún contenedor del pod no declararequestspara esa métrica, incluidos los sidecars.- Las réplicas no pasan de
maxReplicas. Es el límite configurado. Si los pods nuevos se quedan enPending, el clúster no tiene capacidad: revisakubectl describe pody añade nodos. - Las réplicas no bajan. Recuerda la ventana de estabilización de 5 minutos. Si escalas por memoria, comprueba con
kubectl top podssi la aplicación libera memoria realmente.
Conclusión
Has instalado metrics-server, creado un HPA que escala por CPU y memoria y ajustado su comportamiento para reaccionar rápido ante picos sin oscilar al bajar. La clave está en definir requests realistas, porque todo el cálculo se basa en ellas. Como siguientes pasos, puedes escalar por métricas de la aplicación (peticiones por segundo, longitud de colas) con Prometheus y prometheus-adapter, probar KEDA para escalar según eventos externos, o añadir Cluster Autoscaler para que el clúster cree nodos cuando los pods nuevos no caben.
