Задача
Настроен HorizontalPodAutoscaler:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Нагрузка выросла, приложение тормозит, kubectl top pods показывает высокое потребление CPU. Реплик по-прежнему две.
kubectl get hpa выводит в колонке TARGETS значение <unknown>/70%.
Вопрос: почему HPA не масштабирует?
Что кажется очевидным
«Метрики не собираются, надо чинить metrics-server». Направление верное, но <unknown> возникает и по другой, более коварной причине.
Как это работает на самом деле
Главное, что нужно понять про HPA: averageUtilization — это процент не от лимита и не от мощности ноды, а от requests.
Формула:
utilization = текущее потребление CPU / requests.cpu × 100%Отсюда два следствия:
1. Без requests.cpu HPA не работает вовсе. Делить не на что — метрика становится <unknown>, масштабирования нет. Это и есть самая частая причина.
2. Проценты могут превышать 100. Если requests.cpu: 100m, а под потребляет 500m, утилизация — 500%. Это нормально: requests — обещание, а не потолок (см. задачу 3).
Отсюда же неочевидный эффект: завышенные requests «прячут» нагрузку от HPA. При requests.cpu: 2 и реальном потреблении 1 утилизация — 50%, порог в 70% не достигается, и HPA не реагирует, хотя приложению плохо по другим причинам.
Как HPA принимает решение
Желаемое число реплик считается так:
желаемые = ceil( текущие × ( текущая метрика / целевая метрика ) )При 2 репликах, текущей утилизации 140% и цели 70% получится ceil(2 × 140/70) = 4 реплики.
Дополнительно действуют встроенные предохранители:
- Порог нечувствительности — отклонение метрики меньше 10% от цели игнорируется, чтобы не «дёргать» число реплик.
- Окно стабилизации при уменьшении — по умолчанию 5 минут: HPA снижает число реплик неохотно, чтобы не схлопнуться на кратковременном затишье. Увеличение происходит быстро.
- Периодичность проверки — раз в 15 секунд по умолчанию.
Ответ
Метрика <unknown> означает, что HPA не может вычислить утилизацию. Две причины по частоте:
- У контейнеров не заданы
requests.cpu— нет базы для расчёта процента; - Не установлен или не работает metrics-server — нет данных о потреблении.
Что с этим делать
Проверка по шагам
# 1. Есть ли метрики вообще
kubectl top pods
# Если "error: Metrics API not available" — нет metrics-server
kubectl get deployment metrics-server -n kube-system
# 2. Заданы ли requests
kubectl get deployment api -o jsonpath='{.spec.template.spec.containers[*].resources}'
# 3. Что говорит сам HPA — в событиях причина обычно названа прямо
kubectl describe hpa apiВ describe смотрите блок Conditions: ScalingActive: False с причиной FailedGetResourceMetric — это как раз про отсутствие метрик или requests.
Настройка, которая работает
resources:
requests:
cpu: "200m" # обязательно для HPA по CPU
memory: "256Mi"
limits:
memory: "512Mi" # CPU-лимит часто лучше не ставитьrequests должны отражать типичное потребление под нормальной нагрузкой — тогда проценты HPA имеют смысл.
Тонкая настройка поведения
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50 # убирать не более половины реплик за раз
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100 # можно удваивать
periodSeconds: 30
- type: Pods
value: 4
periodSeconds: 30
selectPolicy: MaxАсимметрия здесь осознанная: вверх быстро, вниз осторожно. Преждевременное схлопывание при пиковой нагрузке дороже, чем лишние поды в течение пары минут.
Чего HPA не умеет
- Масштабировать по «медленным» метрикам мгновенно. Между ростом нагрузки и готовым подом проходит время: сбор метрик + решение + запуск пода + probe. Для резких пиков нужен запас реплик.
- Работать с CPU-метрикой для I/O-bound сервисов. Приложение, которое ждёт ответа БД, не грузит CPU — масштабировать нужно по числу запросов, длине очереди или задержке. Для этого нужны кастомные метрики (Prometheus Adapter или KEDA).
- Добавлять ноды. Если места нет, новые поды повиснут в
Pending— за ноды отвечает Cluster Autoscaler / Karpenter. HPA и автоскейлер нод — разные вещи, и нужны обе. - Совмещаться с VPA по одному ресурсу. HPA и VPA по CPU одновременно конфликтуют.