Kubernetes
Теория: StatefulSet
В предыдущих уроках мы работали с Deployment — контроллером для stateless-приложений. Deployment создаёт Pod с произвольными именами, не гарантирует порядок запуска и предполагает, что все реплики взаимозаменяемы. Для веб-серверов и API это работает отлично.
Но есть приложения, которым нужна стабильная идентичность: базы данных, кластеры Kafka, Elasticsearch, ZooKeeper. Каждый экземпляр уникален, имеет своё состояние, и замена одного экземпляра другим — это не просто перезапуск, а потенциальная потеря данных или разрушение кластера. StatefulSet решает эту проблему.
Зачем нужен StatefulSet
Представьте кластер PostgreSQL с репликацией: один primary и два replica. Каждый инстанс должен знать свою роль и иметь стабильный адрес для связи с другими нодами кластера. В реальных HA-кластерах primary выбирается динамически — это делает оператор или само приложение (например, Patroni для PostgreSQL). Обычно создают отдельный Service, который указывает на текущего primary через labels, а не привязываются к database-0 навсегда.
Или кластер Elasticsearch из трёх нод. Каждая нода хранит часть данных (шарды). Если нода перезапустится с потерей данных — кластер потеряет часть индекса.
Deployment не подходит для таких сценариев:
- Pod получают случайные имена (
nginx-deployment-7b8c9d5f4-xk2p9) - При перезапуске Pod получает новое имя и новый IP
- Нет гарантии порядка запуска
- Нет связи между Pod и его хранилищем
StatefulSet даёт то, что нужно stateful-приложениям:
- Стабильные, предсказуемые имена Pod (
database-0,database-1,database-2) - Стабильные сетевые идентификаторы через Headless Service
- Гарантированный порядок запуска и остановки
- Постоянное хранилище, привязанное к конкретному Pod
Важно понимать: StatefulSet даёт стабильность идентичности Pod (имя, DNS) и привязку PVC к конкретному Pod. Но он не делает приложение кластерным или отказоустойчивым сам по себе. За репликацию, failover и выбор primary отвечает само приложение или оператор (Patroni, Zalando Postgres Operator, Elasticsearch Operator и т.д.).
Отличия от Deployment
Как работает StatefulSet
Создадим простой StatefulSet:
После применения вы увидите:
Pod создаются последовательно: web-0 запускается первым, только после его готовности запускается web-1, затем web-2.
Headless Service и сетевые идентификаторы
Обычный Service имеет ClusterIP и балансирует трафик между Pod. Headless Service (clusterIP: None) работает иначе — он создаёт DNS-записи для каждого Pod отдельно.
Headless Service нужен, чтобы у каждого Pod появились стабильные DNS-имена вида:
Для нашего примера:
Это позволяет обращаться к конкретному Pod по имени. Реплика базы данных может подключиться к нужной ноде кластера по её стабильному DNS-имени.
volumeClaimTemplates: хранилище для каждого Pod
В Deployment все Pod используют один PVC (или не используют вовсе). В StatefulSet каждому Pod нужен свой PVC — свои данные, своё хранилище.
volumeClaimTemplates — это шаблон PVC. StatefulSet автоматически создаёт PVC для каждого Pod:
StatefulSet создаст:
- Pod:
database-0,database-1,database-2 - PVC:
data-database-0,data-database-1,data-database-2
Каждый Pod привязан к своему PVC. При перезапуске database-1 он получит тот же PVC data-database-1 с теми же данными.
Порядок создания и удаления
StatefulSet гарантирует порядок:
При создании: По умолчанию StatefulSet использует podManagementPolicy: OrderedReady — Pod создаются последовательно. app-1 не запустится, пока app-0 не станет Ready. Это важно для кластерных приложений, где порядок запуска имеет значение.
При удалении: Pod удаляются в обратном порядке. Сначала app-2, потом app-1, потом app-0. Это позволяет корректно остановить кластер.
При обновлении: По умолчанию используется стратегия RollingUpdate. Pod обновляются в обратном порядке: сначала app-2, потом app-1, потом app-0.
Можно изменить поведение:
Но для большинства stateful-приложений последовательный запуск — это то, что нужно.
Обновление StatefulSet
StatefulSet обновляет Pod по одному, в обратном порядке: сначала app-2, потом app-1, потом app-0. Каждый Pod должен стать Ready, прежде чем начнётся обновление следующего.
Можно контролировать обновление через partition:
С partition: 2 обновятся только Pod с ordinal >= 2 (то есть app-2 и выше). Pod app-0 и app-1 останутся на старой версии. Это полезно для канареечного обновления — сначала обновить один Pod, проверить, потом остальные.
Масштабирование
При увеличении реплик:
Создаются database-3, database-4 с новыми PVC data-database-3, data-database-4.
При уменьшении реплик:
Удаляются database-4, database-3, database-2 (в обратном порядке). Но PVC остаются! Данные не теряются. Если снова увеличить реплики — Pod получат свои старые PVC.
Это важная особенность: StatefulSet не удаляет PVC автоматически. Для удаления PVC нужно сделать это вручную:
Когда использовать StatefulSet
Используйте StatefulSet:
- Базы данных: PostgreSQL, MySQL, MongoDB.
- Кластерные системы: Kafka, Elasticsearch, ZooKeeper, etcd.
- Приложения, которым нужна стабильная сетевая идентичность.
- Приложения, которым нужно постоянное хранилище, привязанное к конкретному экземпляру.
Используйте Deployment:
- Stateless веб-серверы и API.
- Воркеры, обрабатывающие очереди.
- Любые приложения, где экземпляры взаимозаменяемы.
Частые ошибки
- Забыли Headless Service — StatefulSet требует
serviceName, который указывает на Headless Service. Без него не будет стабильных DNS-имён видаpod-name.service-name. - PVC в статусе Pending — проблема не в StatefulSet, а в StorageClass или provisioner. Проверьте
kubectl get scиkubectl describe pvc <name>. - Pod не запускается, ждёт предыдущий — это нормальное поведение при
podManagementPolicy: OrderedReady.web-1ждёт, покаweb-0станет Ready. Проверьте, почемуweb-0не готов. - Думают, что StatefulSet = HA — StatefulSet не настраивает репликацию и failover. Это делает само приложение или оператор.
- Удалили StatefulSet, PVC остались — это by design. Данные не удаляются автоматически. Удалите PVC вручную, если они больше не нужны.
- Привязались к
app-0как к primary — в реальных HA-кластерах primary выбирается динамически. Используйте отдельный Service с селектором на текущего primary.
Что запомнить
- StatefulSet — для приложений со состоянием: базы данных, кластеры. Даёт стабильную идентичность, но не делает приложение отказоустойчивым автоматически.
- Стабильные имена —
app-0,app-1,app-2вместо случайных. - Headless Service — создаёт DNS-записи для каждого Pod:
app-0.service-name. - volumeClaimTemplates — автоматически создаёт PVC для каждого Pod.
- OrderedReady — по умолчанию Pod создаются по порядку, каждый ждёт готовности предыдущего.
- partition — позволяет контролировать обновление, обновляя только часть Pod.
- PVC не удаляются — при масштабировании вниз или удалении Pod данные сохраняются.


