Этот модуль про две вещи: как кластер подстраивает число подов под нагрузку и как в эфемерном мире подов хранить данные, которые должны пережить пересоздание.
HPA: горизонтальное автомасштабирование
HorizontalPodAutoscaler меняет replicas у Deployment или StatefulSet по метрикам.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Формула: желаемое = ceil(текущее × текущая_метрика / целевая). При 4 подах и средней загрузке CPU 140% от requests при цели 70% получится 8 подов.
Важное:
- Utilization считается от requests, а не от лимитов. Без requests HPA по CPU не работает, а заниженные requests дают лишнее масштабирование.
- Нужен metrics-server (для CPU и памяти). Для своих метрик (RPS, длина очереди) нужен адаптер Prometheus или KEDA, который масштабирует и по внешним событиям, вплоть до нуля.
- Поведение задаёт
behavior: окно стабилизации на уменьшение по умолчанию 300 секунд, чтобы не было «пилы». - Память плохой сигнал для масштабирования: многие рантаймы не отдают память после пика, и HPA не сможет уменьшить число подов.
- Не ставь HPA и ручной
replicasв манифесте, который постоянно применяется: каждыйapplyбудет сбрасывать число реплик.
VPA меняет requests и limits подов (вертикально), Cluster Autoscaler или Karpenter добавляют и убирают узлы, когда поды не помещаются (Pending) или узлы простаивают. Обычно работают вместе: HPA добавил поды, они не поместились, автоскейлер узлов поднял узел.
Тома: PV, PVC, StorageClass
- PersistentVolume (PV): кусок хранилища в кластере (облачный диск, NFS, локальный диск).
- PersistentVolumeClaim (PVC): заявка приложения: «нужно 20 ГиБ, режим доступа такой, класс такой».
- StorageClass: шаблон для динамического создания PV: какой драйвер (CSI), тип диска, политика удаления.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: uploads
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
Режимы доступа:
- ReadWriteOnce (RWO): монтируется на запись одним узлом (несколько подов на этом узле могут). Обычные облачные блочные диски.
- ReadOnlyMany (ROX): только чтение многими узлами.
- ReadWriteMany (RWX): запись многими узлами: NFS, CephFS, облачные файловые хранилища.
- ReadWriteOncePod: строго один под.
reclaimPolicy: Delete (диск удаляется вместе с PVC, частое значение по умолчанию у динамических классов) или Retain (PV остаётся, данные можно спасти). Для важных данных Retain и бэкапы. volumeBindingMode: WaitForFirstConsumer создаёт диск в той зоне, где запланирован под, иначе под и диск могут оказаться в разных зонах, и под застрянет в Pending.
Расширение: если класс разрешает (allowVolumeExpansion: true), увеличивают spec.resources.requests.storage у PVC. Уменьшить нельзя.
StatefulSet
Для приложений с состоянием (базы, брокеры, кластеры вроде Kafka, etcd, Elasticsearch):
- Стабильные имена:
db-0,db-1,db-2, сохраняются при пересоздании. - Стабильный DNS через headless Service:
db-0.db.prod.svc.cluster.local. - Свой том на реплику:
volumeClaimTemplatesсоздаёт PVCdata-db-0,data-db-1... Подdb-1после пересоздания получает тот же PVC. - Порядок: по умолчанию поды создаются по одному по возрастанию номера (следующий после готовности предыдущего) и удаляются в обратном порядке.
podManagementPolicy: Parallelэто отключает. - Обновление RollingUpdate идёт от старшего номера к младшему;
partitionпозволяет обновить только часть реплик (канарейка). - При удалении StatefulSet или уменьшении реплик PVC не удаляются по умолчанию: данные защищены (настраивается
persistentVolumeClaimRetentionPolicy).
StatefulSet не делает репликацию данных за тебя: кто мастер, как синхронизировать реплики, как переключаться при отказе, решает само приложение или оператор (CloudNativePG, Strimzi, операторы Redis и других). Многие команды держат базы вне кластера в управляемых сервисах, и это нормальный выбор.
PodDisruptionBudget
PDB ограничивает добровольные нарушения: kubectl drain при обслуживании узла, обновление кластера, работа автоскейлера узлов.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api
spec:
minAvailable: 2 # или maxUnavailable: 1
selector:
matchLabels: { app: api }
От падения узла PDB не защищает (это недобровольное нарушение). Ловушка: minAvailable равен числу реплик, и drain никогда не закончится.
Размещение подов
nodeSelectorи node affinity: на какие узлы можно.- Pod anti-affinity и topologySpreadConstraints: распределить реплики по узлам и зонам, чтобы отказ одного не уронил всё.
- Taints и tolerations: узел отталкивает поды без соответствующей толерантности (выделенные узлы под GPU или под базы).
- PriorityClass: при нехватке места высокоприоритетные поды могут вытеснить низкоприоритетные.
Частые ошибки
- HPA без requests или с заниженными requests.
- Масштабирование по памяти для JVM или Node.
- База в Deployment с одним RWO-томом и RollingUpdate: новый под не может смонтировать диск, пока старый жив.
reclaimPolicy: Deleteдля данных, которые нельзя потерять.- Все реплики на одном узле без anti-affinity.
- PDB, который блокирует обслуживание узлов.
Что спросят на собесе
- Как работает HPA и от чего считается утилизация.
- Чем StatefulSet отличается от Deployment.
- Что такое PV, PVC и StorageClass, режимы доступа.
- Как HPA и автоскейлер узлов работают вместе.
- Зачем PodDisruptionBudget и от чего он не защищает.