Как происходит масштабирование в Kubernetes
Масштабирование в Kubernetes — это динамическое изменение вычислительных мощностей для обработки меняющегося объёма пользовательской нагрузки.
🟢 Junior Level
30-секундный ответ
Масштабирование в Kubernetes — это динамическое изменение вычислительных мощностей для обработки меняющегося объёма пользовательской нагрузки.
Масштабирование происходит на трёх независимых уровнях:
- Горизонтальное масштабирование подов (Horizontal Scaling — HPA): добавление или удаление копий (реплик) подов приложения (например, было 2 пода, стало 10). Это основной и самый надёжный способ масштабирования в микросервисной архитектуре.
- Вертикальное масштабирование подов (Vertical Scaling — VPA): динамическое увеличение или уменьшение ресурсов CPU и RAM, выделенных конкретному контейнеру.
- Масштабирование кластера (Cluster Autoscaler / Karpenter): автоматический заказ и добавление новых физических серверов (нод) в кластер, если для новых подов не хватает места.
Наглядная аналогия из жизни
Представьте кассовую зону в супермаркете:
- Горизонтальное масштабирование (HPA): открываются новые соседние кассы с дополнительными кассирами (больше реплик).
- Вертикальное масштабирование (VPA): кассиру заменяют старый компьютер на супермощный моноблок и ставят скоростной сканер штрихкодов (больше CPU/RAM на процесс).
- Масштабирование кластера (Cluster Autoscaler): если в супермаркете кончилось место для установки новых кассовых столов, администрация сносит стену и арендует соседнее помещение (добавление новых нод).
Ручное vs Автоматическое масштабирование
- Ручное (Imperative / Declarative):
kubectl scale deployment order-service --replicas=5 - Автоматическое (HPA):
# Автомасштабирование от 2 до 10 реплик при загрузке CPU > 60% kubectl autoscale deployment order-service --cpu-percent=60 --min=2 --max=10
🟡 Middle Level
3-уровневая таксономия автомасштабирования в K8s
+─────────────────────────────────────────+
| МАСШТАБИРОВАНИЕ |
+────────────────────┬────────────────────+
│
┌─────────────────────────────────┼─────────────────────────────────┐
▼ ▼ ▼
[ Уровень 1: HPA ] [ Уровень 2: VPA ] [ Уровень 3: Cluster ]
Горизонтально Вертикально Пул серверов
Больше копий Pods Больше CPU/RAM на Pod Больше Worker Nodes
(HorizontalPodAutoscaler) (VerticalPodAutoscaler) (Cluster Autoscaler/Karpenter)
Как HPA рассчитывает количество реплик (Математическая модель)
Контроллер HPA каждые 15 секунд (параметр horizontal-pod-autoscaler-sync-period) опрашивает Metrics API и вычисляет желаемое число реплик по строгой формуле:
Пример расчёта:
- Текущее число подов: 2.
- Целевой порог загрузки CPU (
target): 50%. - Фактическая текущая загрузка CPU: 80%.
- Решение HPA: $\lceil 2 \times (80 / 50) \rceil = \lceil 3.2 \rceil =$ 4 реплики.
[!IMPORTANT] HPA не работает без
resources.requests! Процент утилизации CPU рассчитывается строго относительно значенияrequests.cpu. Если реквесты не заданы в манифесте пода, Metrics Server не может вычислить процент, и HPA переходит в состояние ошибкиScalingActive=False.
Предотвращение осцилляции (Flapping / Flapping Effect)
При резких всплесках трафика возможен эффект «дребезга»: нагрузка выросла на 5 секунд $\to$ HPA добавил 10 подов $\to$ нагрузка спала $\to$ HPA удалил 8 подов $\to$ JVM не успела прогреться $\to$ оставшиеся 2 пода захлебнулись.
Для стабилизации используется блок behavior:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
behavior:
scaleUp:
stabilizationWindowSeconds: 0 # Мгновенная реакция на пик нагрузки
policies:
- type: Percent
value: 100 # Удвоить поды за 15 секунд
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300 # Ждать 5 минут спада перед удалением подов!
policies:
- type: Pods
value: 1 # Удалять не более 1 пода в минуту
periodSeconds: 60
🔴 Senior Level
Почему масштабирование Java по Memory — катастрофический антипаттерн
На собеседованиях Senior-уровня частый вопрос:
«Почему опасно настраивать HPA для Spring Boot приложения по метрике
type: Resource, name: memory?»
Глубокое HotSpot объяснение:
- Поведение аллокатора памяти JVM: при запуске JVM выделяет память из пула операционной системы до границы
-Xmx(илиMaxRAMPercentage). - Память не возвращается мгновенно: после того как сборщик мусора (G1 или ZGC) очистил объекты в Young/Old Generation, выделенные виртуальные страницы памяти ядра Linux (RSS — Resident Set Size) НЕ возвращаются обратно операционной системе немедленно.
- Ловушка для HPA: метрика контейнера
container_memory_working_set_bytesвидит занятую память около 80–90%, даже если реальная нагрузка на сервис нулевая. - Каскадное масштабирование: HPA считает, что приложению не хватает памяти, и начинает бесконечно наращивать поды до границы
maxReplicas. Кластер переполняется раздутыми подами, тратя тысячи долларов впустую. Правило: Java-сервисы масштабируют строго по CPU utilization либо по Custom/External метрикам (RPS, Latency, Kafka Consumer Lag).
KEDA: Event-Driven Autoscaling и Scale-to-Zero
Стандартный HPA умеет опрашивать только внутренние метрики кластера и не умеет масштабировать от 0 до 1 (Scale-to-Zero). KEDA (Kubernetes Event-driven Autoscaling) расширяет возможности Kubernetes:
- Масштабирует поды на основе внешних очередей: Kafka Lag, RabbitMQ Queue Length, AWS SQS, Redis Lists.
- Умеет опускать реплики до 0, если в очереди нет сообщений, и мгновенно поднимать 1 под при появлении первого сообщения в топике.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-kafka-consumer-scaler
spec:
scaleTargetRef:
name: order-consumer-deployment
minReplicaCount: 0 # Поддержка Scale to Zero!
maxReplicaCount: 30
triggers:
- type: kafka
metadata:
bootstrapServers: kafka:9092
consumerGroup: order-processors
topic: new-orders
lagThreshold: "50" # На каждые 50 непрочитанных сообщений добавлять 1 под!
Масштабирование узлов: Karpenter vs Cluster Autoscaler
| Характеристика | Classical Cluster Autoscaler | AWS Karpenter (Next-Gen) | | :— | :— | :— | | Принцип работы | Управляет облачными Node Groups (ASG) | Напрямую заказывает виртуальные машины через Cloud API | | Скорость реакции | 3–6 минут на добавление ноды | 30–45 секунд | | Подбор инстансов | Шаблонный (добавляет инстансы фиксированного типа) | Just-in-Time Bin-Packing: выбирает оптимальный CPU/RAM под текущие поды | | Консолидация (Дефрагментация) | Примитивное удаление пустых нод | Активная миграция подов и замена дорогих нод на дешевые |
In-Place Pod Resize (Kubernetes 1.27+ / 1.29 beta)
Исторически изменение ресурсов через VPA требовало принудительного перезапуска и выселения пода (RestartPolicy).
Фича InPlacePodVerticalScaling позволяет изменять CPU и RAM контейнера на лету без перезапуска JVM-процесса через динамическое обновление cgroups.procs ядра Linux, исключая разрыв соединений.
4 Tricky Questions
1. Что произойдёт, если на один и тот же Deployment одновременно повесить HPA (по CPU) и VPA (в автоматическом режиме UpdateMode: "Auto")?
Ответ: Возникнет разрушительный конфликт двух управляющих контуров (Feedback Loop / Flapping Conflict):
- При резком росте нагрузки на приложение загрузка CPU вырастает до 90%.
- HPA решит, что нужно увеличить количество реплик (например, с 2 до 6).
- VPA параллельно решит, что подам не хватает ресурсов, принудительно перезапустит текущие поды и выделит им больше ядер CPU (с 1 до 4 ядер).
- Из-за увеличения ядер CPU относительный процент загрузки мгновенно падает. HPA реагирует на это и начинает уменьшать реплики обратно до 2.
- Система входит в нестабильный осцилляционный штопор с постоянными перезапусками подов. Правило: VPA и HPA никогда не настраиваются одновременно по одним и тем же метрикам. Допустимо сочетать HPA по Custom Metrics (RPS/Kafka Lag) с VPA по памяти.
2. Почему при масштабировании приложений на Java Spring Boot с помощью HPA часто наблюдается резкий всплеск ошибок 503 и задержек (Latency Spikes) в первые 30 секунд после старта новых реплик?
Ответ: Проблема вызвана особенностями прогрева виртуальной машины Java (JVM Warmup):
- Новые поды только что запустились, код выполняется в режиме интерпретатора, JIT-компилятор C1/C2 ещё не оптимизировал байт-код в нативный машинный код, а пулы соединений HikariCP и кэши пустые.
- Как только
readinessProbeотвечает успехом, Kubernetes мгновенно начинает балансировать на новый «холодный» под полную треть или четверть общего трафика. - Непрогретый под испытывает 100% нагрузку на CPU на компиляцию методов, запросы выстраиваются в очередь сокетов Tomcat, превышают таймауты и падают с ошибками.
Решение:
- Использование Spring Boot 3 AOT / GraalVM Native Image (старт за 50 мс без прогрева).
- Настройка
readinessProbeс прогревочным endpoint’ом. - Внедрение Service Mesh (Envoy / Istio) с функцией Slow Start Window (постепенное линейное наращивание веса трафика на новый под в течение 60 секунд).
3. Как HPA ведёт себя, если один из подов завис или находится в нездоровом состоянии (Unready) во время расчёта метрик?
Ответ: Алгоритм HPA содержит защитный механизм от искажения статистики:
- Если под находится в статусе
Unreadyили его метрика CPU не может быть собрана (например, под только запускается), HPA полностью исключает этот под из расчёта среднего значения утилизации. - Если бы HPA учитывал холодные поды с нулевым CPU, средняя загрузка искусственно занижалась бы, мешая необходимому масштабированию.
- Если все поды не отдают метрики, HPA сохраняет текущее количество реплик и отказывается масштабировать систему вверх или вниз до восстановления сбора метрик.
4. В чём разница между метриками Resource, Pods, Object и External в спецификации autoscaling/v2?
Ответ:
Resource: стандартные ресурсы контейнера, собираемые Metrics Server из cgroups (строгоcpuилиmemory). Вычисляются как среднее использование (averageValue) или процент от реквестов (averageUtilization).Pods: метрики самого пода, агрегированные по всем подам в таргете (например, среднее количество HTTP RPS на один под через Prometheus Adapter).Object: метрика стороннего объекта Kubernetes внутри того же namespace (например, количество активных соединений на контроллере Ingress).External: метрика внешнего сервиса, физически не представленного в кластере K8s (например, глубина очереди AWS SQS, длина очереди сообщений RabbitMQ или метрика в Datadog).
🎯 Шпаргалка для интервью
Главное о масштабировании
- HPA: изменяет число реплик подов (
HorizontalPodAutoscaler); основной инструмент в K8s. - VPA: изменяет CPU/RAM одного пода (
VerticalPodAutoscaler). - Cluster Autoscaler / Karpenter: добавляет/удаляет рабочие ноды (серверы).
- Обязательное условие HPA: наличие
Metrics Serverи блокаresources.requestsв манифесте пода. - Java Golden Rule: никогда не масштабировать Java по памяти (Memory). Масштабировать строго по CPU или RPS/Kafka Lag.
- Защита от колебаний: настраивать
behavior.scaleDown.stabilizationWindowSeconds(300 секунд). - KEDA: автомасштабирование по событиям (Kafka Lag, RabbitMQ) с поддержкой Scale to Zero ($0 \to 1$).
Сравнение Karpenter и Cluster Autoscaler
| Параметр | Cluster Autoscaler | Karpenter | | :— | :— | :— | | Архитектура | Через AWS AutoScaling Groups | Напрямую через Cloud API | | Скорость добавления ноды | 3–5 минут | 30–45 секунд | | Подбор ресурсов | Статический тип ВМ | Динамический под размер подов |
Красные флаги на собеседовании (Чего говорить нельзя)
- ❌ «Я настраиваю HPA для Java-сервиса по памяти, чтобы при 80% RAM поднимались поды» — JVM не отдает память ОС, HPA моментально улетит в
maxReplicasнавсегда. - ❌ «HPA может работать без указания requests в Deployment» — без requests процент утилизации посчитать невозможно.
- ❌ «HPA и VPA в автоматическом режиме отлично работают вместе по CPU» — они войдут в конфликт и бесконечный шторм перезапусков.