превратить сигналы наблюдаемости в надежные действия масштабирования

Автомасштабирование в Kubernetes на основе наблюдаемости: от метрик к действию

14 минут

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

Почему масштабирование только по ресурсам недостаточно

В каждом продакшен-кластере есть одно и то же противоречие: ёмкость нужна до всплеска спроса, но избыток сжигает бюджет, а нехватка бьет по пользователям. Обычно команды стартуют с HPA по CPU и памяти — это полезно, но реактивно. Пока утилизация пересечет порог и появятся новые реплики, задержка уже вырастет, а очереди уже накопятся. Проблема не в отсутствии данных, а в отсутствии логики управления. Prometheus и Grafana показывают опережающие сигналы — рост частоты запросов, тренд глубины очереди, дрейф p99 — но автомасштабирование часто видит только узкую ресурсную часть. Подход на основе наблюдаемости закрывает этот разрыв: сигналы уровня сервиса становятся ограниченными и проверяемыми решениями по масштабу.

Опорная архитектура: собрать, вычислить, применить, проверить

Смотрите на масштабирование как на замкнутую систему управления. Сначала собирайте телеметрию через OpenTelemetry Collector и Prometheus. Затем вычисляйте сигналы через recording rules, чтобы KEDA не запускала тяжелые сырые запросы при каждом опросе. Применяйте решения через ScaledObject в KEDA, который передает поведение в HPA с отдельными окнами для scale-up и scale-down. Проверяйте качество через панели и алерты: время до масштабирования, перерегулирование, дрожание реплик и влияние на SLO. Такой контур отвязывает масштабирование от одной метрики утилизации и делает его наблюдаемым элементом политики надежности.

Recording rules Prometheus для сигналов масштабирования

Предварительно считайте нормализованные сигналы для каждого сервиса и namespace. Формулы должны быть устойчивы к делению на ноль и к несовпадению меток. Основной сигнал — saturation ratio (текущая частота запросов к проверенной емкости на реплику), дополнительные — давление очереди и задержка. Для глубины очереди используйте gauge-метрику queue_depth и тренд через delta или increase; rate() применяйте только к счетчикам.

YAML · PrometheusRule для сигналов автомасштабирования
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: autoscaling-signals
  namespace: monitoring
spec:
  groups:
    - name: scaling.signals
      interval: 30s
      rules:
        - record: app:request_rps:2m
          expr: |
            sum by (namespace, service) (
              rate(http_requests_total{job="app"}[2m])
            )

        - record: app:queue_depth_delta:2m
          expr: |
            sum by (namespace, service) (
              delta(queue_depth{job="app"}[2m])
            )

        - record: app:latency_p99:5m
          expr: |
            histogram_quantile(
              0.99,
              sum by (le, namespace, service) (
                rate(http_request_duration_seconds_bucket{job="app"}[5m])
              )
            )

        - record: app:saturation_ratio
          expr: |
            app:request_rps:2m
            /
            clamp_min(app:capacity_rps, 1)

ScaledObject KEDA с защищенным поведением HPA

KEDA может объединять несколько триггеров Prometheus; масштабирование срабатывает, когда любой триггер выше порога. Используйте saturation ratio как основной вход для роста, а queue_depth_delta как ускоритель для всплесков. Настройте поведение HPA на быстрый рост и осторожное снижение, чтобы избежать флаттинга. Явно задавайте minReplicaCount и maxReplicaCount — это защита базовой надежности и бюджета кластера.

YAML · KEDA ScaledObject с триггерами Prometheus
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: api-gateway-scaler
  namespace: production
spec:
  scaleTargetRef:
    name: api-gateway
  minReplicaCount: 3
  maxReplicaCount: 50
  pollingInterval: 30
  cooldownPeriod: 180
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          stabilizationWindowSeconds: 30
          selectPolicy: Max
          policies:
            - type: Percent
              value: 100
              periodSeconds: 60
            - type: Pods
              value: 5
              periodSeconds: 60
        scaleDown:
          stabilizationWindowSeconds: 300
          selectPolicy: Min
          policies:
            - type: Percent
              value: 20
              periodSeconds: 60
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: saturation_ratio
        threshold: "0.8"
        query: |
          app:saturation_ratio{namespace="production",service="api-gateway"}

    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring:9090
        metricName: queue_depth_delta
        threshold: "20"
        query: |
          app:queue_depth_delta:2m{namespace="production",service="api-gateway"}

Панель здоровья масштабирования и алертинг

Подход на основе наблюдаемости не работает, если команда не может объяснить, почему изменилось число реплик. Добавьте отдельную панель рядом с SLO: текущие реплики, частота запросов, saturation ratio и изменения реплик. Поднимайте алерты на антипаттерны: длительное высокое насыщение без scale-up или частые изменения реплик при ровном трафике. Так автомасштабирование перестает быть черным ящиком и становится проверяемым контуром управления.

PromQL · ключевые запросы для панели масштабирования
# Текущее число реплик
sum by (namespace, deployment) (
  kube_deployment_status_replicas{namespace="production",deployment="api-gateway"}
)

# Изменения реплик за 10 минут
changes(
  kube_deployment_status_replicas{namespace="production",deployment="api-gateway"}[10m]
)

# Насыщение относительно порога
app:saturation_ratio{namespace="production",service="api-gateway"}
YAML · алерт на отставание масштабирования при высоком насыщении
groups:
  - name: autoscaling-health
    rules:
      - alert: ScalingLagUnderHighSaturation
        expr: |
          app:saturation_ratio{namespace="production",service="api-gateway"} > 0.9
          and
          avg_over_time(kube_deployment_status_replicas{namespace="production",deployment="api-gateway"}[5m])
          ==
          min_over_time(kube_deployment_status_replicas{namespace="production",deployment="api-gateway"}[5m])
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Насыщение высокое, а реплики не растут"

Практика эксплуатации: границы, нагрузочные тесты и безопасность выкатки

Начните с одного сервиса с понятным профилем нагрузки и известной capacity_rps. Прогоняйте нагрузочные тесты в k6, Locust или Vegeta и измеряйте время до масштабирования, пик задержки, перерегулирование по репликам и время стабилизации. Держите отдельные окна для scale-up и scale-down: агрессивное снижение чаще всего рождает дрожание. Ежемесячно пересматривайте min и max реплик по сезонности. Храните recording rules и манифесты масштабирования в том же репозитории, что и конфигурацию сервиса. На рискованных релизах сочетайте политику масштабирования с burn-rate алертами SLO, чтобы выкатка и контроль емкости усиливали друг друга. Цель — не частые события масштабирования, а стабильная пользовательская производительность при меняющемся спросе.

Начните с базовой телеметрии, а затем добавляйте продвинутую логику масштабирования по гайду по observability для небольших платформенных команд.

Единый конвейер сервисных и инфраструктурных сигналов — в гайде по production-пайплайну OpenTelemetry Collector.