ADG Оставить заявку
Блог DevOps 5 мин чтения

Kubernetes 1.23: HPA v2 в GA и KEDA для event-driven autoscaling

Kubernetes 1.23 стабилизировал HPA v2 с custom metrics и ephemeral containers. Документируем настройку KEDA для autoscaling по очередям и внешним метрикам.

Контекст момента

Kubernetes 1.23 выходит с HPA v2 GA, ephemeral containers stable и объявляет FlexVolume deprecated

Kubernetes 1.23 выходит в декабре - RC уже доступен, и главное в нём - это не очередной deprecated API, а, наоборот, два давно ожидаемых перевода в stable. HPA v2 с поддержкой custom и external metrics наконец-то GA. Ephemeral containers - тоже stable. FlexVolume объявлен deprecated в пользу CSI, но это отдельная история, которую мы пока трогать не будем.

Нас интересует HPA v2, потому что мы уже несколько месяцев работаем с KEDA - и теперь под ней наконец стабильный API.

Что было не так с HPA v1

HPA v1 умеет масштабировать по CPU и памяти. Это рабочий минимум, но у большинства реальных нагрузок узкое место - не CPU. Worker'ы, обрабатывающие очереди, должны масштабироваться по глубине очереди. API-сервисы - по latency или RPS. Batch-джобы - по количеству задач в очереди.

HPA v2beta1 появился ещё в 1.6 и умеет в custom metrics через Metrics API. Но beta - это beta: API мог поменяться, и менялся. HPA v2beta2 вышел в 1.12. И вот только в 1.23 всё это переходит в autoscaling/v2 без beta-суффикса.

Для нас это означает одно: конфиги, которые мы писали как autoscaling/v2beta2, теперь можно переписать в autoscaling/v2, и это не временный хак, а нормальный API.

KEDA: зачем он нужен рядом с HPA

KEDA (Kubernetes Event-Driven Autoscaling) - это не замена HPA, а расширение над ним. Он реализует интерфейс External Metrics API, который HPA умеет читать с 1.10. KEDA переводит метрики из конкретных источников - RabbitMQ, Kafka, Redis, Azure Service Bus, Prometheus - в числа, которые HPA умеет потреблять.

Без KEDA для custom metrics нужно самостоятельно писать или поднимать Prometheus Adapter, настраивать правила для каждой метрики, держать конфиги в синхронизации. KEDA делает это через ScaledObject - один ресурс, где описываешь источник и параметры масштабирования.

У нас клиент с RabbitMQ-воркерами. Несколько очередей, разные приоритеты. До KEDA масштабирование было ручным: дежурный смотрел на графики в Grafana и менял replicas руками. Это работало, пока нагрузка была предсказуемой. Потом перестало.

Установка KEDA

KEDA ставится через Helm - это самый удобный путь:

helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda \
  --namespace keda \
  --create-namespace \
  --version 2.4.0

После установки в кластере появляется несколько CRD: ScaledObject, ScaledJob, TriggerAuthentication, ClusterTriggerAuthentication. И два деплоя: оператор и metrics-сервер, который регистрируется как external metrics API.

Проверить регистрацию:

kubectl get apiservice v1beta1.external.metrics.k8s.io
# NAME                            SERVICE                           AVAILABLE   AGE
# v1beta1.external.metrics.k8s.io   keda/keda-operator-metrics-apiserver   True   2m

Если AVAILABLE=True - HPA видит KEDA как источник метрик.

ScaledObject для RabbitMQ

Базовый пример для очереди RabbitMQ - масштабирование по глубине очереди:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: worker-scaledobject
  namespace: production
spec:
  scaleTargetRef:
    name: worker-deployment
  minReplicaCount: 1
  maxReplicaCount: 20
  cooldownPeriod: 60
  triggers:
    - type: rabbitmq
      metadata:
        host: amqp://rabbitmq.production.svc.cluster.local:5672
        queueName: tasks-high-priority
        mode: QueueLength
        value: "10"
      authenticationRef:
        name: rabbitmq-trigger-auth

value: "10" - это целевое количество сообщений на один pod. Если в очереди 100 сообщений, HPA выставит 10 реплик. Если 5 - одна реплика (minReplicaCount).

Credentials вынесены в TriggerAuthentication:

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-trigger-auth
  namespace: production
spec:
  secretTargetRef:
    - parameter: host
      name: rabbitmq-secret
      key: connection-string

KEDA создаёт под собой обычный HPA-объект - его видно через kubectl get hpa. Это значит, что поведение стандартное: cooldown, stabilization window, все механизмы HPA работают.

Несколько очередей - несколько триггеров

Реальный случай: три очереди с разными приоритетами. KEDA поддерживает несколько триггеров в одном ScaledObject - масштабирование происходит по максимальному значению среди всех:

triggers:
  - type: rabbitmq
    metadata:
      queueName: tasks-high-priority
      mode: QueueLength
      value: "5"
    authenticationRef:
      name: rabbitmq-trigger-auth
  - type: rabbitmq
    metadata:
      queueName: tasks-normal
      mode: QueueLength
      value: "20"
    authenticationRef:
      name: rabbitmq-trigger-auth

Если в high-priority 50 сообщений (нужно 10 реплик) и в normal 40 (нужно 2 реплики) - получим 10. Это работает так, как ожидаешь.

Что с ephemeral containers

Ephemeral containers - это debug-контейнеры, которые можно запустить в существующем pod без его рестарта. Использование простое:

kubectl debug -it pod/worker-abc123 \
  --image=busybox \
  --target=worker \
  -- sh

До stable это работало только с включённым feature gate EphemeralContainers. Теперь - по умолчанию. Для диагностики production-подов без netcat и curl в основном образе - очень удобно. Особенно если образ минимальный (distroless или scratch).

Практические наблюдения после недели с RC 1.23

Обновление манифестов. autoscaling/v2beta2 продолжает работать в 1.23 - API не удалён, только deprecated. Но мы уже начали переписывать на autoscaling/v2. Делать это заранее проще, чем в спешке перед следующим апгрейдом.

KEDA и HPA одновременно. Нельзя иметь и ScaledObject, и отдельный HPA на один Deployment - KEDA владеет HPA-объектом и перезаписывает его. Если такая конфигурация была - нужно убрать ручной HPA до применения ScaledObject.

Scale to zero. KEDA умеет уменьшать replicas до 0 при пустой очереди. Для воркеров это полезно. Для API-сервисов - осторожно: первый запрос будет ждать, пока pod поднимется. У нас пока minReplicaCount: 1 везде, где есть внешний трафик.

Результат первой недели - ручной мониторинг очередей и корректировка реплик ушли из дежурства. Это само по себе достаточный аргумент продолжать.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.