StatefulSets i przechowywanie danych w Kubernetes
Wdrażaj aplikacje stanowe z skalowaniem z zachowaniem kolejności i danymi trwałymi
Kubernetes StatefulSets to idealne rozwiązanie do zarządzania aplikacjami stanowymi, które wymagają stabilnych identyfikatorów, trwałego przechowywania danych oraz uporządkowanych wzorców wdrażania – niezbędnych dla baz danych, systemów rozproszonych oraz warstw cache.
Jeśli jesteś nowy w Kubernetes lub tworzysz klaster, rozważ eksplorowanie dystrybucji Kubernetes takich jak k3s lub MicroK8s do rozwoju, lub instalację Kubernetes z użyciem Kubespray do klastrów produkcyjnych.
To piękne zdjęcie zostało wygenerowane przez model AI Flux 1 dev.
Co to są StatefulSets?
StatefulSets to obiekt API obciążenia w Kubernetes, zaprojektowany specjalnie do zarządzania aplikacjami stanowymi. W przeciwieństwie do Deployments, które traktują wszystkie kontenery jako wymienne, StatefulSets utrzymują unikalną tożsamość dla każdego kontenera z gwarancjami dotyczącymi kolejności i unikalności.
Główne cechy:
- Stable Network Identifiers: Każdy kontener otrzymuje przewidywalny nazwisko hosta, który utrzymuje się po ponownym uruchomieniu
- Trwałe przechowywanie danych: Wyłączone PersistentVolumeClaims, które śledzą kontenery podczas ponownego wdrażania
- Uporządkowane wdrażanie: Kontenery tworzone są sekwencyjnie (0, 1, 2…) i zakończone w odwrotnej kolejności
- Uporządkowane aktualizacje: Aktualizacje w trybie rolowym przebiegają w kolejności, zapewniając stabilność aplikacji
StatefulSets są kluczowe dla aplikacji takich jak PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis i etcd – wszystkie obciążenia, w których tożsamość kontenera i trwałość danych mają znaczenie.
Zrozumienie trwałego przechowywania w Kubernetes
Kubernetes oferuje zaawansowaną warstwę abstrakcji przechowywania, która oddziela zarządzanie przechowywaniem od cyklu życia kontenera:
Komponenty przechowywania
PersistentVolume (PV): Fragment przechowywania w klastrze przydzielony przez administratora lub dynamicznie utworzony za pomocą StorageClass. PV istnieje niezależnie od kontenerów.
PersistentVolumeClaim (PVC): Wymóg przechowywania przez kontener. PVC wiąże się z dostępnymi PV, które odpowiadają jego wymaganiom (rozmiar, tryb dostępu, klasa przechowywania).
StorageClass: Definiuje różne “klasy” przechowywania z różnymi dostarczycielami (AWS EBS, GCE PD, Azure Disk, NFS itp.) i parametrami takimi jak replikacja, poziomy wydajności i polityki kopii zapasowych.
Tryby dostępu
- ReadWriteOnce (RWO): Wolumin zamontowany jako do odczytu i zapisu przez pojedynczy węzeł
- ReadOnlyMany (ROX): Wolumin zamontowany jako do odczytu przez wiele węzłów
- ReadWriteMany (RWX): Wolumin zamontowany jako do odczytu i zapisu przez wiele węzłów (wymaga specjalnych backendów przechowywania)
Architektura przechowywania StatefulSet
StatefulSets korzystają z volumeClaimTemplates, aby automatycznie utworzyć PersistentVolumeClaims dla każdej repliki kontenera. To jest fundamentalnie inne niż Deployments:
Jak działa 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
Gdy utworzysz ten StatefulSet:
- Kubernetes utworzy kontener
mysql-0i PVCdata-mysql-0 - Następnie utworzy kontener
mysql-1i PVCdata-mysql-1 - Na koniec utworzy kontener
mysql-2i PVCdata-mysql-2
Każdy kontener otrzymuje własny dedykowany 10GB wolumin trwały. Jeśli mysql-1 zostanie usunięty lub ponownie wdrożony, Kubernetes ponownie utworzy go i dołączy ten sam data-mysql-1 PVC, zachowując wszystkie dane.
Tworzenie bezgłównej usługi dla StatefulSets
StatefulSets wymagają bezgłównej usługi, aby zapewnić stabilne identyfikatory sieciowe:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None # To sprawia, że jest to bezgłowa usługa
selector:
app: mysql
ports:
- port: 3306
name: mysql
To tworzy wpisy DNS dla każdego kontenera:
mysql-0.mysql-service.default.svc.cluster.localmysql-1.mysql-service.default.svc.cluster.localmysql-2.mysql-service.default.svc.cluster.local
Aplikacje mogą bezpośrednio łączyć się z konkretnymi instancjami kontenerów za pomocą tych stabilnych nazw DNS.
Wzorce wdrażania StatefulSet
Dla zespołów zarządzających złożonymi wdrożeniami Kubernetes, Helm Charts oferują potężny sposób pakowania i wdrażania StatefulSets z szablonami, wersjonowaniem i zarządzaniem zależnościami. Helm upraszcza zarządzanie konfiguracjami StatefulSet w różnych środowiskach.
Skalowanie uporządkowane
Podczas skalowania z 3 do 5 replik:
kubectl scale statefulset mysql --replicas=5
Kubernetes tworzy kontenery w kolejności: mysql-3 → czeka, aż będzie gotowy → mysql-4
Podczas skalowania z 5 do 3 replik:
kubectl scale statefulset mysql --replicas=3
Kubernetes kończy w odwrotnej kolejności: mysql-4 → czeka, aż zostanie zakończony → mysql-3
Aktualizacje rolowe
StatefulSets obsługują dwa strategie aktualizacji:
OnDelete: Aktualizacje ręczne – kontenery aktualizują się tylko wtedy, gdy je usuniesz RollingUpdate: Automatyczne sekwencyjne aktualizacje w odwrotnej kolejności numerów
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Aktualizuj tylko kontenery z numerem >= 2
Parametr partition umożliwia wdrażanie kanaryjskie – możesz najpierw aktualizować kontenery o wyższych numerach, a następnie przetestować przed wdrożeniem na wszystkie repliki.
Praktyki przechowywania
Dynamiczne przydzielanie
Zawsze używaj StorageClass do dynamicznego przydzielania woluminów:
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: Włącza rozszerzanie PVC bez ponownego tworzenia
reclaimPolicy: Retain zachowuje dane PV po usunięciu PVC, Delete usuwa je automatycznie
Polityki retencji PVC
Kubernetes 1.23+ obsługuje persistentVolumeClaimRetentionPolicy:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # Zachowuj PVC po usunięciu StatefulSet
whenScaled: Delete # Usuń PVC po skalowaniu w dół
Opcje:
- Retain: Zachowuj PVC (domyślny zachowanie, najbezpieczniejsze)
- Delete: Automatycznie usuwaj PVC (przydatne w środowiskach deweloperskich)
Strategie kopii zapasowych
Snapshopy woluminów: Użyj zasobów VolumeSnapshot do tworzenia kopii zapasowych w punkcie w czasie
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20251113
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: data-mysql-0
Kopie zapasowe na poziomie aplikacji: Użyj narzędzi takich jak mysqldump, pg_dump lub Velero do kopii zapasowych baz danych
Replikacja rozproszona: Skonfiguruj replikację na poziomie aplikacji (replikacja MySQL, strumieniowa replikacja PostgreSQL) jako pierwszy warstwę obrony
Przypadki użycia w praktyce
Klaster bazy danych (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
Rozproszony cache (Redis)
Dla klastrów Redis potrzebne są zarówno StatefulSet, jak i staranny konfiguracja:
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
Kolejka wiadomości (Kafka)
Kafka wymaga zarówno trwałego przechowywania dla dzienników, jak i stabilnych identyfikatorów sieciowych dla koordynacji brokerów:
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
Monitorowanie i rozwiązywanie problemów
Dla kompletnego odniesienia do poleceń Kubernetes używanych w tej sekcji, zobacz Kubernetes Cheatsheet.
Sprawdź stan StatefulSet
# Wyświetl szczegóły StatefulSet
kubectl get statefulset mysql
kubectl describe statefulset mysql
# Sprawdź kolejność tworzenia kontenerów i ich stan
kubectl get pods -l app=mysql -w
# Wyświetl stan PVC
kubectl get pvc
kubectl describe pvc data-mysql-0
Typowe problemy
Kontener zawieszony w stanie oczekiwania: Sprawdź stan PVC i dostępność przechowywania
kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'
Pełne przechowywanie: Rozszerz PVC, jeśli StorageClass to pozwala
kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
Kontener nie chce się zakończyć: Sprawdź blokady na poziomie aplikacji lub problemy z odmontowaniem woluminu
kubectl delete pod mysql-0 --grace-period=0 --force
Metryki do monitorowania
- Użycie przechowywania: Monitoruj pojemność i procent użycia PVC
- Wydajność I/O: śledź IOPS, przepustowość i opóźnienie
- Restarty kontenerów: Częste restarty mogą wskazywać na problemy z przechowywaniem
- Czas wiązania PVC: Wolne wiązanie wskazuje na problemy z przydzielaniem
Strategie migracji
Podczas migracji do StatefulSets upewnij się, że klaster Kubernetes jest odpowiednio skonfigurowany. Dla domowych laboratoriów lub małych klastrów, sprawdź nasz szczegółowy przegląd dystrybucji Kubernetes, aby wybrać odpowiednią platformę dla Twoich wymagań obciążenia.
Od Deployment do StatefulSet
- Utwórz StatefulSet z volumeClaimTemplates
- Zmniejsz skalę Deployment w sposób łagodny
- Przywróć dane z kopii zapasowych do kontenerów StatefulSet
- Zaktualizuj odniesienia do DNS/Service
- Usuń stary Deployment i PVCs
Kopie zapasowe przed migracją
# Zrób snapshot istniejących PVC
kubectl get pvc -o yaml > pvc-backup.yaml
# Utwórz snapshoty woluminów
kubectl apply -f volume-snapshot.yaml
Rozważania bezpieczeństwa
Szyfrowanie przechowywania
Włącz szyfrowanie danych w spoczynku za pomocą parametrów 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
Kontrola dostępu
Użyj RBAC, aby ograniczyć, kto może tworzyć/modyfikować StatefulSets i 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"]
Polityki sieciowe
Ogranicz komunikację między kontenerami:
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
Optymalizacja wydajności
Warstwy wydajności przechowywania
Wybierz odpowiednie StorageClasses na podstawie obciążenia:
- Wysokie IOPS: Bazy danych z intensywnym odczytem i zapisem losowym (gp3, io2)
- Wysoka przepustowość: Agregacja dzienników, analiza (st1, sc1)
- Zrównoważone: Ogólne cele (gp3)
Rozproszenie kontenerów
Użyj antyafinity podów, aby rozproszyć kontenery StatefulSet w różnych strefach dostępności:
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: topology.kubernetes.io/zone
Wymagania i limity zasobów
Ustaw odpowiednie zasoby dla spójnej wydajności:
resources:
requests:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
limits:
cpu: "4"
memory: "8Gi"
ephemeral-storage: "20Gi"
Zaawansowane wzorce
Aplikacja stanowa z kontenerami inicjalizacyjnymi
Użyj kontenerów inicjalizacyjnych do inicjalizacji bazy danych:
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
Wiele kontenerów w podach dla sidecarów
Dodaj sidecary do kopii zapasowych lub agentów monitoringu:
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
# ... konfiguracja mysql ...
- name: backup-sidecar
image: mysql-backup:latest
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readOnly: true
ConfigMaps dla dynamicznej konfiguracji
Oddziel konfigurację od definicji 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
Przydatne linki
- Dokumentacja Kubernetes StatefulSets
- Trwałe woluminy
- Klasy przechowywania
- Snapshopy woluminów
- Najlepsze praktyki StatefulSet
- Dostawcy CSI
- Rozwiązania kopii zapasowych Velero
- Rook Ceph Storage
- Kubernetes Cheatsheet
- Instalacja Kubernetes z użyciem Kubespray
- Porównanie dystrybucji Kubernetes dla 3-węzłowego homelaba
- Dystrybucje Kubernetes - szybki przegląd kubeadm, k3s, MicroK8s, Minikube, Talos Linux i RKE2
- Mistrz Helm Charts: Przewodnik po zarządzaniu pakietami Kubernetes