Задача
StatefulSet базы данных на 5 реплик. Для экономии его временно уменьшили до 3:
kubectl scale statefulset db --replicas=3Поды db-3 и db-4 удалены. Через неделю нагрузка вернулась, реплики подняли обратно до 5.
Вопрос: что окажется на дисках у новых db-3 и db-4 — пустые тома или данные недельной давности?
Что кажется очевидным
«Поды удалены — значит, и данные удалены. Новые реплики начнут с чистого листа и синхронизируются с остальными».
Это неверно, и последствия могут быть серьёзными.
Как это работает на самом деле
PVC переживают удаление подов. Это принципиальное отличие StatefulSet от Deployment.
volumeClaimTemplates создаёт для каждого пода свой PVC с предсказуемым именем:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
replicas: 5
serviceName: db-headless
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 100GiИмена получаются вида data-db-0, data-db-1, …, data-db-4.
При уменьшении числа реплик Kubernetes удаляет поды, но не удаляет PVC. Это сделано намеренно: данные считаются ценными, и автоматическое удаление было бы опасным.
Когда реплики возвращают обратно, под db-3 получает имя db-3, ищет PVC data-db-3 — находит существующий и монтирует его со старыми данными.
Почему это может быть проблемой
Для многих СУБД подключение узла с устаревшими данными — не «ускоренное восстановление», а риск:
- узел может попытаться участвовать в кворуме со старым состоянием;
- при некорректной обработке — конфликт данных или расхождение реплик;
- диск занят старыми данными, которые уже неактуальны.
Поэтому решение «вернуть реплики обратно» должно быть осознанным: либо вы хотите переиспользовать данные (быстрое восстановление узла), либо PVC нужно удалить вручную перед масштабированием вверх.
StatefulSet против Deployment
| Deployment | StatefulSet | |
|---|---|---|
| Имена подов | Случайный суффикс | Порядковые: db-0, db-1 |
| Тома | Общий PVC или без него | Свой PVC на каждый под |
| Порядок запуска | Параллельно | Последовательно, 0 → 1 → 2 |
| Порядок удаления | Произвольный | В обратном порядке |
| DNS | Общий Service | Стабильное имя на под (с headless Service) |
| Замена пода | Новое имя и IP | То же имя и тот же том |
Стабильная идентичность — главное, ради чего существует StatefulSet: под db-0 после перезапуска остаётся db-0 с тем же именем, тем же DNS-именем и тем же диском.
Ответ
На дисках окажутся данные недельной давности. PVC, созданные через volumeClaimTemplates, не удаляются при уменьшении числа реплик, и новые поды подхватывают существующие тома по имени.
Что с этим делать
Управление жизненным циклом PVC
Если нужно, чтобы тома удалялись автоматически, есть политика на уровне StatefulSet:
spec:
persistentVolumeClaimRetentionPolicy:
whenScaled: Delete # удалять PVC при уменьшении реплик
whenDeleted: Retain # но сохранять при удалении StatefulSetЗначения по умолчанию для обоих — Retain (сохранять). Ставьте Delete осознанно: для кэшей и легко воссоздаваемых данных это удобно, для баз — опасно.
Ручное удаление, когда нужен чистый старт:
kubectl delete pvc data-db-3 data-db-4Reclaim policy: судьба самого тома
Есть второй уровень — что происходит с PersistentVolume после удаления PVC. Определяется reclaimPolicy у StorageClass:
Delete— том удаляется вместе с данными (обычно по умолчанию у облачных StorageClass);Retain— том остаётся, данные сохраняются, освободить его нужно вручную.
Для продовых данных используйте Retain. Это последний рубеж между случайным kubectl delete и безвозвратной потерей.
kubectl get storageclass
kubectl patch pv pvc-xxxx -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'Практические правила
- Расширение тома, а не пересоздание. Убедитесь, что StorageClass имеет
allowVolumeExpansion: true— иначе увеличить диск можно будет только через пересоздание реплики. Расширение делается правкой размера в PVC (для StatefulSet — с оговорками:volumeClaimTemplatesиммутабелен, редактируются существующие PVC). ReadWriteOnce— это один узел. Блочные тома нельзя примонтировать к подам на разных нодах. Отсюда следует: под с таким томом «привязан» к зоне тома.WaitForFirstConsumerв StorageClass — почти всегда правильный выбор: том создаётся в той же зоне, где запланирован под, а не наоборот (см. задачу 16).- Порядок обновления. StatefulSet обновляется по одному поду в обратном порядке;
podManagementPolicy: Parallelускоряет запуск, но лишает гарантий порядка — для кворумных систем это может быть нежелательно. - Осиротевшие PVC стоят денег. Заведите регулярную проверку:BASH
# PVC, не связанные ни с одним подом kubectl get pvc -A - PVC — не бэкап. Снапшоты томов (
VolumeSnapshot) и логические бэкапы средствами самой СУБД — это разные вещи, и первое не заменяет второе.