масштабировать пакетные и очередевые нагрузки по внешним событиям

KEDA: событийное автомасштабирование в Kubernetes для пакетных и очередевых нагрузок

14 минут

HPA масштабирует по CPU и памяти; потребители очередей и пакетные задачи нуждаются в глубине очереди, отставании (lag) и расписании. KEDA даёт масштабирование до нуля, ScaledObject для долгоживущих воркеров и ScaledJob для параллельного разбора очереди — без оплаты простаивающих подов между всплесками.

Почему HPA не тянет потребителей очередей и пакетные задачи

Horizontal Pod Autoscaler хорош для API без состояния, где CPU или память следуют за запросами. Для событийных систем он ломается. Kafka-консьюмеры должны масштабироваться по отставанию группы (lag), а не по CPU — воркер с одним сообщением в минуту и воркер с тысячей в секунду для HPA выглядят одинаково. Пакетным конвейерам нужно масштабирование до нуля между запусками; минимум HPA — одна реплика. Появление файлов в S3, вебхуки и CDC не дают осмысленного ресурсного сигнала. Окна по cron требуют точной ёмкости на два часа, затем ноль. Команды, подкручивающие пороги CPU под такие случаи, либо переплачивают за простой, либо недодают мощность и копят необработанные сообщения, срывая SLO. HPA создавали для сервисов, управляемых запросами, а не событиями.

Как KEDA расширяет автомасштабирование Kubernetes

KEDA — оператор CNCF с десятками адаптеров масштабирования для Kafka, RabbitMQ, SQS, Prometheus, cron и других источников. Вы описываете ScaledObject или ScaledJob; оператор KEDA опрашивает внешние системы, отдаёт метрики через external.metrics.k8s.io и управляет HPA над Deployment или шаблоном Job. ScaledObject привязывается к Deployment и StatefulSet с опциональным масштабированием до нуля. ScaledJob создаёт краткоживущие Job по глубине очереди — единица работы выполняется и завершается, не держа долгоживущие поды. Несколько триггеров на одном ScaledObject масштабируют, когда любой из них требует ёмкости. KEDA стоит между внешними системами и нативным поведением HPA, которое вы настраиваете на быстрый рост и осторожное снижение.

Масштабирование Kafka-консьюмера через ScaledObject

Для группы, читающей топик orders, ориентируйтесь примерно на один под на партицию по lag. Задайте lagThreshold — допустимый backlog на реплику, minReplicaCount ноль при простое и maxReplicaCount по числу партиций или пропускной способности следующих сервисов. SASL и TLS выносите в TriggerAuthentication, не в манифест scaler. Сочетайте cooldownPeriod со stabilization при scaleDown в HPA, чтобы не дрожало число реплик при колебаниях lag.

YAML · ScaledObject Kafka с порогом lag
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaler
  namespace: production
spec:
  scaleTargetRef:
    name: order-processor
  pollingInterval: 15
  cooldownPeriod: 120
  minReplicaCount: 0
  maxReplicaCount: 12
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 180
          policies:
            - type: Percent
              value: 30
              periodSeconds: 60
  triggers:
    - type: kafka
      metadata:
        bootstrapServers: kafka-cluster.kafka.svc:9092
        consumerGroup: order-processor
        topic: orders
        lagThreshold: "100"
        offsetResetPolicy: latest
      authenticationRef:
        name: kafka-auth

Параллельный разбор очереди RabbitMQ через ScaledJob

Когда каждое сообщение независимо и имеет чёткий конец обработки, ScaledJob выигрывает у масштабируемого Deployment. KEDA создаёт Job до maxReplicaCount по длине очереди; завершённые Job сразу освобождают CPU и память. Стратегия accurate даёт примерно один Job на сообщение. Учётные данные RabbitMQ храните в TriggerAuthentication и ссылайтесь из адаптера — не зашивайте строку подключения в ScaledJob.

YAML · ScaledJob RabbitMQ для рассылки email
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  name: email-sender-job
  namespace: production
spec:
  jobTargetRef:
    template:
      spec:
        containers:
          - name: email-worker
            image: registry.example.com/email-worker:v2.4.1
            resources:
              requests:
                cpu: 250m
                memory: 256Mi
              limits:
                cpu: 500m
                memory: 512Mi
        restartPolicy: Never
  pollingInterval: 10
  successfulJobsHistoryLimit: 5
  failedJobsHistoryLimit: 3
  maxReplicaCount: 50
  scalingStrategy:
    strategy: accurate
  triggers:
    - type: rabbitmq
      metadata:
        protocol: amqp
        queueName: email.delivery.high-priority
        mode: QueueLength
        value: "1"
      authenticationRef:
        name: rabbitmq-auth
YAML · TriggerAuthentication для RabbitMQ
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-auth
  namespace: production
spec:
  secretTargetRef:
    - parameter: host
      name: rabbitmq-credentials
      key: connection-string

Окна по cron и триггер Prometheus по глубине очереди

Сочетайте запланированную ёмкость с реактивным масштабированием. Триггер cron гарантирует реплики в ночном пакетном окне; Prometheus добавляет поды, когда report_queue_depth растёт днём. KEDA оценивает все триггеры и масштабирует до максимально требуемого числа реплик. Явно указывайте часовой пояс и согласуйте desiredReplicas с лимитами подключений к базе.

YAML · cron и Prometheus на одном ScaledObject
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: report-generator
  namespace: analytics
spec:
  scaleTargetRef:
    name: report-generator
  minReplicaCount: 0
  maxReplicaCount: 20
  pollingInterval: 30
  triggers:
    - type: cron
      metadata:
        timezone: Europe/Minsk
        start: 0 2 * * 1-5
        end: 0 5 * * 1-5
        desiredReplicas: "10"
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: pending_reports
        threshold: "5"
        query: |
          sum(report_queue_depth{service="report-generator"})

Разделение аутентификации и прогрессивное поведение HPA

Держите TriggerAuthentication или ClusterTriggerAuthentication отдельно от ScaledObject — ротация учётных данных не должна трогать логику масштабирования. На AWS EKS используйте pod identity provider aws с IRSA вместо долгоживущих ключей. Поведение HPA настраивайте в блоке advanced KEDA: быстрый рост с selectPolicy Max, медленное снижение по процентам, чтобы не дрожало число реплик. При ошибке чтения адаптера KEDA откатывается к minReplicaCount — мониторьте keda_scaler_errors_total, иначе растущая очередь останется незамеченной.

YAML · TriggerAuthentication AWS SQS и scaler
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: aws-sqs-auth
  namespace: production
spec:
  podIdentity:
    provider: aws
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: sqs-consumer
  namespace: production
spec:
  scaleTargetRef:
    name: sqs-consumer
  minReplicaCount: 0
  maxReplicaCount: 40
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.eu-west-1.amazonaws.com/123456789/orders
        queueLength: "5"
        awsRegion: eu-west-1
      authenticationRef:
        name: aws-sqs-auth
YAML · быстрый scale-up и осторожный scale-down
advanced:
  horizontalPodAutoscalerConfig:
    behavior:
      scaleUp:
        stabilizationWindowSeconds: 0
        selectPolicy: Max
        policies:
          - type: Percent
            value: 100
            periodSeconds: 60
          - type: Pods
            value: 10
            periodSeconds: 60
      scaleDown:
        stabilizationWindowSeconds: 300
        selectPolicy: Min
        policies:
          - type: Percent
            value: 25
            periodSeconds: 120

Практика: лимиты, мониторинг и изоляция арендаторов

Явно задавайте minReplicaCount, maxReplicaCount, pollingInterval и cooldownPeriod — значения по умолчанию в продакшене часто неожиданны. Для Kafka ограничивайте maxReplicaCount числом партиций; лишние поды без партиций простаивают. Для параллельного разбора с чётким концом предпочитайте ScaledJob. Алертируйте на lag выше SLO, рост keda_scaler_errors_total и HPA на minReplicaCount при ненулевой глубине очереди. Анализируйте метрики адаптеров во времени, чтобы подобрать maxReplicaCount и пулы узлов. При NetworkPolicy разрешите подам оператора KEDA доступ к брокерам, Prometheus и API Kubernetes. Ограничьте RBAC KEDA namespace, которыми он управляет. KEDA превращает Kubernetes в событийную платформу: отзывчивое масштабирование очередей и пакетных задач, масштабирование до нуля между всплесками и стоимость, привязанная к реальной работе в очереди.

Масштабирование по saturation в Prometheus дополняет очередевые триггеры из гайда по observability-driven auto-scaling в Kubernetes.

Экономия от scale-to-zero всё равно требует бюджетов namespace из гайда по FinOps на уровне namespace с ResourceQuota.