Зачем нужно автомасштабирование в 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: в чём разница и когда что использовать
| Инструмент | Объект или компонент | Что меняет | Триггер | Когда уместен |
|---|---|---|---|---|
| HPA | HorizontalPodAutoscaler, autoscaling/v2 | spec.replicas деплоймента или statefulset | CPU, память, кастомные метрики из Metrics API | Stateless API, воркеры с переменным RPS |
| VPA | VerticalPodAutoscaler, autoscaling.k8s.io/v1 | requests и limits контейнера | История потребления за окно наблюдения | Сервисы с плохо предсказуемым потреблением CPU или RAM |
| Cluster Autoscaler | Deployment в 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 конфликтует: оба инструмента читают одну метрику и меняют разные параметры. Держите это ограничение в голове при выборе, детали разберём ниже.
Как автоскейлеры взаимодействуют между собой
Цепочка событий при росте нагрузки выглядит так:
- HPA увеличивает spec.replicas деплоймента.
- Планировщик не находит ноду со свободным объёмом requests, новые поды переходят в Pending.
- Cluster Autoscaler видит Pending-поды и добавляет ноду в подходящей node group.
- Поды планируются, потребление 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 available | APIService 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 | Значения применяются при создании пода и далее не меняются | Новые деплойменты с предсказуемой нагрузкой |
| Recreate | Updater удаляет под, контроллер создаёт его с новыми 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 не масштабирует
- HPA в состоянии unknown, kubectl top pods пустой — у контейнеров нет requests по метрике. Начните с 50–70% от наблюдаемого потребления под рабочей нагрузкой.
- Поды троттлятся, HPA добавляет реплики, а latency не падает — занижен limit. При CPU-троттлинге каждый под получает лишь часть ядра. Единого универсального соотношения limits и requests нет: подбирайте их по профилю нагрузки и результатам нагрузочного теста, а не по фиксированной формуле.
- OOMKilled при живом HPA — память упирается в limit, добавочные реплики не помогают, пока не поднят limit или не настроен VPA.
- Резкие колебания 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 или память для одного workload | VPA меняет requests, HPA меняет replicas, утилизация скачет | Развести метрики или перевести VPA в Off |
| HPA и Cluster Autoscaler | Работают в паре, конфликта нет при корректных requests | Держать requests близкими к реальному потреблению |
| VPA в Recreate и Cluster Autoscaler | VPA поднимает requests, под не помещается, добавляется нода | Заранее оценить upperBound, на критичных сервисах оставить Off или Initial |
| VPA в Recreate и PodDisruptionBudget | Eviction затягивается, на одной реплике это простой | Минимум две реплики, ослабленный 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
- kubectl top nodes и kubectl top pods -A отдают ненулевые значения: Metrics API работает.
- kubectl get hpa: колонка TARGET не содержит unknown.
- У всех контейнеров есть requests по той метрике, от которой считает HPA.
- Нагрузочный тест: replicas растут, а не только CPU.
- kubectl get events -A | grep -i autoscaler: Cluster Autoscaler фиксирует добавление нод.
- kubectl describe vpa: рекомендации укладываются в limits контейнера.
- Троттлинг по container_cpu_cfs_throttled_seconds_total не растёт относительно вашей latency-цели.
- Поды не висят в Pending дольше ожидаемого времени провижининга ноды у вашего провайдера.
- PDB не блокирует evictions при снижении нагрузки.
- 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. Так каждый инструмент проверяется отдельно, и при сбое понятно, какой слой виноват.