Зачем HPA v2 и почему он «дребезжит»
Horizontal Pod Autoscaler в Kubernetes решает практичную задачу: держать нагрузку на подах около целевого уровня, автоматически делая scale up и scale down. В реальности часто появляется неприятный эффект: реплики то растут, то тут же падают, потом снова растут — это называют flapping (дребезг).
В HPA v1 (по сути, только CPU) инструментов «успокоить» поведение было мало. В HPA v2 появилось поле behavior: окна стабилизации, ограничения скорости роста/снижения, выбор «более агрессивной» или «более осторожной» политики.
Дальше разберём практическую настройку HPA v2 для:
- метрик CPU/Memory через
metrics-server; - custom metrics (RPS, глубина очереди, lag) через Prometheus и
prometheus-adapter; - устранения flapping:
stabilizationWindowSeconds,policies, корректныеrequestsи адекватный выбор метрик.
Как HPA принимает решение: короткая «модель в голове»
HPA периодически опрашивает API метрик и вычисляет желаемое число реплик. Для ресурсных метрик (CPU/Memory) обычно работает простая пропорция: если метрика вдвое выше таргета, то и реплик нужно примерно вдвое больше.
desiredReplicas ≈ currentReplicas × (currentMetricValue / targetMetricValue)
Затем вступают ограничения: minReplicas/maxReplicas, а в HPA v2 ещё и behavior. Если метрика шумная, поды долго стартуют, есть спайки или неверно выставлены requests — оценка будет «дёргаться», провоцируя flapping.
Важно помнить: HPA работает с метриками подов и/или объектов (через адаптер), но не понимает «бизнес-контекст». Поэтому задача администратора — подобрать метрику и правила так, чтобы изменения реплик происходили предсказуемо, с учётом задержек приложения.

CPU и Memory в HPA v2: что нужно для корректной работы
1) metrics-server — источник resource metrics
Для масштабирования по CPU/Memory HPA использует ресурсные метрики из metrics-server. Типовая быстрая проверка:
kubectl get apiservices | grep metrics
kubectl -n kube-system get deploy metrics-server
kubectl top nodes
kubectl top pods -A
Если kubectl top не показывает данные — HPA по ресурсам работать не будет (или будет в состоянии Unknown по этим метрикам).
2) Правильные requests — основа адекватного scale up/scale down
Частая причина «странного» поведения CPU-HPA — неверные resources.requests.cpu. Для таргета averageUtilization HPA смотрит на процент использования от request. Если request занижен в 5–10 раз, HPA будет видеть 300–800% утилизации и взлетать в реплики при любой нагрузке. Если request завышен — наоборот, HPA может «не видеть» рост нагрузки и не масштабироваться вовремя.
Быстро сравнить фактическое потребление и настройки requests можно так:
kubectl -n your-namespace top pods
kubectl -n your-namespace get deploy your-app -o yaml
Практика для старта: выставить requests примерно на 60–80 перцентиль steady-state потребления, а дальше уточнять по факту. Отдельно проверьте CPU limits: слишком жёсткие лимиты могут привести к троттлингу, и вы получите деградацию при «красивых» метриках.
3) Память: HPA может масштабировать, но это не «волшебная таблетка»
Масштабирование по memory технически работает, но часто даёт сюрпризы: память растёт ступенчато (GC/кэши), а OOM может случиться раньше, чем HPA успеет отреагировать. Обычно memory как триггер применяют:
- как дополнительный сигнал, но не единственный;
- для воркеров, где память реально коррелирует с объёмом работы;
- вместе с адекватными лимитами и наблюдением за OOM/eviction.
Если основная боль — пики RPS/очереди, чаще правильнее идти в custom metrics.
Custom metrics: зачем они нужны и как устроены
CPU/Memory — косвенные метрики. Для веб-приложений и воркеров часто полезнее масштабироваться по тому, что напрямую отражает нагрузку и/или SLO: RPS на pod, количество активных запросов, глубина очереди, lag консьюмера.
В Kubernetes обычно встречаются два API:
- Custom Metrics API — метрики уровня подов/неймспейса, которые HPA читает как
PodsилиObject. - External Metrics API — «внешние» метрики, не принадлежащие конкретному объекту Kubernetes (очереди, внешние сервисы).
Самый распространённый стек: Prometheus собирает метрики, prometheus-adapter публикует их в Custom/External Metrics API, HPA v2 читает и масштабирует.
Проверяем, что адаптер реально отдаёт метрики
Начните с проверки доступности API-сервисов:
kubectl get apiservices | grep -E 'custom.metrics|external.metrics'
Дальше полезно посмотреть, что вообще публикуется (для отладки правил адаптера):
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | head
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | head
И точечно проверить конкретную метрику (путь зависит от типа метрики и ресурса):
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/your-namespace/pods/*/http_requests_per_second" | head
Если здесь пусто или ошибки — HPA будет показывать Unknown и не будет масштабироваться по этой метрике.
Если ваш адаптер и мониторинг крутятся не в managed-кластере, а на своих серверах, часто удобнее держать инфраструктуру наблюдаемости отдельно на VDS: проще обновлять компоненты, контролировать ресурсы и не мешать прод-кластеру.
Практический HPA v2: CPU/Memory + custom metrics (шаблон)
Ниже пример манифеста HPA v2 с двумя ресурсными метриками и одной custom (на уровне pod). Это не «идеал для всех», но хороший старт, чтобы получить управляемое поведение и снизить flapping.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
namespace: your-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: your-app
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "25"
behavior:
scaleUp:
stabilizationWindowSeconds: 0
selectPolicy: Max
policies:
- type: Percent
value: 100
periodSeconds: 60
- type: Pods
value: 4
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
selectPolicy: Max
policies:
- type: Percent
value: 20
periodSeconds: 60
Как это читается:
- По CPU целимся в 70% от
requests, по памяти — 75%. - По custom метрике держим около 25 RPS на pod (условный пример).
scaleUpразрешаем агрессивнее: до +100% или +4 pod за минуту (что больше), потому чтоselectPolicy: Max.scaleDownзамедляем: окно стабилизации 5 минут и не более -20% за минуту.
Почему возникает flapping: 10 частых причин
Дребезг почти никогда не «баг в HPA», обычно это сочетание метрик, времени реакции и ограничений.
- Неверные requests CPU/Memory: HPA считает проценты от request.
- Шумная метрика (RPS/latency «скачет» каждую минуту) без сглаживания/усреднения.
- Слишком быстрый scale down при коротком окне стабилизации.
- Долгий старт подов (init, миграции, прогрев кэша): HPA добавляет ещё, пока новые не стали Ready.
- Неудачный тип метрики: например, p99 latency как триггер без фильтрации и минимального трафика.
- Неправильная агрегация в Prometheus: суммирование «в ноль» лейблов pod, пропуски, некорректная обработка reset counters.
- Слишком жёсткие CPU limits: троттлинг меняет поведение приложения и динамику метрик.
- Проблемы источника метрик: лаг
metrics-server, отвал адаптера, таймауты API. - Ступенчатая нагрузка, когда один pod не тянет, а два уже дают «запас» и запускают быстрый откат.
- Конкуренция контроллеров: KEDA, ручные правки replicas, cron-скейлинг, «умные» скрипты.

Инструменты HPA v2 против flapping: stabilization window и policies
Stabilization window: «не верь первому импульсу»
stabilizationWindowSeconds — ключевой параметр против дребезга. Он работает по-разному для scale up и scale down.
- Для scale down окно обычно ставят больше (например, 300–600 секунд). HPA будет выбирать «более осторожное» желаемое значение из истории за окно, чтобы не рубить реплики на кратковременной просадке.
- Для scale up часто оставляют 0 или небольшое значение: рост должен реагировать быстрее на реальную нагрузку.
Типовой рабочий паттерн: быстрый scale up, медленный scale down.
Policies: ограничиваем скорость изменения реплик
Политики позволяют задать «не более X подов» или «не более Y%» за период. Практически удобно комбинировать Percent и Pods, а выбор делать через selectPolicy:
selectPolicy: Max— выбрать наиболее агрессивную политику (быстрее менять реплики).selectPolicy: Min— выбрать наиболее консервативную политику (медленнее).
Для защиты от flapping чаще важнее ограничить scale down: например, не больше -10…-30% в минуту и окно стабилизации 5–10 минут.
Cooldown «в голове»: учитывайте время выхода новых подов на плато
Даже с окнами стабилизации полезно прикинуть, через сколько секунд новый pod начинает реально обслуживать трафик и отдавать репрезентативные метрики. Если приложение прогревается 2–3 минуты (JIT, кэши, коннекты), то scaleDown.stabilizationWindowSeconds логично держать не меньше этого времени, а иногда и больше.
Custom metrics без сюрпризов: как выбирать метрику и запрос в Prometheus
Стабильность важнее «красоты» метрики
Для autoscaling метрика должна быть:
- предсказуемо связана с нагрузкой (больше работы → больше метрики);
- достаточно гладкой на горизонте опроса HPA;
- правильно «привязана» к pod, если вы используете тип
Podsи хотите удерживать нагрузку «на pod».
Плохие кандидаты как единственный триггер: p99 latency, метрики с редкими выбросами, проценты ошибок без минимального объёма трафика, метрики, которые сильнее зависят от кэша/GC, чем от нагрузки.
Prometheus-практика: rate и агрегация «до pod»
Если вы делаете custom метрику RPS, обычно нужен rate() по счётчику и агрегация до pod. Для AverageValue HPA ожидает значения на каждый pod, чтобы посчитать среднее. В адаптере это достигается правилом, которое возвращает временной ряд с лейблом pod.
Проверьте два момента:
- что в метрике есть стабильный идентификатор pod (лейбл
podи обычноnamespace); - что вы не агрегируете всё до одного значения на namespace, если используете тип
Pods.
Если вы параллельно строите алертинг на эти метрики, полезно продумать маршрутизацию и «заглушки» на время инцидентов: как настроить routing, silence и шаблоны в Alertmanager.
Диагностика: что смотреть, когда HPA «скачет»
1) Состояние HPA и причины решений
kubectl -n your-namespace describe hpa app-hpa
В выводе особенно полезны блоки:
Metrics— какие значения видит HPA и какие таргеты применяет.Conditions— например,AbleToScale,ScalingActive,ScalingLimited.Events— причины изменений реплик и ошибки чтения метрик.
2) Проверка, что метрика соответствует ожиданиям
При flapping из-за custom metrics цель — увидеть «сырые» значения, которые HPA получает из адаптера (а не только в Grafana). Используйте kubectl get --raw для Custom/External Metrics API и сравнивайте с запросом в Prometheus.
3) Сопутствующие факторы: readiness, PDB, HPA vs rollout
Иногда дребезг — следствие того, что поды долго не проходят readiness, а HPA уже добавляет новые. Проверьте:
- время старта и readiness-пробы;
- PodDisruptionBudget, который ограничивает снижение реплик (и HPA начинает «пилить» попытки);
- идёт ли параллельный rollout, меняющий число Ready-подов.
Рецепты анти-flapping (проверенная база)
Рецепт 1: «быстро вверх, медленно вниз»
Подходит для веб-приложений с пиками нагрузки:
scaleUp.stabilizationWindowSeconds: 0 или 30–60;scaleDown.stabilizationWindowSeconds: 300 или 600;- scale down ограничить до 10–30% в минуту.
Рецепт 2: ограничиваем scale up, если поды дорогие
Если каждый pod тяжёлый (прогрев, миграции, долгий init), а нагрузка не «взрывная», имеет смысл ограничить рост: например, +2 pod в минуту. Это снижает риск «перестрелять» и затем получить немедленный откат.
Рецепт 3: один главный сигнал + один страховочный
Смешивать CPU, memory и custom metrics можно, но помните: HPA берёт максимум из желаемых реплик по всем метрикам. Добавление шумной метрики автоматически сделает поведение шумным.
- Главный триггер: RPS/pod или queue depth.
- Страховочный: CPU (чтобы поймать регрессии и неожиданные циклы).
- Memory: только если понимаете корреляцию и риск OOM.
Частые ошибки в манифестах HPA v2
- Использовать
averageUtilizationбез выставленныхrequests(или с «на глаз» значениями). - Задать слишком маленький
maxReplicas: HPA упирается в потолок, метрика остаётся выше target, а приложение деградирует. - Для custom metrics выбрать не тот тип (
PodsvsObjectvsExternal), из-за чего метрика агрегируется неправильно. - Не учесть лаг метрик: HPA реагирует на прошлое, поэтому резкие scale down опаснее.
Мини-чеклист перед продом
- Убедиться, что
metrics-serverработает иkubectl topотдаёт данные. - Проверить
requestsCPU/Memory на основе наблюдений, а не «минимально возможные». - Для custom metrics: проверить доступность Custom/External Metrics API и конкретный путь через
kubectl get --raw. - Поставить «быстро вверх, медленно вниз»: окно стабилизации для scale down и ограничения политики.
- Оценить время прогрева/старта подов и подстроить
stabilizationWindowSecondsпод реальность. - После изменений наблюдать HPA Events и динамику метрик минимум сутки на реальном трафике.
Итог
HPA v2 даёт достаточно рычагов, чтобы сделать autoscaling предсказуемым: CPU/Memory через metrics-server, custom/external метрики через prometheus-adapter, а против flapping — грамотные stabilizationWindowSeconds и policies. В большинстве случаев источник проблемы не «HPA сам по себе», а шумная метрика, неверные requests или слишком агрессивный scale down.
Если параллельно выстроили мониторинг, не забудьте про контроль сроков сертификатов и предупреждения заранее: алерты по истечению SSL-сертификатов и продлению.


