Автомасштабирование: HPA, VPA, Cluster Autoscaler

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

Автомасштабирование в Kubernetes работает на трёх независимых уровнях: HPA меняет число подов, VPA — размер каждого пода, а Cluster Autoscaler — число узлов под все эти поды.

Зачем это на практике

В базовых разделах мы упоминали HorizontalPodAutoscaler как способ «добавить реплик под нагрузкой». На проде этого мало. Реальный трафик пульсирует: ночью почти ноль, в часы пик — десятикратный рост. Держать фиксированно 20 реплик «на всякий случай» — значит жечь деньги 20 часов в сутки ради 4 часов пика. Автомасштабирование решает задачу деньгами-по-потребности: ресурсов ровно столько, сколько нужно прямо сейчас.

Но «добавить подов» — лишь один из трёх рычагов. Если каждому поду выделено мало памяти, новые реплики не спасут — поможет VPA, который подберёт правильные requests. А если поды некуда ставить (узлы заполнены), их не запустит никакой HPA, пока Cluster Autoscaler не добавит узел. Эти три механизма дополняют друг друга, и понимать их связку — значит уметь строить кластер, который сам подстраивается под нагрузку.

metrics-server: топливо для HPA

HPA принимает решения на основе метрик, а откуда он их берёт? Базовый источник — metrics-server, лёгкий компонент, который собирает потребление CPU и памяти с каждого пода через kubelet и отдаёт их по Metrics API. Без него HPA по CPU/памяти просто не работает: вы увидите в статусе <unknown>/50%.

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# проверяем, что метрики собираются
kubectl top pods
kubectl top nodes

Команда kubectl top — быстрый способ убедиться, что metrics-server жив. Если она отдаёт цифры, HPA получит те же данные.

HorizontalPodAutoscaler вглубь

HPA масштабирует число реплик Deployment'а так, чтобы средняя загрузка держалась около целевой. Опишем автоскейлер декларативно (версия API autoscaling/v2, она умеет несколько метрик):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60

Здесь HPA держит среднюю загрузку CPU около 60% от requests, не опускаясь ниже 2 реплик и не поднимаясь выше 20. Ключевой нюанс: averageUtilization считается в процентах от requests, а не от лимита и не от мощности узла. Поэтому HPA вообще не работает без выставленного resources.requests.cpu у подов — ему не от чего считать процент.

Кастомные метрики

Масштабировать по CPU удобно не всегда. Очередь сообщений растёт не от загрузки процессора, а от числа необработанных задач; для веб-API честнее метрика «запросов в секунду» или «глубина очереди». Для этого HPA умеет брать кастомные метрики — но не из metrics-server, а из адаптера (обычно prometheus-adapter), который выставляет метрики приложения по Custom Metrics API:

  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "100"

Теперь HPA добавляет реплики, как только среднее число запросов на под превышает 100/с — даже если CPU при этом простаивает. Так масштабируют по бизнес-смыслу, а не по косвенному признаку.

VerticalPodAutoscaler: размер вместо количества

HPA отвечает на вопрос «сколько подов?». VPA отвечает на «какого размера каждый под?». Он наблюдает реальное потребление и рекомендует (или автоматически проставляет) requests/limits. Это спасает от двух бед: либо вы выставили requests с потолка и платите за неиспользуемый резерв, либо занизили — и поды страдают от нехватки.

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: worker-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: worker
  updatePolicy:
    updateMode: "Auto"

В режиме Auto VPA пересоздаёт поды с новыми ресурсами. Важно: VPA и HPA по одной и той же метрике несовместимы. Если оба смотрят на CPU, они конфликтуют — HPA добавляет реплики, VPA раздувает каждую, и система идёт вразнос. На практике VPA в режиме Off (только рекомендации) используют для подбора стартовых requests, а уже потом включают HPA по CPU. Либо HPA масштабируют по кастомной метрике, а VPA — по CPU/памяти: тогда они не пересекаются.

Cluster Autoscaler: масштабирование узлов

HPA создал 15 подов, но три из них висят в статусе Pending — на узлах нет места. Здесь вступает Cluster Autoscaler: он следит за неразмещёнными подами и при необходимости просит облако (через node group / managed node pool) добавить узел. А когда нагрузка спала и узлы простаивают — выводит лишние, переселяя поды.

Cluster Autoscaler опирается ровно на requests подов: «влезет ли под на узел» он решает по запрошенным ресурсам, а не по фактическому потреблению. Поэтому корректные requests — фундамент для всех трёх уровней сразу.

МеханизмЧто меняетКогда применять
HPAчисло реплик подаstateless-сервисы под пульсирующей нагрузкой
VPArequests/limits каждого подаподбор ресурсов, нагрузка плохо параллелится
Cluster Autoscalerчисло узлов кластеракогда подам не хватает места на узлах

Как это работает под капотом

HPA — это контроллер в control plane, который раз в 15 секунд (по умолчанию) опрашивает Metrics API и пересчитывает желаемое число реплик по формуле: желаемые = ceil(текущие × текущая_метрика / целевая_метрика). Если 4 пода грузят CPU на 90% при цели 60%, то ceil(4 × 90 / 60) = 6 — HPA выставит 6 реплик. Чтобы не «дёргать» систему при шуме метрик, у HPA есть окно стабилизации при уменьшении (по умолчанию 5 минут): он не снижает реплики мгновенно, а ждёт, убеждаясь, что спад устойчив. Увеличивает — быстро, уменьшает — осторожно, потому что недобор реплик опаснее небольшого перебора.

Cluster Autoscaler же не управляет узлами напрямую — он лишь дёргает API облачного провайдера, меняя желаемый размер node group. Сам факт появления узла и присоединения его к кластеру обеспечивает kubelet нового узла. Поэтому добавление узла занимает не секунды, а минуты: облаку нужно поднять виртуалку. Это закладывают в планирование пиков.

Частые ошибки

  • HPA без requests. Без resources.requests.cpu утилизация считается не от чего; HPA показывает <unknown> и не масштабирует.
  • Нет metrics-server. HPA по CPU/памяти молча не работает; первым делом проверьте kubectl top pods.
  • HPA и VPA по одной метрике. Конфликт: один растит реплики, другой — размер. Разводите их по разным метрикам или используйте VPA только в режиме рекомендаций.
  • Ждать узел мгновенно. Cluster Autoscaler добавляет узел минутами; при резком пике поды повисят в Pending. Держите небольшой запас или прогревайте узлы заранее.
  • Слишком агрессивный maxReplicas при кривой метрике. Ошибка в кастомной метрике (например, делёж на ноль) может раздуть деплой до потолка и опустошить бюджет; ставьте разумный потолок и алерты.

Итоги

  • Три уровня независимы: HPA — реплики, VPA — размер пода, Cluster Autoscaler — узлы; вместе они дают полную эластичность.
  • HPA считает утилизацию в процентах от requests, поэтому корректные requests — обязательное условие для всех трёх.
  • metrics-server питает HPA по CPU/памяти; для бизнес-метрик нужен адаптер (Custom Metrics API).
  • HPA и VPA нельзя натравливать на одну и ту же метрику — будет раскачка.
  • Cluster Autoscaler решает по requests и добавляет узлы минутами, а не мгновенно.
Проверьте себя
1. Относительно чего HPA вычисляет averageUtilization при масштабировании по CPU?
AОт лимита (resources.limits.cpu) пода
BОт requests (resources.requests.cpu) пода
CОт полной мощности CPU узла
DОт среднего по всему кластеру
2. Почему HPA и VPA нельзя настраивать на одну и ту же метрику (например, оба на CPU)?
AKubernetes запрещает создавать оба объекта в одном namespace
BОни конфликтуют: HPA добавляет реплики, а VPA одновременно меняет размер подов, и система раскачивается
CVPA полностью заменяет HPA, поэтому второй бесполезен
Dmetrics-server не умеет отдавать CPU сразу двум потребителям
3. За что отвечает Cluster Autoscaler в отличие от HPA?
AМеняет число реплик Deployment под нагрузкой
BПодбирает requests и limits для каждого пода
CДобавляет и убирает узлы кластера, когда подам не хватает места
DСобирает метрики CPU и памяти с подов