KEDA: автоматическое масштабирование в Kubernetes

от автора

Привет, друзья-разработчики и Kubernetes-энтузиасты! Сегодня мы с вами погрузимся в мир KEDA (Kubernetes-based Event Driven Autoscaling) — инструмента, который позволит вашим приложениям масштабироваться как по волшебству.

KEDA позволяет Kubernetes автоматически масштабировать приложения на основе различных внешних событий: сообщений в очередях, метрик из Prometheus, вебхуков и многого другого. Мастхев для некоторых микросервисов.

Самый простой способ установить KEDA — применить официальный манифест:

kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.10.0/keda-2.10.0.yaml

Проверьте статус подов KEDA:

kubectl get pods -n keda

Вы должны увидеть что-то вроде этого:

NAME                                     READY   STATUS    RESTARTS   AGE keda-operator-7d9f9f8d9b-abcde          1/1     Running   0          2m keda-operator-metrics-apiserver-xyz123   1/1     Running   0          2m

Если все поды в статусе Running, значит, установка прошла успешно. Если нет, загляните в логи:

kubectl logs <pod-name> -n keda

Немного расскажу про сами компоненты KEDA:

ScaledObject — это центральный объект конфигурации KEDA. Он связывает приложение (например, Deployment или StatefulSet) с источниками событий, определяя условия масштабирования. В ScaledObject задаются минимальное и максимальное количество реплик, а также параметры триггеров, по которым будет происходить масштабирование.

Trigger — это источник событий или метрик, на основе которых KEDA принимает решение о масштабировании. Это может быть очередь сообщений, метрика из Prometheus или другой источник. Каждый триггер имеет свои параметры и пороги, при превышении которых инициируется масштабирование.

Scaler — компонент, отвечающий за сбор и анализ метрик из триггера. Он регулярно опрашивает источник событий, сравнивает полученные данные с заданными порогами и сообщает оператору о необходимости изменения количества реплик. Каждый тип триггера имеет свой собственный Scaler, оптимизированный для работы с конкретным источником данных.

TriggerAuthentication — объект, обеспечивающий безопасное хранение и доступ к данным подключения к внешним системам. Он содержит конфиденциальную информацию, такую как имена пользователей и пароли, необходимые для взаимодействия с триггерами. TriggerAuthentication гарантирует, что KEDA может безопасно получать доступ к ресурсам.

KEDA Operator — основной компонент, управляющий жизненным циклом ScaledObject и TriggerAuthentication. Он следит за изменениями в этих объектах, инициализирует соответствующие Scaler и взаимодействует с Kubernetes API для масштабирования приложений.

Metrics Server — агрегатор метрик внутри Kubernetes, который собирает данные о ресурсах, используемых подами и узлами. KEDA использует эти метрики для принятия решений о масштабировании, дополняя информацию от внешних триггеров.

Масштабирование п очереди RabbitMQ

Рассмотрим пошаговый пример настройки KEDA для масштабирования приложения на основе количества сообщений в очереди RabbitMQ.

Создадим простой Deployment для приложения, которое будет обрабатывать задачи из очереди RabbitMQ. Предположим, есть Docker-образ myregistry/task-processor:latest:

# deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:   name: task-processor   labels:     app: task-processor spec:   replicas: 1   selector:     matchLabels:       app: task-processor   template:     metadata:       labels:         app: task-processor     spec:       containers:       - name: processor         image: myregistry/task-processor:latest         ports:         - containerPort: 8080         env:         - name: RABBITMQ_HOST           value: "my-rabbitmq"         - name: RABBITMQ_QUEUE           value: "tasks"

Применим манифест:

kubectl apply -f deployment.yaml

Для безопасного хранения данных подключения создадим Kubernetes Secret.

# secret.yaml apiVersion: v1 kind: Secret metadata:   name: rabbitmq-secret type: Opaque data:   host: bXktcmFiYmF0bXFoYXN0Cg== # закодировано в base64, например, echo -n 'my-rabbitmq' | base64   username: YWRtaW4=               # admin   password: cGFzc3dvcmQ=           # password

Применим секрет:

kubectl apply -f secret.yaml

Создадим объект TriggerAuthentication, который будет использоваться для безопасного подключения к RabbitMQ.

# triggerauth.yaml apiVersion: keda.sh/v1alpha1 kind: TriggerAuthentication metadata:   name: rabbitmq-auth   namespace: default spec:   secretTargetRef:   - parameter: host     name: rabbitmq-secret     key: host   - parameter: username     name: rabbitmq-secret     key: username   - parameter: password     name: rabbitmq-secret     key: password

Применим манифест:

kubectl apply -f triggerauth.yaml

Теперь создадим ScaledObject, который будет отслеживать очередь RabbitMQ и масштабировать наше приложение в зависимости от количества сообщений.

# scaledobject.yaml apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata:   name: task-processor-scaledobject   namespace: default spec:   scaleTargetRef:     kind: Deployment     name: task-processor   minReplicaCount: 1   maxReplicaCount: 10   cooldownPeriod: 300  # Время в секундах перед уменьшением числа реплик   pollingInterval: 30  # Интервал проверки метрик   triggers:   - type: rabbitmq     metadata:       queueName: tasks       queueLength: "5"  # Порог для масштабирования     authenticationRef:       name: rabbitmq-auth

Применим манифест:

kubectl apply -f scaledobject.yaml

Убедимся, что все работает как надо. Напишем простой скрипт на Питоне для добавления задач в очередь и посмотрим, как KEDA реагирует на рост нагрузки.

# send_tasks.py import pika import time  def main():     connection = pika.BlockingConnection(pika.ConnectionParameters('my-rabbitmq'))     channel = connection.channel()      channel.queue_declare(queue='tasks')      for i in range(20):         message = f'Task {i}'         channel.basic_publish(exchange='', routing_key='tasks', body=message)         print(f" [x] Sent {message}")         time.sleep(1)  # Пауза, чтобы видеть постепенное масштабирование      connection.close()  if __name__ == "__main__":     main()

Запускаем скрипт:

python send_tasks.py 

Открываем другой терминал и выполняем команду:

kubectl get pods -w -l app=task-processor

Можно увидеть, как количество реплик вашего приложения увеличивается по мере добавления задач в очередь:

NAME                             READY   STATUS    RESTARTS   AGE task-processor-6d4d5c8c6d-abcde   1/1     Running   0          1m task-processor-6d4d5c8c6d-fghij   1/1     Running   0          30s ...

Масштабирование по HTTP-запросам с Prometheus

Рассмотрим еще один пример использования KEDA — масштабирование приложения на основе количества HTTP-запросов с Prometheus.

Создадим простой HTTP-сервис, который будет обрабатывать запросы. Предположим, есть Docker-образ myregistry/http-processor:latest.

# http-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:   name: http-processor   labels:     app: http-processor spec:   replicas: 1   selector:     matchLabels:       app: http-processor   template:     metadata:       labels:         app: http-processor     spec:       containers:       - name: processor         image: myregistry/http-processor:latest         ports:         - containerPort: 8080

Применим манифест:

kubectl apply -f http-deployment.yaml

Создадим ScaledObject, который будет масштабировать HTTP-сервис на основе метрик из Prometheus.

# http-scaledobject.yaml apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata:   name: http-processor-scaledobject   namespace: default spec:   scaleTargetRef:     kind: Deployment     name: http-processor   minReplicaCount: 1   maxReplicaCount: 5   cooldownPeriod: 300   pollingInterval: 30   triggers:   - type: prometheus     metadata:       serverAddress: http://prometheus-server.default.svc.cluster.local       metricName: http_requests_total       threshold: "100"  # Масштабировать при превышении 100 запросов       query: sum(rate(http_requests_total[2m])) by (job) > 100

Применим манифест:

kubectl apply -f http-scaledobject.yaml

Напишем скрипт для генерации HTTP-запросов к сервису.

# send_http_requests.py import requests import time  def main():     url = 'http://<your-http-service-url>:8080/process'     for i in range(200):         response = requests.get(url)         print(f" [x] Sent request {i}, Response Code: {response.status_code}")         time.sleep(0.5)  # Пауза между запросами  if __name__ == "__main__":     main()

Если вы работаете локально, используйте localhost или соответствующий сервисный URL в кластере.

Запускаем скрипт:

python send_http_requests.py

Открываем другой терминал и выполняем команду:

kubectl get pods -w -l app=task-processor

В этом примере так же увидим, как количество реплик вашего HTTP-сервиса увеличивается по мере роста числа запросов.


В заключение приглашаем всех желающих на первые открытые уроки в этом году:

  • 14 января — Percona XtraDB Cluster (PXC): знакомство и настройка. Подробнее

  • 15 января — Ansible: быстрый старт. Подробнее

  • 16 января — Улица разбитых кластеров: про бэкапы и реплики в PostgreSQL. Подробнее


ссылка на оригинал статьи https://habr.com/ru/articles/868526/