Що таке StatefulSet і коли його використовувати?
StatefulSet гарантує чотири ключові властивості:
🟢 Junior Level
Просте визначення
StatefulSet — це контроллер в Kubernetes для управління додатками, яким важливо зберігати свою ідентичність та дані між перезапусками. На відміну від Deployment (де усі Pod’и однакові та взаємозамінні), кожен Pod в StatefulSet має унікальне ім’я, постійний DNS та свій власний диск.
StatefulSet – як Deployment, але кожен Pod отримує стабільне ім’я (web-0, web-1, web-2) та стабільне сховище (свій PersistentVolume). Pod’и створюються та видаляються по порядку.
Аналогія
Deployment — це як таксі: будь-яка машина може підвезти вас, усі взаємозамінні. StatefulSet — це як потяг: кожен вагон має свій номер, стоїть на певному місці, і якщо вагон №3 прибрати, потяг не може просто поставити будь-який інший на його місце — потрібен саме вагон №3.
Приклад YAML
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
spec:
serviceName: database-headless
replicas: 3
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: db
image: postgres:15
ports:
- name: postgres
containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
# Headless Service для StatefulSet
apiVersion: v1
kind: Service
metadata:
name: database-headless
spec:
clusterIP: None # Headless!
selector:
app: database
ports:
- port: 5432
Headless Service (clusterIP: None) – K8s не створює віртуальний IP. Замість цього DNS-запит повертає IP кожного Pod напряму. Потрібно для StatefulSet – кожен Pod має бути доступний за своїм ім’ям.
Приклад kubectl
# Подивитися StatefulSet
kubectl get statefulset database
# Подивитися Pod'и з їх унікальними іменами
kubectl get pods -l app=database
# database-0 Running
# database-1 Running
# database-2 Running
# DNS до конкретного Pod'у
# database-0.database-headless.default.svc.cluster.local
# Масштабувати (порядково!)
kubectl scale statefulset database --replicas=5
Коли використовувати
- Бази даних: PostgreSQL, MySQL, MongoDB
- Розподілені системи: Kafka, Zookeeper, Cassandra, Elasticsearch
- Будь-які додатки, де кожен екземпляр має унікальну роль (master/replica)
- Коли дані мають бути прив’язані до конкретного Pod’у
StatefulSet потрібен коли: (1) важлива ідентичність Pod (web-0 ≠ web-1), (2) потрібне стабільне сховище, (3) важливий порядок запуску (web-0 → web-1 → web-2).
🟡 Middle Level
Як це працює
StatefulSet гарантує чотири ключові властивості:
-
Стабільний мережевий ідентифікатор: Pod’и именуються
<name>-<ordinal>(database-0, database-1, database-2). Кожен Pod отримує DNS-ім’я:database-0.database-headless.default.svc.cluster.local. При перезапуску Pod зберігає своє ім’я. -
Стабільне сховище: Через
volumeClaimTemplatesKubernetes створює окремий PVC для кожного Pod’а (data-database-0,data-database-1). PVC не видаляється при видаленні Pod’а або StatefulSet. При перестворенні Pod монтує той самий PVC. -
Порядкове розгортання: Pod’и створюються строго по порядку (0 → 1 → 2) та видаляються у зворотному (2 → 1 → 0). Pod N не почне створюватися, поки Pod N-1 не стане
RunningтаReady. -
Headless Service: StatefulSet вимагає Service з
clusterIP: None. Цей Service не створює віртуальний IP, а дозволяє DNS резольвити кожен Pod індивідуально.
Практичні сценарії
Сценарій 1: PostgreSQL Master-Replica кластер
database-0 → Master (read/write)
database-1 → Replica (read-only)
database-2 → Replica (read-only)
Кожен Pod знає свою роль через ordinal. Init-контейнер визначає: якщо ordinal=0, стати master; якщо >0, підключитися до master як replica.
Сценарій 2: Kafka кластер
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka-headless
replicas: 3
template:
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5
env:
- name: KAFKA_BROKER_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
# broker.id = pod ordinal
Кожен Kafka broker має унікальний broker.id, рівний ordinal. Це критично для Kafka internals.
Сценарій 3: Паралельне масштабування
spec:
podManagementPolicy: Parallel # замість OrderedReady (за замовчуванням)
Для додатків, яким не потрібен строгий порядок (наприклад, Elasticsearch data nodes), Parallel прискорює масштабування.
Таблиця поширених помилок
| Помилка | Наслідок | Рішення |
|---|---|---|
| Забутий Headless Service | DNS не резольвить індивідуальні Pod’и, StatefulSet не працює коректно | Завжди створювати Service з clusterIP: None |
PVC не видаляються при kubectl delete statefulset |
“Осиротілі” PVC займають сховище | Видалити PVC вручну: kubectl delete pvc -l app=database |
Використання :latest тегу |
Rolling Update не працює, Pod’и після перестворення можуть отримати інший образ | Використовувати конкретні теги |
| Очікування, що StatefulSet сам налаштує кластер | StatefulSet тільки гарантує ідентичність, не налаштовує БД/кластер | Використовувати Init-контейнери або Operators |
| StorageClass без динамічного provisioning | PVC зависають в Pending, Pod’и не стартують | Налаштувати StorageClass з provisioner |
| StatefulSet для stateless додатку | Зайва складність, порядковий запуск сповільнює деплой | Використовувати Deployment |
Порівняльна таблиця: StatefulSet vs Deployment vs DaemonSet
| Характеристика | StatefulSet | Deployment | DaemonSet |
|---|---|---|---|
| Ідентичність Pod’ів | Унікальна (ім’я, DNS, PVC) | Усі однакові | Усі однакові |
| Порядок створення | Строгий (0→1→2) | Паралельний | Паралельний |
| Сховище | Стабільний PVC на Pod | Спільний PVC або ephemeral | HostPath або local volume |
| DNS | Індивідуальний (pod-N.service) |
Один Service IP | Один Service IP |
| Масштабування | Порядкове (повільне) | Паралельне (швидке) | По одній репліці на ноду |
| Коли використовувати | БД, черги, кластери | Stateless API, веб-додатки | Моніторинг, лог-агенти |
| Rolling Update | Зворотний порядок (N→0), з waitForFirstConsumer | Паралельний | По нодам |
Коли НЕ використовувати
- Stateless-додатки — використовуйте Deployment. StatefulSet додає складність без користі
- Коли усі Pod’и однакові — якщо не важлива ідентичність, Deployment простіше і швидше
- Потрібне швидке масштабування — порядковий запуск StatefulSet повільний. Для 100 реплік це займе 100 × startup_time
- Зберігання стану у зовнішній БД — якщо додаток stateless, а стан в PostgreSQL, використовуйте Deployment
🔴 Senior Level
Глибинна механіка: StatefulSet Controller, PVC, та Reconciliation
StatefulSet Controller Architecture: StatefulSet Controller (в kube-controller-manager) працює через reconciliation loop, але значно складніше за Deployment Controller:
- Watch: Підписується на StatefulSet, Pod, PVC події
- Ordinal Management: Підтримує набір ordinal’ів (0..N-1). Кожен ordinal = Pod + PVC
- Ordered Creation:
Для i = 0 до replicas-1: Якщо Pod-<i> не існує → створити Якщо Pod-<i> не Ready → чекати Якщо Pod-<i> Ready → перейти до i+1 -
PVC Binding: Для кожного Pod створюється PVC з
volumeClaimTemplates. PVC іменується<volumeClaimTemplate-name>-<statefulset-name>-<ordinal>. PVC створюється до Pod’а, щоб kubelet міг змонтувати volume перед запуском контейнера. - Pod Identity: При перестворенні Pod (на іншій ноді) контроллер знаходить існуючий PVC за label selector і приєднує його до нового Pod’у. PVC ніколи не видаляється автоматично.
Headless Service DNS:
Headless Service (clusterIP: None) не створює iptables/IPVS правил. Замість цього CoreDNS створює DNS записи для кожного Pod:
database-0.database-headless.default.svc.cluster.local → 10.244.1.5
database-1.database-headless.default.svc.cluster.local → 10.244.2.7
database-2.database-headless.default.svc.cluster.local → 10.244.3.9
DNS оновлюється при зміні Pod IP (через EndpointSlice).
PersistentVolume Binding:
PV прив’язується до PVC через claimRef. При видаленні Pod’а PVC зберігається. При видаленні StatefulSet PVC зберігається (orphaned). PV reclaimPolicy (Retain/Delete/Recycle) визначає долю даних.
Update Strategy:
spec:
updateStrategy:
type: RollingUpdate # або OnDelete
rollingUpdate:
partition: 0 # тільки Pod'и з ordinal >= partition оновлюються
RollingUpdate: Оновлює Pod’и у зворотному порядку (N→0), по одномуOnDelete: Оновлює Pod тільки після його ручного видаленняpartition: Дозволяє оновити тільки частину Pod’ів (для canary в StatefulSet)
Trade-offs
| Аспект | Trade-off |
|---|---|
| OrderedReady vs Parallel | OrderedReady = безпечніше для кластерів з leader election. Parallel = швидше для data nodes |
| Retain vs Delete PVC | Retain = дані безпечні, але потрібне ручне управління. Delete = автоматично, але ризик втрати даних |
| StatefulSet vs Operator | StatefulSet = базова ідентичність + storage. Operator = повна автоматизація (backup, failover, scaling). Operator складніший, але потужніший |
| Replica count | Малий (3) = менше ресурсів, але менш відмовостійкий. Великий (7+) = більше replica lag, повільніше writes |
| StorageClass local vs network | Local SSD = швидше (IOPS), але немає міграції між нодами. Network (EBS, Ceph) = portable, але latency вище |
Edge Cases (7+)
Edge Case 1: Pod переїжджає на іншу ноду, PVC не слідує
PVC прив’язаний до зони доступності (availability zone). Якщо Pod переїжджає в іншу зону, PVC не може бути примонтований (EBS/Ceph zone-locked). Pod застряє в ContainerCreating. Рішення: використовувати WaitForFirstConsumer volumeBindingMode в StorageClass — PVC створюється в зоні, де запланований Pod.
Edge Case 2: Split-brain при network partition StatefulSet з 3 репліками PostgreSQL. Network partition: database-0 (master) на одній стороні, database-1 та database-2 на іншій. database-1 та database-2 обирають нового master. Тепер два master’и записують дані. При відновленні мережі — data corruption. Рішення: Patroni або інший HA framework з consensus (etcd/Zookeeper).
Edge Case 3: StatefulSet Rolling Update з несумісними версіями Оновлення Kafka з версії 3.0 → 3.5. StatefulSet оновлює Pod’и у зворотному порядку: broker-2, broker-1, broker-0. Під час оновлення кластер має mix версій. Якщо протокол реплікації несумісний, data loss або cluster failure. Рішення: Operator з версіонуванням та pre-flight checks.
Edge Case 4: Orphaned PVC після kubectl delete statefulset
StatefulSet видалено, але 3 PVC (по 10Gi кожен) залишилися. Вони займають сховище, але не використовуються. Через тиждень StorageClass quota exceeded. Рішення: автоматизувати cleanup через finalizer або CronJob.
Edge Case 5: Readiness Probe + StatefulSet ordinal awareness Усі Pod’и мають однакову Readiness Probe. Але database-0 (master) та database-1 (replica) мають різні вимоги до готовності. Replica може бути “готова” тільки після повної синхронізації з master (що займає хвилини). Стандартна readinessProbe не враховує це. Рішення: custom readiness script, що перевіряє replication lag.
Edge Case 6: HPA з StatefulSet
HPA не працює з StatefulSet напряму — StatefulSet не підтримує scale subresource для HPA в тому ж вигляді, що Deployment. Починаючи з K8s 1.23, StatefulSet підтримує scale subresource, але HPA scaling не враховує порядкові обмеження. Рішення: KEDA (Kubernetes Event-driven Autoscaling) з StatefulSet scaler.
Edge Case 7: Pod ordinal reuse після видалення
Видалити database-2. StatefulSet з replicas=3 створює новий database-2. Новий Pod отримує той самий ordinal і той самий PVC (data-database-2). Але якщо PVC було видалено вручну, новий PVC створюється порожнім. Pod стартує без даних, і кластер думає, що у нього є репліка з даними. Рішення: ніколи не видаляти PVC без повної re-sync кластера.
Edge Case 8: StatefulSet з podManagementPolicy: Parallel та failure
При паралельному створенні усі 3 Pod’и стартують одночасно. Якщо database-0 (master) ще не готовий, database-1 та database-2 не можуть підключитися до master. Вони входять в crash loop. Рішення: OrderedReady (за замовчуванням) для кластерів з leader-follower архітектурою.
Performance Numbers
| Метрика | Значення |
|---|---|
| Pod creation latency (ordered) | N × (scheduling + container startup + readiness) |
| StatefulSet 3 replicas startup | 2-5 хвилин (залежить від container startup) |
| StatefulSet 10 replicas startup (ordered) | 5-15 хвилин |
| StatefulSet 10 replicas startup (parallel) | 1-3 хвилини |
| PVC creation + binding | 5-30 секунд (залежить від provisioner) |
| DNS update latency (CoreDNS) | 1-5 секунд |
| Rolling Update (reverse order) | N × startup_time (оновлює по одному) |
| Local SSD IOPS | 100K-1M IOPS, <1ms latency |
| Network storage (EBS) IOPS | 3K-64K IOPS, 1-10ms latency |
Security
- PVC дані не шифруються за замовчуванням — використовуйте StorageClass з encryption (AWS EBS encryption, Ceph encryption)
- StatefulSet Pod’и мають стабільні IP — це спрощує атаку на конкретний Pod. NetworkPolicy має обмежувати доступ до кожного ordinal
- Headless Service DNS enumeration — зловмисник може enumerate усі Pod’и через DNS:
pod-0.service,pod-1.service, … Не розкривайте чутливі сервіси через headless Service - PVC access modes —
ReadWriteOnceдозволяє монтувати тільки на одну ноду.ReadWriteMany— на кілька, що збільшує поверхню атаки - Pod Security Admission — StatefulSet Pod’и часто вимагають привілеїв (наприклад, для БД). Використовуйте
baselineабоprivilegedтільки якщо необхідно - Secrets для БД credentials — зберігайте в Secrets, монтуйте як volumes (не env vars). Rotating secrets вимагає Rolling Update StatefulSet
Production War Story
Ситуація: Фінтех-компанія, PostgreSQL кластер на StatefulSet (3 репліки), EBS storage, AWS. StatefulSet налаштований з volumeClaimTemplates, StorageClass з volumeBindingMode: Immediate.
Інцидент:
- Нода з database-0 (master) впала (hardware failure)
- Kubernetes перестворив database-0 на новій ноді
- EBS volume був в зоні us-east-1a. Нова нода була в us-east-1b
- PVC не міг примонтуватися:
Multi-Attach error— EBS zone-locked - database-0 застряг в
ContainerCreatingна 2 години - database-1 та database-2 (replicas) продовжували працювати в read-only режимі
- Усі writes впали — master недоступний
- Ручне втручання: snapshot EBS в us-east-1a → restore в us-east-1b → attach до PVC → restart Pod
- Downtime: 2.5 години, втрата ~$200K транзакцій
Post-mortem та fix:
- StorageClass з
volumeBindingMode: WaitForFirstConsumer— PVC створюється в зоні, де запланований Pod, а не заздалегідь - Pod Topology Spread Constraints — розкидати Pod’и по зонах:
```yaml
topologySpreadConstraints:
- maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule ```
- Patroni для HA — автоматичний failover: якщо master впав, replica promoted to master
- EBS Multi-AZ storage — використовувати AWS EBS з cross-zone replication (дорожче, але надійніше)
- Automated backup — щоденний snapshot + point-in-time recovery через WAL-G
- Alert на Pod pending > 5 хвилин — спрацював би одразу при zone mismatch
Моніторинг після fix:
# Alert: Pod в ContainerCreating > 5 хвилин
sum(kube_pod_status_phase{phase="Pending"}) by (statefulset) > 0
# Alert: PVC не примонтований
sum(kube_persistentvolumeclaim_status_phase{phase!="Bound"}) > 0
# Alert: PostgreSQL replication lag
pg_replication_lag_seconds > 30
# Alert: Pod cross-zone reschedule
kube_pod_info{namespace="database"} unless on(node) kube_node_info
Monitoring (Prometheus/Grafana)
Ключові метрики:
# StatefulSet replicas status
kube_statefulset_status_replicas
kube_statefulset_status_replicas_ready
kube_statefulset_status_replicas_current
kube_statefulset_status_replicas_updated
# Pod status по ordinal'ам
kube_pod_status_phase{statefulset="database"}
# PVC status
kube_persistentvolumeclaim_status_phase
# PVC storage usage
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes
# PostgreSQL replication lag (через postgres_exporter)
pg_replication_lag_seconds
# Kafka broker status (через kafka_exporter)
kafka_brokers
# DNS resolution latency для headless service
coredns_dns_request_duration_seconds{service="database-headless"}
Grafana Dashboard панелі:
- StatefulSet replicas: current vs ready vs desired — детекція mismatch
- Pod status по ordinal’ам — heatmap (0, 1, 2)
- PVC storage usage % — alert при > 80%
- Replication lag — critical для HA кластерів
- Pod restart rate по ordinal’ам — детекція crash loop конкретного Pod’а
- DNS resolution latency — детекція проблем з headless service
Highload Best Practices
- Використовуйте Operator, а не raw StatefulSet — Zalando Postgres Operator, Strimzi Kafka Operator. Вони автоматизують backup, failover, scaling, version upgrades
volumeBindingMode: WaitForFirstConsumer— PVC створюється в зоні Pod’а, не zone-locked заздалегідь- Pod Topology Spread Constraints — розкидати Pod’и по зонах та нодах для fault tolerance
- Розділяйте master та replica на різні StorageClass — master: продуктивний SSD, replica: дешевше сховище
- Automated backup — щоденний snapshot + continuous WAL archiving (WAL-G, barman)
- Monitor replication lag — alert при lag > 30 секунд
- PodDisruptionBudget —
minAvailable: 2для 3-replica кластера, щоб не втратити quorum - Readiness Probe з replication awareness — replica готова тільки після sync з master
podManagementPolicy: Parallelтільки для stateless data nodes — для master-replica кластерів завждиOrderedReady- Не використовуйте StatefulSet для production БД без Operator — StatefulSet дає ідентичність, але не автоматизує failover, backup, або recovery
- Storage IOPS monitoring — alert при volume IOPS approaching limit
- Regular failover testing — Chaos Engineering: kill master Pod, verify automatic failover
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- StatefulSet — контроллер для stateful додатків: стабільні імена (pod-0, pod-1), DNS, PVC
- Pod’и створюються по порядку (0→1→2), видаляються назад (2→1→0)
- Headless Service (
clusterIP: None) обов’язковий — DNS резольвить кожен Pod індивідуально - volumeClaimTemplates — окремий PVC на кожен Pod; PVC не видаляється при видаленні StatefulSet
- Для production БД використовуйте Operator (Zalando Postgres, Strimzi Kafka), не raw StatefulSet
- WaitForFirstConsumer в StorageClass — PVC створюється в зоні Pod’а, не zone-locked
- Rolling Update у зворотному порядку (N→0); partition для canary-оновлення
Часті уточнюючі запитання:
- «StatefulSet vs Deployment?» — Deployment: усі Pod’и однакові; StatefulSet: унікальна ідентичність + stable storage
- «PVC видаляється при
kubectl delete statefulset?» — Ні, PVC осиротіють; потрібно видаляти вручну - «HPA працює з StatefulSet?» — Обмежено (scale subresource з K8s 1.23); краще KEDA
- «Split-brain в StatefulSet?» — При network partition можливий dual master; потрібен Patroni/consensus
Червоні прапорці (НЕ говорити):
- «StatefulSet для stateless додатків» (надмірно, використовуйте Deployment)
- «StatefulSet сам налаштовує БД кластер» (тільки ідентичність; потрібне налаштування через Init/Operator)
- «PVC видаляється автоматично» (orphaned PVC — часта проблема)
- «StatefulSet = швидке масштабування» (порядковий запуск повільний)
Пов’язані теми:
- [[Що таке Pod в Kubernetes]] — одиниця запуску
- [[Як організувати rolling update в Kubernetes]] — оновлення Pod’ів
- [[Як відбувається масштабування в Kubernetes]] — KEDA для StatefulSet