tutoriales.com

Gestión de Eventos en Kubernetes: Reaccionando a Cambios con Event-driven Architectures y KEDA 🚀

Este tutorial explora cómo implementar arquitecturas basadas en eventos en Kubernetes, centrándose en el uso de KEDA (Kubernetes Event-driven Autoscaling) para escalar automáticamente tus aplicaciones. Cubriremos la teoría, configuración y ejemplos prácticos para crear sistemas altamente reactivos y eficientes.

Intermedio18 min de lectura9 views
Reportar error

Introducción a las Arquitecturas Basadas en Eventos y Kubernetes 🚀

En el mundo de las microservicios y las aplicaciones nativas en la nube, la capacidad de reaccionar a eventos en tiempo real es crucial. Las arquitecturas basadas en eventos permiten desacoplar componentes, mejorar la escalabilidad y la resiliencia, y construir sistemas más dinámicos y eficientes. Kubernetes, como orquestador de contenedores líder, proporciona una base sólida para desplegar este tipo de arquitecturas, pero necesita herramientas adicionales para la gestión de eventos y el autoescalado basado en ellos.

Aquí es donde entra KEDA (Kubernetes Event-driven Autoscaling). KEDA extiende el autoescalado de Kubernetes (HPA - Horizontal Pod Autoscaler) para permitir que las aplicaciones escalen y se reduzcan a cero basándose en métricas provenientes de una amplia gama de fuentes de eventos, como colas de mensajes, bases de datos, y más.

En este tutorial, exploraremos los fundamentos de las arquitecturas basadas en eventos en Kubernetes y nos sumergiremos en cómo usar KEDA para construir sistemas reactivos que respondan dinámicamente a la demanda.

¿Por qué Arquitecturas Basadas en Eventos? 🤔

Las arquitecturas basadas en eventos ofrecen múltiples beneficios:

  • Desacoplamiento: Los componentes no necesitan conocer la existencia de otros, solo publicar o suscribirse a eventos.
  • Escalabilidad: Los componentes pueden escalar independientemente, y KEDA facilita el escalado de consumidores de eventos.
  • Resiliencia: Si un componente falla, otros pueden seguir funcionando y procesando eventos cuando el componente se recupere.
  • Flexibilidad: Facilita la evolución y el cambio de componentes sin afectar a todo el sistema.
  • Eficiencia de Costos: Con KEDA, los pods pueden reducirse a cero cuando no hay eventos, ahorrando recursos y dinero.

Comprendiendo KEDA: El Corazón del Autoescalado por Eventos ❤️

KEDA es un componente ligero y flexible que funciona junto con el HPA estándar de Kubernetes. Su función principal es monitorizar fuentes de eventos (llamadas escaladores) y alimentar esas métricas al HPA, que luego ajusta el número de réplicas de los pods de tu aplicación.

💡 Consejo: KEDA no reemplaza al HPA, sino que lo complementa. KEDA se encarga de traducir métricas externas a un formato que el HPA pueda entender.

Componentes Clave de KEDA 🛠️

KEDA consta de los siguientes componentes principales:

  1. KEDA Operator: El controlador principal que observa los recursos ScaledObject y ScaledJob.
  2. Metrics Server: Expone métricas de los escaladores a la API de métricas de Kubernetes, que el HPA consume.
  3. Scalers: Plugins que se conectan a sistemas externos (colas de mensajes, bases de datos, etc.) para obtener métricas y determinar cuándo escalar.
Fuentes de Eventos • Apache Kafka • RabbitMQ • Azure Service Bus • Redis / etc. Métricas KEDA Pod Scalers (Controlador y Métricas) Kube-API Server (Metrics API) HPA (Horizontal Pod Autoscaler) Escalado Pods de Aplicación (Deployment / StatefulSet) Consulta

Instalación de KEDA en tu Cluster Kubernetes ⚙️

Instalar KEDA es un proceso sencillo. Utilizaremos Helm para una instalación rápida y reproducible.

Requisitos Previos ✅

Antes de empezar, asegúrate de tener:

  • Un cluster Kubernetes en funcionamiento (minikube, kind, GKE, EKS, AKS, etc.).
  • kubectl configurado para conectarse a tu cluster.
  • helm instalado en tu máquina local.

Pasos de Instalación de KEDA 📖

  1. Añadir el repositorio de Helm de KEDA:
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
  1. Instalar KEDA en el namespace keda:
kubectl create namespace keda
helm install keda kedacore/keda --namespace keda
  1. Verificar la instalación:
kubectl get pods -n keda
Deberías ver los pods de KEDA `keda-operator` y `keda-admission-webhooks` ejecutándose.
NAME                                                 READY   STATUS    RESTARTS   AGE
keda-operator-76c8c7f998-abcde                       1/1     Running   0          2m
keda-admission-webhooks-67b7f94b-vwxyz               1/1     Running   0          2m
🔥 Importante: KEDA se instala en su propio namespace `keda`. Aunque tus aplicaciones estén en otros namespaces, KEDA podrá escalar sus pods.

Ejemplo Práctico: Escalado Basado en una Cola de Mensajes (RabbitMQ) 🐇

Vamos a crear una aplicación simple que consume mensajes de una cola de RabbitMQ y la escalaremos usando KEDA.

1. Desplegar RabbitMQ 📦

Para este ejemplo, usaremos un despliegue simple de RabbitMQ en el mismo cluster.

Crea un archivo rabbitmq.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rabbitmq
  labels:
    app: rabbitmq
spec:
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
      - name: rabbitmq
        image: rabbitmq:3-management
        env:
        - name: RABBITMQ_DEFAULT_USER
          value: user
        - name: RABBITMQ_DEFAULT_PASS
          value: password
        ports:
        - containerPort: 5672
        - containerPort: 15672
---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
  labels:
    app: rabbitmq
spec:
  ports:
  - port: 5672
    name: amqp
  - port: 15672
    name: management
  selector:
    app: rabbitmq
  type: ClusterIP

Despliégalo:

kubectl apply -f rabbitmq.yaml

2. Crear un Consumer de RabbitMQ (Aplicación de Ejemplo) 💻

Necesitamos una aplicación que consuma de la cola. Para simplificar, usaremos una imagen Docker preexistente que simula un consumidor de RabbitMQ.

Crea un archivo consumer.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rabbitmq-consumer
  labels:
    app: rabbitmq-consumer
spec:
  replicas: 0 # KEDA gestionará las réplicas, empezamos con 0 para demostrar el escalado desde cero
  selector:
    matchLabels:
      app: rabbitmq-consumer
  template:
    metadata:
      labels:
        app: rabbitmq-consumer
    spec:
      containers:
      - name: consumer
        image: kpenal/keda-rabbitmq-consumer:latest # Imagen de ejemplo que consume de RabbitMQ
        env:
        - name: RABBITMQ_HOST
          value: rabbitmq
        - name: RABBITMQ_PORT
          value: "5672"
        - name: RABBITMQ_USER
          value: user
        - name: RABBITMQ_PASSWORD
          value: password
        - name: RABBITMQ_QUEUE
          value: my-queue
        # Simular un trabajo que toma tiempo para que el escalado sea visible
        - name: SLEEP_SECONDS
          value: "5"

Despliégalo:

kubectl apply -f consumer.yaml

Observa que replicas está configurado a 0. KEDA se encargará de escalarlo.

kubectl get deploy rabbitmq-consumer

Deberías ver 0/0 réplicas.

3. Configurar KEDA para el Escalado 📊

Ahora, definiremos un ScaledObject para decirle a KEDA cómo escalar nuestro rabbitmq-consumer.

Crea un archivo scaledobject-rabbitmq.yaml:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: rabbitmq-consumer-scaler
  namespace: default # O el namespace donde desplegaste tu consumer
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: rabbitmq-consumer
  minReplicaCount: 0 # Escalar a cero pods cuando no hay mensajes
  maxReplicaCount: 5 # Máximo de 5 pods para el consumidor
  pollingInterval: 10 # KEDA verificará la cola cada 10 segundos
  triggers:
  - type: rabbitmq
    metadata:
      host: amqp://user:password@rabbitmq:5672/ # URL de conexión a RabbitMQ
      queueName: my-queue
      queueLength: "5" # Escalar cuando hay 5 o más mensajes en la cola por pod
      # advanced:
      #   cooldownPeriod: "300" # Tiempo en segundos antes de reducir a cero (por defecto 300s)
      #   burstTolerance: "1" # Número de pods para escalar de forma inmediata (por defecto 1)

Explicación de los campos clave:

  • scaleTargetRef: Apunta al Deployment que KEDA debe escalar (rabbitmq-consumer).
  • minReplicaCount: El número mínimo de réplicas. Establecerlo en 0 permite que KEDA reduzca la aplicación completamente cuando no hay trabajo.
  • maxReplicaCount: El número máximo de réplicas.
  • pollingInterval: Con qué frecuencia KEDA debe consultar la fuente de eventos (en segundos).
  • triggers: Define la fuente de eventos y las condiciones para el escalado.
    • type: rabbitmq: Indica que estamos usando el escalador de RabbitMQ.
    • metadata: Contiene los parámetros específicos del escalador.
      • host: La URL de conexión a tu servidor RabbitMQ.
      • queueName: El nombre de la cola que KEDA debe monitorizar.
      • queueLength: El umbral de mensajes por réplica. Si la longitud de la cola dividida por el número de réplicas actuales es igual o mayor a este valor, KEDA indicará al HPA que escale.

Despliégalo:

kubectl apply -f scaledobject-rabbitmq.yaml

Ahora, KEDA ha creado un Horizontal Pod Autoscaler por ti. Puedes verificarlo:

kubectl get hpa

Deberías ver algo como:

NAME                       REFERENCE                      TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
keda-hpa-rabbitmq-consumer-scaler   Deployment/rabbitmq-consumer   0/5       0         5         0          10s

Observa que REPLICAS es 0 porque no hay mensajes en la cola.

4. Generar Eventos y Observar el Escalado 📈

Ahora vamos a enviar mensajes a la cola de RabbitMQ para ver cómo KEDA reacciona.

Vamos a ejecutar un pod temporal para enviar mensajes:

kubectl run -it --rm rabbitmq-producer --image=rabbitmq --restart=Never -- rabbitmqadmin -H rabbitmq -u user -p password publish routing_key=my-queue payload="Hello from KEDA!"

Ejecuta este comando varias veces para añadir mensajes a la cola. Observa lo que ocurre con tu rabbitmq-consumer Deployment:

kubectl get deploy rabbitmq-consumer -w # -w para watch

Verás cómo las réplicas del rabbitmq-consumer aumentan desde 0, procesan los mensajes, y luego, después de un tiempo de inactividad (cooldownPeriod, por defecto 300 segundos), se reducen de nuevo a 0.

También puedes ver el estado del ScaledObject:

kubectl describe scaledobject rabbitmq-consumer-scaler

Esto te mostrará el estado actual, el número de réplicas deseadas por el HPA, y las métricas obtenidas por KEDA.

📌 Nota: El valor de `queueLength` en el `ScaledObject` es crucial. Si lo pones demasiado bajo, los pods escalarán más agresivamente. Si lo pones demasiado alto, puede haber latencia en el procesamiento de eventos.

KEDA con ScaledJob: Procesamiento de Tareas Asíncronas ⏱️

Además de ScaledObject para Deployments y StatefulSets, KEDA también ofrece ScaledJob para Jobs de Kubernetes. Esto es ideal para escenarios donde necesitas ejecutar una tarea única en respuesta a un evento, y luego terminar.

Un ScaledJob crea un Job de Kubernetes cada vez que hay eventos en la cola, y una vez que el Job se completa, el Pod asociado se termina.

Ejemplo de ScaledJob con RabbitMQ 💡

Imaginemos que tenemos una cola donde se envían solicitudes de procesamiento de imágenes. Cada solicitud es un evento que debe ser manejado por un Job separado.

  1. Definir el Job para el Procesamiento de Imágenes (Ejemplo): Crea image-processor-job.yaml:
apiVersion: batch/v1
kind: Job
metadata:
name: image-processor-template
labels:
app: image-processor
spec:
template:
metadata:
labels:
app: image-processor
spec:
containers:
- name: processor
image: busybox
command: ["sh", "-c", "echo 'Processing image from queue...'; sleep 10; echo 'Image processed.'"]
env:
- name: RABBITMQ_HOST
value: rabbitmq
- name: RABBITMQ_USER
value: user
- name: RABBITMQ_PASSWORD
value: password
- name: RABBITMQ_QUEUE
value: image-queue
restartPolicy: Never
backoffLimit: 4
**Importante:** Este `Job` es solo una plantilla. No lo despliegues directamente. KEDA lo usará.

2. Configurar ScaledJob para el Procesador de Imágenes: Crea scaledjob-image-processor.yaml:

apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: rabbitmq-image-processor-job
namespace: default
spec:
jobTargetRef:
template: # Aquí se referencia la plantilla de Job
spec:
containers:
- name: processor
image: busybox
command: ["sh", "-c", "echo 'Processing image from queue...'; sleep 10; echo 'Image processed.'"]
env:
- name: RABBITMQ_HOST
value: rabbitmq
- name: RABBITMQ_USER
value: user
- name: RABBITMQ_PASSWORD
value: password
- name: RABBITMQ_QUEUE
value: image-queue
restartPolicy: Never
backoffLimit: 4
pollingInterval: 10
maxReplicaCount: 10 # Máximo de 10 Jobs concurrentes
triggers:
- type: rabbitmq
metadata:
host: amqp://user:password@rabbitmq:5672/
queueName: image-queue
queueLength: "1" # Crear un Job por cada mensaje en la cola
En un `ScaledJob`, en lugar de `scaleTargetRef`, usamos `jobTargetRef` y definimos directamente la plantilla del `Job` (o referenciamos una existente si está bien definida).

Despliégalo:
kubectl apply -f scaledjob-image-processor.yaml
  1. Generar Mensajes para el ScaledJob: Envía mensajes a la cola image-queue:
kubectl run -it --rm rabbitmq-image-producer --image=rabbitmq --restart=Never -- rabbitmqadmin -H rabbitmq -u user -p password publish routing_key=image-queue payload="Process image 123"
kubectl run -it --rm rabbitmq-image-producer-2 --image=rabbitmq --restart=Never -- rabbitmqadmin -H rabbitmq -u user -p password publish routing_key=image-queue payload="Process image 456"
Observa los `Jobs` creados:
kubectl get jobs -w
Verás cómo KEDA crea `Jobs` para cada mensaje en la cola `image-queue`. Una vez que el `Job` se completa, el `Pod` asociado terminará.
⚠️ Advertencia: Asegúrate de que el `Job` de tu `ScaledJob` consuma el evento y se complete. Si el `Job` se queda eternamente en ejecución o falla, KEDA podría seguir lanzando nuevos `Jobs` si la cola no se vacía.

Escaladores Comunes y Consideraciones Avanzadas ✨

KEDA soporta una gran variedad de escaladores. Aquí hay algunos de los más comunes:

Lista de Escaladores Populares de KEDA
  • Colas de Mensajes: rabbitmq, kafka, azure-servicebus, aws-sqs, gcp-pubsub.
  • Bases de Datos: postgresql, mysql, mssql (basado en el número de filas o resultados de consultas).
  • Métricas Personalizadas: prometheus, datadog, azure-monitor.
  • Almacenamiento en la Nube: azure-blob, aws-s3 (basado en el número de blobs/objetos).
  • HTTP: http (escalar basado en la carga de solicitudes HTTP).

Consideraciones Avanzadas para la Producción 🌟

  • Autenticación Segura: En entornos de producción, evita credenciales en texto plano. Usa Secrets de Kubernetes para host y credenciales. Puedes referenciar Secrets en la sección metadata del ScaledObject usando valueFrom.
# ... dentro de metadata del trigger
hostFromEnv: RABBITMQ_HOST_SECRET_KEY
# ... o para el host completo
# host: amqp://$(RABBITMQ_USER):$(RABBITMQ_PASSWORD)@rabbitmq:5672/
# triggerAuthenticationRef:
#   name: keda-trigger-auth-rabbitmq
Y luego definir un `TriggerAuthentication`:
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: keda-trigger-auth-rabbitmq
namespace: default
spec:
secretTargetRef:
- parameter: host
name: rabbitmq-secrets # Nombre del Secret de K8s
key: host # Clave dentro del Secret que contiene la URL de RabbitMQ
Y tu `rabbitmq-secrets`:
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-secrets
type: Opaque
data:
host: YW1xcDovL3VzZXI6cGFzc3dvcmRAcmFiYml0bXE6NTY3Mi8= # Base64 de amqp://user:password@rabbitmq:5672/
  • cooldownPeriod: Este parámetro define cuánto tiempo KEDA esperará sin eventos antes de reducir a cero las réplicas. Por defecto es 300 segundos (5 minutos). Ajustarlo es importante para evitar escalados y desescalados excesivos (flapping).
  • fallback: Para evitar un escalado a cero inesperado si el escalador pierde la conexión o falla, puedes definir un fallback para mantener un número mínimo de réplicas.
spec:
# ...
triggers:
- type: rabbitmq
# ...
fallback:
failureThreshold: 3 # Si el escalador falla 3 veces, usa el valor de replicaCount
replicaCount: 1     # Mantener al menos 1 réplica si el escalador falla
  • Escalado Basado en Múltiples Triggers: Puedes combinar varios triggers en un único ScaledObject. El HPA escalará basándose en el trigger que sugiera el mayor número de réplicas.

Monitoreo de KEDA 📊

Es fundamental monitorizar KEDA para entender cómo está escalando tus aplicaciones y detectar posibles problemas. KEDA expone métricas Prometheus que puedes integrar con tu sistema de monitorización (por ejemplo, Grafana).

Algunas métricas clave a observar incluyen:

  • keda_scaled_object_errors_total: Errores al obtener métricas de los escaladores.
  • keda_scaled_object_status: Estado actual de los ScaledObject.
  • keda_scaler_metrics_value: El valor de la métrica que KEDA está leyendo de la fuente de eventos.

Puedes configurar ServiceMonitor si usas Prometheus Operator para recoger estas métricas.

KEDA Operator Metrics Server Prometheus Grafana Usuario Métricas Data

Conclusión 🎉

Las arquitecturas basadas en eventos, potenciadas por KEDA en Kubernetes, abren un abanico de posibilidades para construir aplicaciones altamente reactivas, eficientes y escalables. Al desacoplar la lógica de escalado de la lógica de negocio y basarla en eventos del mundo real, puedes optimizar el uso de recursos y mejorar la capacidad de respuesta de tus sistemas.

Has aprendido a instalar KEDA, a configurar ScaledObject y ScaledJob para el autoescalado basado en colas de mensajes, y a considerar aspectos importantes para entornos de producción. ¡Ahora estás listo para implementar patrones de arquitectura basados en eventos en tus propios despliegues de Kubernetes!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!