Як відбувається масштабування в 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» — вони увійдуть у конфлікт і нескінченний шторм перезапусків.