En los últimos artículos que he escrito sobre Kubernetes me estoy centrando en todo lo que tiene que ver con la monitorización, tanto con Prometheus como con Azure Monitor por ahora, y el autoescalado. Hoy quiero hablarte de KEDA, otra forma de autoescalar tus aplicaciones en base a eventos externos.
¿Qué es KEDA?
KEDA son las siglas de Kubernetes-based Event Driven Autoscaling. Se trata de un componente que puedes desplegar en tu clúster de Kubernetes, esté donde esté, y que te ayuda a autoescalar tus aplicaciones en base a diferentes eventos, utilizando lo que llama scalers. Estos pueden ser mensajes en colas de mensajería de diferentes plataformas (Apache Kafka, AWS SQS Queue, RabbitMQ Queue, Azure Event Hubs, etc), bases de datos (MySQL, PostgreSQL) o incluso servidores de métricas como Prometheus, AWS Cloudwatch o Azure Monitor. La lista va creciendo cada vez más y más.
Una vez decidido cuál será el evento que hará escalar tu aplicación, se crea un recurso llamado ScaledObject con el scaler oportuno y KEDA, por debajo, crea un HorizontalPodAutoscaler por nosotros y lo asocia a dicho recurso para saber cuándo tiene que subir o bajar el número de réplicas de nuestro Deployment o Job.

Instalar KEDA en tu clúster
Antes de comenzar a utilizar KEDA necesitas desplegarlo en tu Kubernetes. La forma más sencilla es a través de Helm:
#Install helm 3 if you don't have it
brew install helm
#Add KEDA repo to Helm
helm repo add kedacore https://kedacore.github.io/charts
#Update Helm repo
helm repo update
#My cluster info
RESOURCE_GROUP="KEDA"
AKS_NAME="aks-keda"
#Get AKS credentials
az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_NAME
#Create a namespace called keda
kubectl create namespace keda
#Install keda components inside the namespace
helm install keda kedacore/keda --namespace keda
#See pods
kubectl get pods --namespace keda
En este ejemplo estoy utilizando Helm 3 en Mac. Si no lo tienes instalado puedes hacerlo a través de Homebrew con la primera línea del código anterior. Añade el repo de KEDA y asigna a kubectl las credenciales del clúster donde quieres desplegarlo. Crea un namespace llamado keda y en él instala los componentes a través del chart kedacore/keda. Si todo ha salido bien deberías de ver dos pods dentro de ese namespace:
➜ kubectl get pods -n keda
NAME READY STATUS RESTARTS AGE
keda-operator-5895ff46b9-jbxbn 1/1 Running 0 20s
keda-operator-metrics-apiserver-6774776dbc-4nnhs 1/1 Running 0 20s
Desplegar una aplicación de ejemplo
Ahora que ya tienes KEDA desplegado lo siguiente que necesitas es una aplicación que autoescalar. Da igual lo que sea, ya que vamos a hacerla subir o bajar en réplicas en base a eventos ajenos a ella, en este caso. Para este ejemplo voy a utilizar el siguiente despliegue:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
labels:
app: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- name: http
containerPort: 80
Como puedes ver es un Nginx básico con una sola réplica. Al desplegarlo deberías de ver algo como lo siguiente:
➜ kubectl apply -f web-deployment.yaml
deployment.apps/web-deployment created
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
web-deployment-7bfddfc4f-9kxjn 1/1 Running 0 6s
Lo último que te queda por definir es en base a qué escalamos esta aplicación.
Crear un ScaledObject para tu despliegue
Como te decía al principio de este artículo, existen diferentes scalers sobre los que puedes basar el autoescalado. Para este ejemplo voy a utilizar el tipo Azure Storage Queue. Por ello, antes que nada, necesito crear una cola de mensajería en Azure Storage, que llamaré tasks.
#Create a storage account on Azure
STORAGE_NAME="boxoftasks"
az storage account create --name $STORAGE_NAME --resource-group $RESOURCE_GROUP
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP --account-name $STORAGE_NAME --query "[0].value" --output tsv)
#Create a queue
az storage queue create --name "tasks" \
--account-name $STORAGE_NAME \
--account-key $ACCOUNT_KEY
#Get storage connection string
CONNECTION_STRING=$(az storage account show-connection-string --name $STORAGE_NAME --resource-group $RESOURCE_GROUP -o tsv)
#Create secret with the storage connection string
kubectl create secret generic storage-secret --from-literal=connection-string=$CONNECTION_STRING
Además, como puedes ver en el código anterior, lo último que hago es crear un secreto que guardará la cadena de conexión de la cuenta de almacenamiento donde está mi cola. Por último, creo el recurso ScaledObject que indicara los umbrales asociados a la misma:
apiVersion: keda.k8s.io/v1alpha1
kind: TriggerAuthentication
metadata:
name: trigger-auth-queue
spec:
secretTargetRef:
- parameter: connection
name: storage-secret
key: connection-string
---
apiVersion: keda.k8s.io/v1alpha1
kind: ScaledObject
metadata:
name: queue-scaledobject
labels:
deploymentName: web-deployment
spec:
scaleTargetRef:
deploymentName: web-deployment
pollingInterval: 15
coolDownPeriod: 30
minReplicaCount: 0
maxReplicaCount: 10
triggers:
- type: azure-queue
metadata:
queueName: tasks
queueLength: '5' # Optional. Queue length target for HPA. Default: 5 messages
authenticationRef:
name: trigger-auth-queue
Para poder asociar la cadena de conexión al ScaledObject necesito hacer uso de otro recurso llamado TriggerAuthentication, al que a su vez asocio al secreto creado anteriormente, aunque existen otras formas de autenticación.
Una vez que apliques los cambios ocurrirán dos cosas: por un lado se creará un recurso del tipo ScaledObject con el trigger azure-queue:
➜ kubectl get scaledobject
NAME DEPLOYMENT TRIGGERS AGE
queue-scaledobject web-deployment azure-queue 59s
Y por otro un HPA asociado a dicho objecto:
➜ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-web-deployment Deployment/web-deployment <unknown>/5 (avg) 1 10 0 79s
Probar el autoescalado con KEDA
Para comprobar que todo funciona correctamente, con Azure CLI puedes generar varios mensajes de una forma sencilla:
#Generate messages
while true; do az storage message put --content "Hello from KEDA" --queue-name "tasks" --account-name $STORAGE_NAME --account-key $STORAGE_KEY; done
Si vigilas el HPA asociado comprobarás que poco a poco va subiendo en replicas:
➜ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-web-deployment Deployment/web-deployment <unknown>/5 (avg) 1 10 0 23m
keda-hpa-web-deployment Deployment/web-deployment 4/5 (avg) 1 10 1 23m
keda-hpa-web-deployment Deployment/web-deployment 13/5 (avg) 1 10 1 23m
keda-hpa-web-deployment Deployment/web-deployment 7334m/5 (avg) 1 10 3 24m
keda-hpa-web-deployment Deployment/web-deployment 5800m/5 (avg) 1 10 5 24m
keda-hpa-web-deployment Deployment/web-deployment 6334m/5 (avg) 1 10 6 24m
keda-hpa-web-deployment Deployment/web-deployment 5750m/5 (avg) 1 10 8 24m
keda-hpa-web-deployment Deployment/web-deployment 5600m/5 (avg) 1 10 10 25m
keda-hpa-web-deployment Deployment/web-deployment 6200m/5 (avg) 1 10 10 25m
Para comprobar el efecto contrario, elimina los mensajes de la cola con el comando clear:
#Clean queue
az storage message clear --queue-name "tasks" --account-name $STORAGE_NAME --account-key $STORAGE_KEY
Verás que a los pocos minutos volverás a tener cero réplicas de tu aplicación:
➜ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-web-deployment Deployment/web-deployment <unknown>/5 (avg) 1 10 0 23m
keda-hpa-web-deployment Deployment/web-deployment 4/5 (avg) 1 10 1 23m
keda-hpa-web-deployment Deployment/web-deployment 13/5 (avg) 1 10 1 23m
keda-hpa-web-deployment Deployment/web-deployment 7334m/5 (avg) 1 10 3 24m
keda-hpa-web-deployment Deployment/web-deployment 5800m/5 (avg) 1 10 5 24m
keda-hpa-web-deployment Deployment/web-deployment 6334m/5 (avg) 1 10 6 24m
keda-hpa-web-deployment Deployment/web-deployment 5750m/5 (avg) 1 10 8 24m
keda-hpa-web-deployment Deployment/web-deployment 5600m/5 (avg) 1 10 10 25m
keda-hpa-web-deployment Deployment/web-deployment 6200m/5 (avg) 1 10 10 25m
keda-hpa-web-deployment Deployment/web-deployment 6900m/5 (avg) 1 10 10 25m
keda-hpa-web-deployment Deployment/web-deployment 7/5 (avg) 1 10 10 25m
keda-hpa-web-deployment Deployment/web-deployment 0/5 (avg) 1 10 10 26m
keda-hpa-web-deployment Deployment/web-deployment 0/5 (avg) 1 10 10 31m
keda-hpa-web-deployment Deployment/web-deployment <unknown>/5 (avg) 1 10 0 31m
¡Saludos!

Bootcamp DevOps
Si tienes ganas de meterte en el área de DevOps, formo parte del
equipo de docentes del Bootcamp DevOps Lemoncode, ¿Te animas a
aprender con nosotros?