Автомасштабирование в Kubernetes: практическое руководство по HPA, VPA и Cluster Autoscaler | AdminWiki

Автомасштабирование в Kubernetes: практическое руководство по HPA, VPA и Cluster Autoscaler

26 сентября 2026 13 мин. чтения
Содержание статьи

Зачем нужно автомасштабирование в Kubernetes и как выбрать подход

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

Отправная точка для любого сценария — metrics-server. Без него HPA не получает метрики CPU и памяти, а VPA нечего анализировать. Второй шаг — задать requests и limits у всех контейнеров: автоскейлеры считают утилизацию как отношение потребления к requests, поэтому поды без requests для них невидимы. Если нужно освежить, из чего состоит control plane и какие объекты API участвуют в схеме, начните с материала про архитектуру кластера и ключевые объекты API.

Дальше выбор идёт по типу нагрузки. Веб-API с переменным RPS масштабируется репликами, JVM-сервис с нестабильным heap подстраивается по размеру, batch-задачи и пиковые наплывы упираются в нехватку нод. Поля и флаги в примерах ниже рассчитаны на кластеры Kubernetes 1.24 и новее: перед применением сверьте доступные API-версии командой kubectl api-versions, а флаги компонентов — с документацией своей версии.

HPA, VPA и Cluster Autoscaler: в чём разница и когда что использовать

ИнструментОбъект или компонентЧто меняетТриггерКогда уместен
HPAHorizontalPodAutoscaler, autoscaling/v2spec.replicas деплоймента или statefulsetCPU, память, кастомные метрики из Metrics APIStateless API, воркеры с переменным RPS
VPAVerticalPodAutoscaler, autoscaling.k8s.io/v1requests и limits контейнераИстория потребления за окно наблюденияСервисы с плохо предсказуемым потреблением CPU или RAM
Cluster AutoscalerDeployment в kube-system плюс node groups провайдераКоличество нод в кластереПоды в статусе Pending, которые некуда запланироватьBatch, пиковые нагрузки, разнородные пулы нод

Примеры под каждую задачу. Для веб-API на Go с ростом RPS летом выдайте HPA на CPU 70% с minReplicas: 2 и maxReplicas: 10. Для Java-сервиса с нестабильным heap включите VPA в режиме Off и раз в неделю переносите рекомендации в манифест руками. Для ночного batch на 40 подов, которые днём не нужны, подойдёт Cluster Autoscaler: днём кластер живёт на двух нодах, ночью вырастает до пяти и возвращается обратно.

Комбинация HPA и VPA на CPU или памяти для одного workload конфликтует: оба инструмента читают одну метрику и меняют разные параметры. Держите это ограничение в голове при выборе, детали разберём ниже.

Как автоскейлеры взаимодействуют между собой

Цепочка событий при росте нагрузки выглядит так:

  1. HPA увеличивает spec.replicas деплоймента.
  2. Планировщик не находит ноду со свободным объёмом requests, новые поды переходят в Pending.
  3. Cluster Autoscaler видит Pending-поды и добавляет ноду в подходящей node group.
  4. Поды планируются, потребление CPU на реплику падает, HPA перестаёт наращивать replicas.

Обратный порядок тоже рабочий: VPA увеличивает requests, под перестаёт помещаться на текущей ноде, и Cluster Autoscaler добавляет ещё одну.

Конфликт возникает там, где HPA и VPA управляют одним и тем же ресурсом. HPA меняет число реплик, VPA меняет requests, а утилизация считается как отношение потребления к requests. Изменение знаменателя сдвигает метрику, HPA пересчитывает replicas, и число подов начинает колебаться. Рабочие комбинации: VPA в режиме Off для выдачи рекомендаций и HPA на CPU; либо VPA на память, а HPA на кастомную метрику (RPS, длина очереди) без пересечения. Настраивать оба инструмента на CPU для одного Deployment не стоит.

Установка и проверка metrics-server: база для HPA и VPA

metrics-server собирает данные о CPU и памяти из kubelet и публикует их через Metrics API, который читают HPA, VPA и команда kubectl top. Это агрегатор текущего среза, без хранения истории. Минимальные условия: kubelet с включённым встроенным cAdvisor и сетевой доступ контроллера до kubelet на порту 10250.

Пошаговая установка metrics-server

Манифест components.yaml скачайте из раздела релизов проекта metrics-server на GitHub: там лежит версия, соответствующая поддерживаемым кластерам. Порядок команд:

kubectl apply -f components.yaml
kubectl get deployment metrics-server -n kube-system
kubectl get apiservice v1beta1.metrics.k8s.io

Для кластеров с самоподписанными сертификатами kubelet в аргументы Deployment добавляют --kubelet-insecure-tls. В продакшене вместо него лучше передать корневой CA через --kubelet-certificate-authority: отключение проверки TLS упрощает подмену kubelet внутри сети кластера.

Косвенное требование к узлам: kubelet и container runtime должны использовать один и тот же cgroup driver, иначе управление ресурсами на уровне контейнера работает непредсказуемо, а метрики потребления расходятся с реально выставленными ограничениями. Kubelet и container runtime взаимодействуют с control groups, чтобы применять управление ресурсами для pod и контейнеров и устанавливать такие ресурсы, как cpu/memory requests и limits. С версии 1.22 kubeadm по умолчанию выставляет systemd cgroup driver, если параметр не задан явно (документация по container runtime).

Проверка метрик и диагностика проблем

kubectl top nodes
kubectl top pods -A
kubectl logs -n kube-system deployment/metrics-server
СимптомПричинаЧто делать
error: Metrics API not availableAPIService v1beta1.metrics.k8s.io не готовПроверить статус APIService, посмотреть логи Deployment, перезапустить его
unable to fetch metrics, dial tcp ...:10250 i/o timeoutНет сетевого доступа до kubeletПроверить firewall, NetworkPolicy и порт 10250 на нодах
x509: certificate signed by unknown authorityСамоподписанный сертификат kubeletДобавить --kubelet-insecure-tls или свой корневой CA
kubectl top отдаёт пустой списокНет работающих контейнеров с requests либо kubelet не отдаёт данныеПроверить requests у контейнеров и состояние kubelet

Истории метрик в metrics-server нет, графики и кастомные метрики собирает Prometheus. Kubernetes описывает сбор системных метрик и трейсов своих компонентов отдельными страницами документации (Kubernetes Monitoring), оттуда же берут основу для дашбордов по kubelet и API-серверу.

Настройка HPA: горизонтальное автомасштабирование подов

HPA — контроллер control plane, который работает как периодический, а не непрерывный control loop: он время от времени пересчитывает желаемый масштаб цели (например, Deployment), чтобы привести его в соответствие с наблюдаемыми метриками. Интервал задаётся параметром --horizontal-pod-autoscaler-sync-period у kube-controller-manager, и значение по умолчанию — 15 секунд (Horizontal Pod Autoscaling). Обязательное условие работы: у контейнера задан requests по той метрике, от которой считается утилизация. Если CPU requests не указан, target типа Utilization не рассчитывается, и HPA висит в состоянии с колонкой unknown.

Манифест HPA на autoscaling/v2 с пояснениями

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

Разбор полей:

  • averageUtilization: 70 — процент от requests, а не от limits. При requests 500m и limit 1 CPU целевые 70% означают 350m фактического потребления на реплику.
  • minReplicas: 2 — нижняя граница, ниже которой HPA не опустится даже при нулевой нагрузке. Для сервисов с требованием к доступности это значение стоит согласовать с принципами отказоустойчивого кластера без единой точки отказа.
  • behavior.scaleUp.stabilizationWindowSeconds: 0 — рост без задержки, пик отрабатывается сразу.
  • behavior.scaleDown.stabilizationWindowSeconds: 300 — пять минут ожидания перед снижением, короткие провалы нагрузки не приводят к откату.
  • scaleDown-политика Percent 10 за 60 секунд снимает не более десятой части реплик в минуту, кластер не теряет половину подов за один цикл.

Манифест на autoscaling/v1 с полем targetCPUUtilizationPercentage продолжает работать, но не умеет behavior и мультиметрику. Для новых кластеров берите autoscaling/v2.

Какие метрики использовать: CPU, память, кастомные

CPU подходит большинству stateless-сервисов: метрика меняется быстро и отражает загрузку обработчика. Как requests и limits влияют на планирование и на поведение автоскейлеров, подробно разобрано в статье про производительность Kubernetes, метрики и лимиты.

Память для HPA опасна. Приложение редко возвращает память операционной системе сразу: RSS растёт ступенями и держится. HPA по памяти добавит реплики, старые поды останутся с высоким потреблением, при снижении нагрузки утилизация не упадёт и replicas не уменьшатся. От OOMKilled при заниженном limit память как метрика тоже не спасает.

Кастомные метрики (RPS, длина очереди, latency) отдаются через внешний адаптер, чаще всего Prometheus Adapter: он превращает запрос PromQL в значение Metrics API, а HPA читает его так же, как CPU. Для воркера очереди метрика по числу сообщений точнее процессорной: CPU может быть занят ожиданием, а очередь при этом растёт.

Настройка VPA: вертикальное автомасштабирование подов

VPA состоит из трёх частей: Recommender считает рекомендации по истории потребления, Updater меняет ресурсы работающих подов, Admission Controller подставляет значения при создании пода. Изменить requests «на лету» VPA не может: лимиты cgroup пересоздаются только вместе с контейнером, поэтому смена значений требует перезапуска.

Манифест VPA и режимы работы

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: java-service
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: java-service
  updatePolicy:
    updateMode: "Off"
РежимПоведениеГде применять
OffТолько рекомендации, поды не трогаютсяПервые дни на любом сервисе, в том числе в продакшене
InitialЗначения применяются при создании пода и далее не меняютсяНовые деплойменты с предсказуемой нагрузкой
RecreateUpdater удаляет под, контроллер создаёт его с новыми requestsСервисы с несколькими репликами и без жёсткого SLA на рестарт
AutoСейчас ведёт себя как RecreateНе критичные окружения

Порядок внедрения: несколько дней в Off, сверка рекомендаций с фактическим потреблением, затем Initial на новых деплойментах. Режимы Recreate и Auto перезапускают поды, что на однорепличном сервисе означает простой.

Как читать рекомендации VPA и избегать конфликтов с HPA

kubectl describe vpa java-service

В блоке Recommendation три набора значений: target (то, что VPA применил бы сейчас), lowerBound и upperBound (границы интервала оценки). Если target выше limits, под упирается в предел и его нужно поднимать. Если upperBound в разы больше текущего потребления, назначайте limits с запасом и оставляйте VPA в Off.

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

Настройка Cluster Autoscaler: масштабирование нод

Cluster Autoscaler запускается Deployment в kube-system и работает с node groups облачного провайдера (AWS ASG, GCP MIG, Azure VMSS) либо с группами виртуальных машин в self-hosted окружении. У него две задачи: добавить ноду, если есть поды, которым не хватило места, и убрать ноду, ресурсы которой долго не нужны.

Как Cluster Autoscaler взаимодействует с HPA

Связка идёт через Pending-поды. HPA увеличивает replicas, планировщик не может разместить новые поды, Cluster Autoscaler читает их requests и подбирает node group. Ключевое условие — корректные requests: контейнер без requests, который фактически съедает 2 CPU, для планировщика выглядит как ноль, и Cluster Autoscaler не поймёт, нода какого размера нужна.

Время до появления готовой ноды зависит от провайдера, размера образа и текущей загрузки API: в примерах профиля AKS встречается ожидание провижининга до 15 минут (Use the Cluster Autoscaler in Azure Kubernetes Service), поэтому для мгновенных пиков держите одну ноду в запасе или настраивайте HPA так, чтобы рост не упирался в железо с первой секунды. Оценить реальную занятость нод и качество планирования поможет материал про то, как оценить загрузку нод и подов без перерасхода ресурсов.

Ключевые параметры и типичные ошибки Cluster Autoscaler

Флаги, которые правят чаще всего:

  • --node-group-auto-discovery — автообнаружение node groups по тегам облака вместо ручного перечисления.
  • --scale-down-delay-after-add=10m — пауза перед снижением после добавления ноды, защита от качелей «добавили-убрали». Значение по умолчанию — 10 минут.
  • --scale-down-unneeded-time=10m — сколько нода должна простаивать, чтобы попасть под удаление. Значение по умолчанию — 10 минут.
  • --scale-down-utilization-threshold — порог утилизации, ниже которого нода считается ненужной; конкретное значение по умолчанию сверяйте с документацией своей версии Cluster Autoscaler.

Ошибки: поды без requests мешают подобрать размер ноды; PodDisruptionBudget с minAvailable, равным числу реплик, блокирует удаление ноды; локальные PV привязывают под к конкретной ноде и она не удаляется; поды DaemonSet учитываются при расчёте занятости, но сами никогда не становятся причиной добавления ноды. Проверка: kubectl get events -A | grep cluster-autoscaler и логи Deployment в kube-system.

Типичные ошибки при настройке автомасштабирования и как их исправить

Ошибки в requests и limits: почему HPA не масштабирует

  1. HPA в состоянии unknown, kubectl top pods пустой — у контейнеров нет requests по метрике. Начните с 50–70% от наблюдаемого потребления под рабочей нагрузкой.
  2. Поды троттлятся, HPA добавляет реплики, а latency не падает — занижен limit. При CPU-троттлинге каждый под получает лишь часть ядра. Единого универсального соотношения limits и requests нет: подбирайте их по профилю нагрузки и результатам нагрузочного теста, а не по фиксированной формуле.
  3. OOMKilled при живом HPA — память упирается в limit, добавочные реплики не помогают, пока не поднят limit или не настроен VPA.
  4. Резкие колебания replicas — слишком короткое stabilizationWindowSeconds на снижение. Поставьте 300 секунд.

Троттлинг виден по метрикам cAdvisor container_cpu_cfs_throttled_seconds_total и container_cpu_cfs_throttled_periods_total (число троттлированных интервалов), которые собирает Prometheus (cAdvisor Prometheus metrics). Единого порога «доля троттлинга выше 10%» в документации нет: сравнивайте троттлинг со своей latency-целью и смотрите на динамику, а не на абсолютную цифру.

Конфликты между HPA, VPA и Cluster Autoscaler

КомбинацияЧто происходитКак исправить
HPA на CPU и VPA на CPU или память для одного workloadVPA меняет requests, HPA меняет replicas, утилизация скачетРазвести метрики или перевести VPA в Off
HPA и Cluster AutoscalerРаботают в паре, конфликта нет при корректных requestsДержать requests близкими к реальному потреблению
VPA в Recreate и Cluster AutoscalerVPA поднимает requests, под не помещается, добавляется нодаЗаранее оценить upperBound, на критичных сервисах оставить Off или Initial
VPA в Recreate и PodDisruptionBudgetEviction затягивается, на одной реплике это простойМинимум две реплики, ослабленный PDB

Отдельная группа ошибок живёт на уровне ноды. Kubelet и container runtime должны использовать один cgroup driver и одну конфигурацию: рассинхрон приводит к тому, что ограничения CPU и памяти применяются не так, как их видит планировщик (Các Container Runtime). Второй момент: Dockershim удалён из проекта Kubernetes начиная с версии 1.24, поэтому кластеры, обновлённые со старых релизов, должны работать на containerd или CRI-O.

Чек-лист проверки автомасштабирования под нагрузкой

Проверка идёт от метрик к поведению под нагрузкой. Начните с базовых команд.

Команды для проверки HPA, VPA и Cluster Autoscaler

kubectl top nodes
kubectl top pods -A
kubectl get hpa
kubectl describe hpa web-api
kubectl get vpa
kubectl describe vpa java-service
kubectl get pods -n kube-system | grep cluster-autoscaler
kubectl logs -n kube-system deployment/cluster-autoscaler --tail=100
kubectl get events -A --sort-by=.lastTimestamp
  1. kubectl top nodes и kubectl top pods -A отдают ненулевые значения: Metrics API работает.
  2. kubectl get hpa: колонка TARGET не содержит unknown.
  3. У всех контейнеров есть requests по той метрике, от которой считает HPA.
  4. Нагрузочный тест: replicas растут, а не только CPU.
  5. kubectl get events -A | grep -i autoscaler: Cluster Autoscaler фиксирует добавление нод.
  6. kubectl describe vpa: рекомендации укладываются в limits контейнера.
  7. Троттлинг по container_cpu_cfs_throttled_seconds_total не растёт относительно вашей latency-цели.
  8. Поды не висят в Pending дольше ожидаемого времени провижининга ноды у вашего провайдера.
  9. PDB не блокирует evictions при снижении нагрузки.
  10. Readiness probe настроен: иначе новые поды получат трафик раньше готовности, и HPA увидит искажённую картину. Логику проб и алертов разбирает материал про самовосстановление систем и проверки работоспособности.

Сценарий нагрузочного теста и что считать успехом

Пример для сервиса, который нормально переносит 50 параллельных клиентов:

hey -z 5m -c 50 http://web-api.internal/healthz

Ожидаемое поведение: HPA поднимает replicas с 2 до 10 за 2–3 минуты, утилизация CPU держится около целевых 70%, при нехватке места Cluster Autoscaler добавляет ноду (время зависит от провайдера), троттлинга нет. После остановки теста replicas снижаются примерно через 5 минут (stabilizationWindowSeconds: 300), а лишняя нода уходит после периода недоиспользования (по умолчанию scale-down-unneeded-time — 10 минут).

Что считать отклонением и куда смотреть: replicas не растут — проверяйте requests и состояние HPA; поды в Pending дольше ожидаемого времени провижининга — логи Cluster Autoscaler и квоты провайдера; latency растёт при высокой утилизации CPU — троттлинг и limits, а не количество реплик; рекомендации VPA выше limits — поднимите пределы или переводите сервис на Initial.

Дальше действуйте по порядку: поднимите metrics-server, выровняйте requests и limits, включите HPA с консервативным снижением, добавьте VPA в режиме Off для сбора рекомендаций и только после этого подключайте Cluster Autoscaler. Так каждый инструмент проверяется отдельно, и при сбое понятно, какой слой виноват.

Поделиться: