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

486137cbaf7ad90805f35fc61b49be41.png

Привет, друзья-разработчики и 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  -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://: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. Подробнее

© Habrahabr.ru