Introducción al Autoescalado Horizontal de Pods
En el despliegue de modelos de Machine Learning, la demanda rara vez es constante. Un modelo que sirve predicciones puede experimentar periodos de calma seguidos de picos masivos de solicitudes, por ejemplo, tras un lanzamiento de producto, una campaña de marketing o durante ciertas horas del día. Si tu clúster de Kubernetes está configurado con un número fijo de réplicas de tu modelo, estarás desperdiciando recursos durante los valles y, lo que es más crítico, arriesgándote a la saturación, latencia inaceptable o fallos completos durante los picos. Aquí es donde el Horizontal Pod Autoscaler (HPA) se convierte en una herramienta indispensable.
El HPA es un controlador de Kubernetes que automáticamente ajusta el número de réplicas de un Deployment, StatefulSet u otro recurso escalable, basándose en métricas de utilización observadas. Su objetivo es garantizar que tu aplicación pueda manejar la carga manteniendo un nivel de rendimiento predefinido, mientras optimiza el uso de recursos del clúster. A diferencia del escalado manual, que es reactivo y propenso a errores humanos, el HPA proporciona una respuesta dinámica, continua y automatizada a los cambios en la demanda.
Para los modelos ML, esto se traduce en una capacidad de servir más predicciones por segundo cuando los usuarios o sistemas upstream lo requieren, y de reducir la huella de recursos cuando la carga disminuye, lo que directamente impacta en la eficiencia de costos y la experiencia del usuario final. Implementar HPA requiere una comprensión clara de sus componentes: el recurso a escalar, las métricas que guiarán la decisión y los umbrales que definen cuándo actuar.
Concepto Clave: El Termostato Inteligente de Tu Aplicación
Piensa en el Horizontal Pod Autoscaler como el termostato inteligente de la infraestructura de tu modelo de Machine Learning. Un termostato común mantiene una temperatura fija, encendiendo o apagando la calefacción cuando la temperatura se desvía de un punto de consigna. El HPA funciona bajo un principio similar, pero en lugar de temperatura, monitorea métricas de rendimiento como el uso de CPU, memoria o, de manera más avanzada, métricas personalizadas como solicitudes por segundo o latencia del percentil 95.
La analogía se profundiza: el termostato inteligente (HPA) no solo reacciona, sino que puede aprender patrones y anticiparse. De manera similar, el HPA evalúa constantemente la métrica objetivo contra el valor deseado que tú defines (el target value). Si la métrica está por encima del objetivo, indica que cada pod está "sobrecargado", por lo que el HPA "enciende" más pods (aumenta réplicas) para distribuir la carga. Si la métrica está por debajo, sugiere que los pods están "infrautilizados", por lo que "apaga" algunos (disminuye réplicas) para ahorrar energía (recursos de clúster).
El objetivo final no es tener la mayor cantidad de pods posibles, sino la cantidad óptima para satisfacer la demanda con el nivel de servicio acordado. Configurar mal el HPA sería como poner el termostato a 15°C en invierno esperando ahorrar, pero haciendo que el sistema trabaje constantemente sin lograr confort. La clave está en elegir la métrica correcta y el objetivo adecuado para tu modelo específico.
Tip Crítico: No uses solo CPU/Memoria para modelos ML. Un modelo de inferencia puede estar inactivo (baja CPU) pero tener la GPU saturada, o puede tener colas de solicitudes internas. Las métricas personalizadas basadas en latencia o throughput suelen ser mejores indicadores del rendimiento real del usuario.
Cómo Funciona en la Práctica: Un Ciclo de Control Continuo
El HPA opera en un ciclo continuo de control, típicamente cada 15-30 segundos (configurable). En cada iteración, realiza tres acciones principales: Recolección, Evaluación y Escalado. Primero, consulta la API de métricas de Kubernetes (Metrics Server para recursos básicos, o un adaptador como Prometheus Adapter para métricas personalizadas) para obtener el valor actual de la métrica especificada para cada pod del despliegue objetivo.
En segundo lugar, evalúa la situación. Calcula el ratio de utilización: (valor actual de la métrica) / (valor objetivo deseado). Si el promedio de este ratio entre todos los pods supera el 100% (por ejemplo, si la CPU usada es un 150% del objetivo), significa que se necesita más capacidad. El HPA calcula entonces el nuevo número deseado de réplicas usando una fórmula: deseadoReplicas = ceil( replicasActuales * (métricaActual / métricaObjetivo) ). Este cálculo está sujeto a los límites mínimo (minReplicas) y máximo (maxReplicas) que defines.
Finalmente, si el número deseado de réplicas es diferente al actual, el HPA procede a la acción de escalado. Actualiza la especificación del recurso (por ejemplo, el campo `replicas` del Deployment). Kubernetes entonces se encarga de orquestar la creación de nuevos pods (si se escala hacia arriba) o la terminación elegante de pods existentes (si se escala hacia abajo), respetando las políticas de terminación y los checks de salud. Todo este proceso es automático y no requiere intervención humana.
Código en Acción: Implementando HPA para un Servicio de Inferencia
Vamos a desplegar un modelo de inferencia simple usando Flask y luego a configurar un HPA basado en el uso de CPU. Asumimos que ya tienes un clúster de Kubernetes con Metrics Server instalado. Primero, creamos un Dockerfile para nuestro modelo y un Deployment de Kubernetes.
1. Deployment del Modelo de ML
# deployment-modelo-ml.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-model-a
labels:
app: model-a
spec:
replicas: 3 # Número inicial de pods
selector:
matchLabels:
app: model-a
template:
metadata:
labels:
app: model-a
spec:
containers:
- name: model-server
image: tu-registro/model-a-inference:v1.2
ports:
- containerPort: 5000
resources:
requests:
cpu: "200m" # 0.2 núcleos de CPU. CRÍTICO para HPA.
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: inference-service
spec:
selector:
app: model-a
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: ClusterIP
Aplica este manifiesto con kubectl apply -f deployment-modelo-ml.yaml. Nota la sección resources.requests. El HPA utiliza estos valores como base para calcular el porcentaje de utilización. Si no defines `requests`, el HPA no podrá funcionar correctamente para métricas de recursos.
2. Creación del Horizontal Pod Autoscaler
Ahora, creamos el recurso HPA que gestionará el escalado de nuestro Deployment. Este HPA monitoreará el uso promedio de CPU y tratará de mantenerlo alrededor del 70% del valor request (200m).
# hpa-cpu-modelo-ml.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: inference-model-a-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: inference-model-a
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior: # Comportamiento avanzado (K8s 1.18+)
scaleDown:
stabilizationWindowSeconds: 300 # Espera 5 min antes de bajar, para evitar oscilaciones.
policies:
- type: Percent
value: 10
periodSeconds: 60 # No reducir más del 10% de las réplicas por minuto.
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 60 # Puede duplicar las réplicas por minuto si es necesario.
- type: Pods
value: 4
periodSeconds: 60 # O añadir 4 pods máximo por minuto (lo que ocurra primero).
Aplica el HPA: kubectl apply -f hpa-cpu-modelo-ml.yaml. Puedes verificar su estado con kubectl get hpa inference-model-a-hpa -w. La sección behavior es crucial para un escalado estable, evitando reacciones bruscas ante picos transitorios.
3. HPA con Métrica Personalizada (Prometheus Adapter)
Para un control más preciso, escalar basado en solicitudes HTTP por segundo es ideal. Esto requiere un adaptador de métricas. Aquí un ejemplo de configuración de HPA para un objetivo de 50 solicitudes por segundo por pod.
# hpa-custom-metric.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: inference-model-a-hpa-rps
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: inference-model-a
minReplicas: 2
maxReplicas: 15
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second # Nombre de la métrica expuesta por Prometheus Adapter
target:
type: AverageValue
averageValue: "50"
Esta configuración le dice al HPA que consulte la métrica personalizada http_requests_per_second y que escale el número de pods para mantener un valor promedio de 50 peticiones por segundo por pod. La instalación y configuración del Prometheus Adapter es un prerrequisito para que esto funcione.
Errores Comunes y Cómo Evitarlos
Implementar HPA sin una comprensión profunda puede llevar a comportamientos erráticos, costos elevados o, peor, a una degradación del servicio durante picos reales. Estos son los errores más frecuentes:
1. No Definir Resource Requests (o hacerlo mal): Este es el error cardinal. Si no defines spec.containers[].resources.requests en tu pod, el HPA no puede calcular el porcentaje de utilización para métricas de CPU/Memoria. El resultado será un HPA inoperante o que escala de manera incorrecta. Solución: Siempre define `requests` realistas basados en el perfil de tu modelo en carga normal.
2. Configurar Límites de Escalado Demasiado Amplios o Estrechos: Un maxReplicas demasiado bajo (ej., 5) puede no ser suficiente para un pico masivo, causando saturación. Un minReplicas demasiado alto (ej., 10) desperdicia recursos constantemente. Un maxReplicas extremadamente alto sin cuotas de recursos puede agotar todo el clúster. Solución: Basa tus límites en pruebas de carga realistas y en la capacidad de tu clúster. Usa Resource Quotas a nivel de namespace para contener el crecimiento.
3. Ignorar el Comportamiento de Escalado (behavior): Usar la configuración por defecto puede causar "flapping" (oscilación rápida de réplicas) si la métrica es volátil. El escalado hacia abajo demasiado rápido puede terminar pods que aún están procesando solicitudes. Solución: Ajusta stabilizationWindowSeconds en scaleDown para ser más conservador (300-600 segundos) y ser más agresivo en scaleUp (60 segundos).
4. Elegir la Métrica Equivocada: Escalar solo por CPU para un modelo de inferencia es un clásico error. La CPU puede estar baja mientras el modelo está bloqueado por E/S, esperando en una cola, o saturando la GPU. La latencia percibida por el usuario puede ser alta aunque la CPU esté ociosa. Solución: Implementa métricas de negocio o de aplicación, como latencia del percentil 95, tasa de solicitudes por segundo, o longitud de cola interna, usando un adaptador de métricas personalizadas.
5. Olvidar la Preparación y el Drenaje de los Pods: Cuando el HPA escala hacia abajo, Kubernetes elige pods para terminar. Si tu pod no maneja señales de terminación correctamente (SIGTERM), puede cortar solicitudes en curso. Solución: Implementa manejo de señales en tu servidor de modelo, con un período de gracia (terminationGracePeriodSeconds), y asegúrate de que tu servicio no enrute nuevas solicitudes a pods que están terminando (usando readinessProbes que fallen antes de la terminación).
Checklist de Dominio
Antes de considerar que has implementado correctamente el autoescalado horizontal para tu modelo de ML, verifica que cumples con los siguientes puntos:
- He definido resources.requests (CPU y memoria) de forma realista en la especificación de mi contenedor.
- He configurado los límites minReplicas y maxReplicas basándome en pruebas de carga y la capacidad de mi clúster.
- He seleccionado una métrica objetivo significativa para mi modelo (no solo CPU; idealmente latencia o throughput).
- He ajustado los parámetros de behavior (escalado hacia arriba/abajo) para evitar oscilaciones y responder adecuadamente a la carga.
- Mi aplicación maneja correctamente las señales de terminación y tiene configuradas readinessProbes y livenessProbes.
- He verificado que el Metrics Server (y el adaptador de métricas personalizadas, si es necesario) esté instalado y funcionando en el clúster.
- He realizado pruebas de estrés controladas para observar el comportamiento del HPA y validar que escala según lo esperado.
- He establecido monitoreo y alertas sobre el número de réplicas y la métrica objetivo para detectar comportamientos anómalos.