масштабировать пакетные и очередевые нагрузки по внешним событиям
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.
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.
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-authapiVersion: 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 с лимитами подключений к базе.
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, иначе растущая очередь останется незамеченной.
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-authadvanced:
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.
