StatefulSets и постоянное хранилище в Kubernetes
Развёртывание приложений с сохранением состояния, упорядоченным масштабированием и постоянными данными
Kubernetes StatefulSets — это основное решение для управления приложениями с состоянием, которым требуются стабильные идентификаторы, постоянное хранилище и упорядоченные паттерны развертывания, что критически важно для баз данных, распределенных систем и кэш-услуг.
Если вы новичок в Kubernetes или настраиваете кластер, рассмотрите возможность изучения дистрибутивов Kubernetes вроде k3s или MicroK8s для разработки, или установки Kubernetes с помощью Kubespray для кластеров производственного уровня.
Это приятное изображение было сгенерировано AI-моделью Flux 1 dev.
Что такое StatefulSets?
StatefulSets — это объект API рабочей нагрузки Kubernetes, специально предназначенный для управления приложениями с состоянием. В отличие от Deployments, которые рассматривают все поды как взаимозаменяемые, StatefulSets сохраняют уникальную идентичность для каждого пода с гарантиями порядка и уникальности.
Основные особенности:
- Стабильные сетевые идентификаторы: Каждый под получает предсказуемое имя хоста, которое сохраняется после перезапусков
- Постоянное хранилище: Выделенные PersistentVolumeClaims, которые следуют за подами при их переназначении
- Упорядоченное развертывание: Поды создаются последовательно (0, 1, 2…) и завершаются в обратном порядке
- Упорядоченные обновления: Ролинговые обновления происходят по порядку, обеспечивая стабильность приложения
StatefulSets критически важны для приложений вроде PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis и etcd — любых рабочих нагрузок, где важна идентичность пода и сохранение данных.
Понимание постоянного хранилища в Kubernetes
Kubernetes предоставляет сложный абстракционный слой хранилища, который отделяет управление хранилищем от жизненного цикла пода:
Компоненты хранилища
PersistentVolume (PV): Фрагмент хранилища в кластере, предоставленный администратором или динамически созданный через StorageClass. PVs существуют независимо от подов.
PersistentVolumeClaim (PVC): Запрос на хранилище от пода. PVC связываются с доступными PV, которые соответствуют их требованиям (размер, режим доступа, класс хранилища).
StorageClass: Определяет различные “классы” хранилища с разными провизерами (AWS EBS, GCE PD, Azure Disk, NFS и т.д.) и параметрами, такими как репликация, уровни производительности и политики резервного копирования.
Режимы доступа
- ReadWriteOnce (RWO): Том монтируется как запись-запись одним узлом
- ReadOnlyMany (ROX): Том монтируется как только для чтения многими узлами
- ReadWriteMany (RWX): Том монтируется как запись-запись многими узлами (требует специальных бэкендов хранилища)
Архитектура хранилища StatefulSets
StatefulSets используют volumeClaimTemplates для автоматического создания PersistentVolumeClaims для каждого реплики пода. Это принципиально отличается от Deployments:
Как работают volumeClaimTemplates
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
Когда вы создаете этот StatefulSet:
- Kubernetes создает под
mysql-0и PVCdata-mysql-0 - Затем создает под
mysql-1и PVCdata-mysql-1 - Наконец создает под
mysql-2и PVCdata-mysql-2
Каждый под получает свой собственный выделенный постоянный том объемом 10ГБ. Если mysql-1 будет удален или переназначен, Kubernetes создаст его заново и снова прикрепит тот же PVC data-mysql-1, сохранив все данные.
Создание Headless Service для StatefulSets
StatefulSets требуют Headless Service для предоставления стабильных сетевых идентификаторов:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None # Это делает его headless сервисом
selector:
app: mysql
ports:
- port: 3306
name: mysql
Это создает DNS-записи для каждого пода:
mysql-0.mysql-service.default.svc.cluster.localmysql-1.mysql-service.default.svc.cluster.localmysql-2.mysql-service.default.svc.cluster.local
Приложения могут подключаться напрямую к конкретным экземплярам пода, используя эти стабильные DNS-имена.
Паттерны развертывания StatefulSets
Для команд, управляющих сложными развертываниями Kubernetes, Helm Charts предоставляют мощный способ упаковки и развертывания StatefulSets с шаблонизацией, управлением версиями и управлением зависимостями. Helm упрощает управление конфигурациями StatefulSets в разных средах.
Упорядоченное масштабирование
При масштабировании с 3 до 5 реплик:
kubectl scale statefulset mysql --replicas=5
Kubernetes создает поды в порядке: mysql-3 → ожидание готовности → mysql-4
При масштабировании с 5 до 3 реплик:
kubectl scale statefulset mysql --replicas=3
Kubernetes завершает в обратном порядке: mysql-4 → ожидание завершения → mysql-3
Ролинговые обновления
StatefulSets поддерживают две стратегии обновления:
OnDelete: Ручное обновление — поды обновляются только при их удалении RollingUpdate: Автоматическое последовательное обновление в обратном порядке ординалов
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Обновлять только поды с ординалом >= 2
Параметр partition позволяет проводить канарные развертывания — вы можете сначала обновить поды с высокими номерами и протестировать перед развертыванием на всех репликах.
Лучшие практики работы с хранилищем
Динамическое предоставление
Всегда используйте StorageClasses для динамического предоставления томов:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Retain
allowVolumeExpansion: Позволяет изменять размер PVC без их пересоздания
reclaimPolicy: Retain сохраняет данные PV после удаления PVC, Delete автоматически удаляет их
Политики сохранения PVC
Kubernetes 1.23+ поддерживает persistentVolumeClaimRetentionPolicy:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Сохранять PVC при удалении StatefulSet
whenScaled: Delete # Удалять PVC при уменьшении масштаба
Варианты:
- Retain: Сохранять PVC (стандартное поведение, самое безопасное)
- Delete: Автоматически удалять PVC (полезно для сред разработки)
Стратегии резервного копирования
Снимки томов: Используйте ресурсы VolumeSnapshot для создания резервных копий на момент времени
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20251113
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: data-mysql-0
Резервное копирование на уровне приложения: Используйте инструменты вроде mysqldump, pg_dump или Velero для резервного копирования баз данных
Распределенная репликация: Настройте репликацию на уровне приложения (репликация MySQL, потоковая репликация PostgreSQL) как первую линию защиты
Реальные сценарии использования
Кластер баз данных (PostgreSQL)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 20Gi
Распределенный кэш (Redis)
Для кластеров Redis требуется как StatefulSet, так и тщательная настройка:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis-service
replicas: 6
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- "--appendonly"
- "yes"
- "--appendfsync"
- "everysec"
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
Очередь сообщений (Kafka)
Kafka требует как постоянного хранилища для логов, так и стабильных сетевых идентификаторов для координации брокеров:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka-service
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5.0
ports:
- containerPort: 9092
name: kafka
env:
- name: KAFKA_BROKER_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: data
mountPath: /var/lib/kafka/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
Мониторинг и устранение неполадок
Для всестороннего справочника по командам Kubernetes, используемым в этом разделе, см. Краткое руководство по Kubernetes.
Проверка состояния StatefulSet
# Просмотр деталей StatefulSet
kubectl get statefulset mysql
kubectl describe statefulset mysql
# Проверка порядка и состояния создания подов
kubectl get pods -l app=mysql -w
# Просмотр состояния PVC
kubectl get pvc
kubectl describe pvc data-mysql-0
Распространенные проблемы
Под застрял в состоянии Pending: Проверьте состояние PVC и доступность хранилища
kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'
Заполнено хранилище: Увеличьте PVC, если StorageClass это позволяет
kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
Под не завершается: Проверьте блокировки на уровне приложения или проблемы с размонтированием томов
kubectl delete pod mysql-0 --grace-period=0 --force
Метрики для мониторинга
- Использование хранилища: Мониторинг емкости и процента использования PVC
- Производительность I/O: Отслеживание IOPS, пропускной способности и задержки
- Перезапуски подов: Частые перезапуски могут указывать на проблемы с хранилищем
- Время привязки PVC: Медленная привязка указывает на проблемы с развертыванием
Стратегии миграции
При миграции на StatefulSets убедитесь, что ваш кластер Kubernetes правильно настроен. Для домашних лабораторий или небольших кластеров ознакомьтесь с нашим подробным сравнением дистрибутивов Kubernetes, чтобы выбрать подходящую платформу для ваших требований к нагрузке.
От Deployment к StatefulSet
- Создайте StatefulSet с volumeClaimTemplates
- Плавно уменьшите масштаб Deployment
- Восстановите данные из резервных копий в поды StatefulSet
- Обновите ссылки DNS/Service
- Удалите старый Deployment и PVCs
Резервное копирование перед миграцией
# Создайте снимок существующих PVCs
kubectl get pvc -o yaml > pvc-backup.yaml
# Создайте снимки томов
kubectl apply -f volume-snapshot.yaml
Меры безопасности
Шифрование хранилища
Включите шифрование “на месте” с помощью параметров StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: encrypted-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd-1234
Контроль доступа
Используйте RBAC для ограничения доступа к созданию/изменению StatefulSets и PVCs:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: statefulset-manager
rules:
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete"]
Политики сети
Ограничьте коммуникацию между подами:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-network-policy
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3306
Оптимизация производительности
Уровни производительности хранилища
Выбирайте подходящие StorageClasses в зависимости от нагрузки:
- Высокие IOPS: Базы данных с интенсивным случайным чтением/записью (gp3, io2)
- Высокая пропускная способность: Агрегация логов, аналитика (st1, sc1)
- Сбалансированные: Общие приложения (gp3)
Распределение подов
Используйте анти-аффинность подов для распределения подов StatefulSet по зонам доступности:
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: topology.kubernetes.io/zone
Запросы и ограничения ресурсов
Установите подходящие ресурсы для стабильной производительности:
resources:
requests:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
limits:
cpu: "4"
memory: "8Gi"
ephemeral-storage: "20Gi"
Дополнительные схемы
Stateful-приложение с init-контейнерами
Используйте init-контейнеры для инициализации базы данных:
spec:
template:
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
if [[ ! -d /var/lib/mysql/mysql ]]; then
mysqld --initialize-insecure --datadir=/var/lib/mysql
fi
volumeMounts:
- name: data
mountPath: /var/lib/mysql
Многоконтейнерные поды для sidecar
Добавьте sidecar для резервного копирования или мониторинга:
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
# ... конфигурация mysql ...
- name: backup-sidecar
image: mysql-backup:latest
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readOnly: true
ConfigMaps для динамической конфигурации
Разделите конфигурацию от определения StatefulSet:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
max_connections=200
innodb_buffer_pool_size=2G
---
spec:
template:
spec:
containers:
- name: mysql
volumeMounts:
- name: config
mountPath: /etc/mysql/conf.d
volumes:
- name: config
configMap:
name: mysql-config
Полезные ссылки
- Документация Kubernetes StatefulSets
- Persistent Volumes
- Storage Classes
- Volume Snapshots
- Лучшие практики StatefulSet
- CSI Drivers
- Решения Velero для резервного копирования
- Хранилище Rook Ceph
- Краткое руководство по Kubernetes
- Установка Kubernetes с Kubespray
- Сравнение дистрибутивов Kubernetes для 3-узловой домашней лаборатории
- Дистрибутивы Kubernetes - краткий обзор kubeadm, k3s, MicroK8s, Minikube, Talos Linux и RKE2
- Овладение Helm Charts: руководство по управлению пакетами Kubernetes