Что такое 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), но нет migration между нодами. 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 — detektsiya problem s 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: cheaper storage
- 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