StatefulSets & Beständig Lagring i Kubernetes

Distribuera tillståndsbaserade appar med ordnad skalning och bestående data

Kubernetes StatefulSets är den bästa lösningen för att hantera tillståndsbaserade applikationer som kräver stabila identiteter, permanent lagring och ordnade distributionsmönster - essentiellt för databaser, distribuerade system och cache-lager.

Om du är ny på Kubernetes eller sätter upp en kluster, överväg att utforska Kubernetes distributioner som k3s eller MicroK8s för utveckling, eller installera Kubernetes med Kubespray för produktionskluster.

presentation i kaféet Det här trevliga bilden är genererad av AI-modellen Flux 1 dev.

Vad är StatefulSets?

StatefulSets är ett Kubernetes arbetsbelastnings-API-objekt som är speciellt utformat för att hantera tillståndsbaserade applikationer. Till skillnad från Deployments som behandlar alla pods som utbytbara, upprätthåller StatefulSets en unik identitet för varje pod med garantier om ordning och unikhet.

Nyckelfunktioner:

  • Stabila nätverksidentifierare: Varje pod får ett förutsägbart värdnamn som består över omstart
  • Permanent lagring: Dedikerade PersistentVolumeClaims som följer pods genom omplanering
  • Ordnad distribution: Pods skapas sekventiellt (0, 1, 2…) och avslutas i omvänd ordning
  • Ordnade uppdateringar: Rullande uppdateringar fortsätter i ordning, vilket säkerställer applikationsstabilitet

StatefulSets är kritiska för applikationer som PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis och etcd - alla arbetsbelastningar där podidentitet och datapermanens är viktiga.

Förstå permanent lagring i Kubernetes

Kubernetes erbjuder ett sofistikerat lagringsabstraktionslager som separerar lagringshantering från podlivscykeln:

Lagringskomponenter

PersistentVolume (PV): Ett lagringsutrymme i klustret som tillhandahålls av en administratör eller skapas dynamiskt via en StorageClass. PVs existerar oberoende av pods.

PersistentVolumeClaim (PVC): En begäran om lagring av en pod. PVCs binder till tillgängliga PVs som matchar deras krav (storlek, åtkomstläge, lagringsklass).

StorageClass: Definierar olika “klasser” av lagring med olika tillhandahållare (AWS EBS, GCE PD, Azure Disk, NFS etc.) och parametrar som replikering, prestanda-nivåer och säkerhetskopieringspolicyer.

Åtkomstlägen

  • ReadWriteOnce (RWO): Volymen monteras som skriv- och läsläsbar av en enda nod
  • ReadOnlyMany (ROX): Volymen monteras som skriftläsbar av många noder
  • ReadWriteMany (RWX): Volymen monteras som skriv- och läsläsbar av många noder (kräver specialiserade lagringsbackends)

StatefulSet-lagringsarkitektur

StatefulSets använder volumeClaimTemplates för att automatiskt skapa PersistentVolumeClaims för varje podreplica. Detta skiljer sig fundamentalt från Deployments:

Hur volumeClaimTemplates fungerar

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

När du skapar denna StatefulSet:

  1. Kubernetes skapar pod mysql-0 och PVC data-mysql-0
  2. Sedan skapar den pod mysql-1 och PVC data-mysql-1
  3. Slutligen skapar den pod mysql-2 och PVC data-mysql-2

Varje pod får sin egen dedikerade 10GB permanenta volym. Om mysql-1 tas bort eller omplaneras, återskapar Kubernetes den och bifogar samma data-mysql-1 PVC, vilket bevarar all data.

Skapa en Headless Service för StatefulSets

StatefulSets kräver en Headless Service för att tillhandahålla stabila nätverksidentiteter:

apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  clusterIP: None  # Detta gör det till en headless service
  selector:
    app: mysql
  ports:
  - port: 3306
    name: mysql

Detta skapar DNS-poster för varje pod:

  • mysql-0.mysql-service.default.svc.cluster.local
  • mysql-1.mysql-service.default.svc.cluster.local
  • mysql-2.mysql-service.default.svc.cluster.local

Applikationer kan ansluta direkt till specifika podinstanser med hjälp av dessa stabila DNS-namn.

StatefulSet-distributionsmönster

För team som hanterar komplexa Kubernetes-distributioner, erbjuder Helm Charts ett kraftfullt sätt att paketera och distribuera StatefulSets med mallning, versionshantering och beroendehantering. Helm förenklar hanteringen av StatefulSet-konfigurationer över olika miljöer.

Ordnad skalning

När du skalas upp från 3 till 5 replikor:

kubectl scale statefulset mysql --replicas=5

Kubernetes skapar pods i ordning: mysql-3 → väntar på Klar → mysql-4

När du skalas ner från 5 till 3:

kubectl scale statefulset mysql --replicas=3

Kubernetes avslutar i omvänd ordning: mysql-4 → väntar på avslut → mysql-3

Rullande uppdateringar

StatefulSets stöder två uppdateringsstrategier:

OnDelete: Manuella uppdateringar - pods uppdateras bara när du tar bort dem RollingUpdate: Automatiska sekventiella uppdateringar i omvänd ordningsföljd

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2  # Endast uppdatera pods med ordinal >= 2

Parametern partition möjliggör canary-distributioner - du kan uppdatera pods med höga nummer först och testa innan du rullar ut till alla replikor.

Lagringsbästa praxis

Dynamisk tillhandahållning

Använd alltid StorageClasses för dynamisk volymtillhandahållning:

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: Aktiverar storleksändring av PVC utan att återskapa dem reclaimPolicy: Retain behåller PV-data efter PVC-radering, Delete tar bort den automatiskt

PVC-bevarandepolicyer

Kubernetes 1.23+ stöder persistentVolumeClaimRetentionPolicy:

spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain    # Bevara PVCs när StatefulSet tas bort
    whenScaled: Delete     # Ta bort PVCs när skalning sker

Alternativ:

  • Retain: Bevara PVCs (standardbeteende, säkraste)
  • Delete: Ta automatiskt bort PVCs (användbart för utvecklingsmiljöer)

Säkerhetskopieringsstrategier

Volymsnappshot: Använd VolumeSnapshot-resurser för att skapa säkerhetskopior vid specifika tidpunkter

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: mysql-snapshot-20251113
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: data-mysql-0

Applikationsnivå-säkerhetskopior: Använd verktyg som mysqldump, pg_dump eller Velero för databasspecifika säkerhetskopior

Distribuerad replikering: Konfigurera applikationsnivåreplikering (MySQL-replikering, PostgreSQL-strömningsreplikering) som första försvar

Reella användningsområden

Databaskluster (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

Distribuerad cache (Redis)

För Redis-kluster behöver du både StatefulSet och noggrann konfiguration:

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

Meddelandekö (Kafka)

Kafka kräver både permanent lagring för loggar och stabila nätverksidentiteter för brokersamordning:

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

Övervakning och felsökning

För en omfattande referens av Kubernetes-kommandon som används i detta avsnitt, se Kubernetes Cheatsheet.

Kontrollera StatefulSet-status

# Visa StatefulSet detaljer
kubectl get statefulset mysql
kubectl describe statefulset mysql

# Kontrollera pod-skapsordning och status
kubectl get pods -l app=mysql -w

# Visa PVC-status
kubectl get pvc
kubectl describe pvc data-mysql-0

Vanliga problem

Pod fast i väntande: Kontrollera PVC-status och lagringsutrymme

kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'

Lagring full: Expandera PVC om StorageClass tillåter

kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

Pod vill inte avslutas: Kontrollera för applikationsnivålås eller volymavmonteringsproblem

kubectl delete pod mysql-0 --grace-period=0 --force

Mätvärden att övervaka

  • Lagringsanvändning: Övervaka PVC-kapacitet och användningsprocent
  • I/O-prestanda: Följ IOPS, genomströmning och latens
  • Pod-omstartar: Frekventa omstartar kan indikera lagringsproblem
  • PVC-bindningstid: Långsam binding tyder på tilldelningsproblem

Migrationsstrategier

När du migrerar till StatefulSets, se till att din Kubernetes-kluster är korrekt konfigurerad. För hemkluster eller små klustermiljöer, granska vårt omfattande jämförelse av Kubernetes-distributioner för att välja rätt plattform för dina arbetsbelastningskrav.

Från Deployment till StatefulSet

  1. Skapa StatefulSet med volumeClaimTemplates
  2. Minska skalning på Deployment på ett smidigt sätt
  3. Återställ data från backup till StatefulSet-pods
  4. Uppdatera DNS/Tjänst referenser
  5. Ta bort gammal Deployment och PVCs

Backup innan migration

# Ta snapshot av befintliga PVCs
kubectl get pvc -o yaml > pvc-backup.yaml

# Skapa volymsnapshots
kubectl apply -f volume-snapshot.yaml

Säkerhetsöverväganden

Lagringskryptering

Aktivera kryptering i vila med StorageClass-parametrar:

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

Åtkomstkontroll

Använd RBAC för att begränsa vem som kan skapa/ändra StatefulSets och 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"]

Nätverksregler

Begränsa pod-till-pod-kommunikation:

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

Prestandaoptimering

Lagringsprestandatier

Välj lämpliga StorageClasses baserat på arbetsbelastning:

  • Hög IOPS: Databaser med tung slumpmässig läsning/skrivning (gp3, io2)
  • Hög genomströmning: Loggaggregering, analys (st1, sc1)
  • Balanserad: Allmänna ändamål (gp3)

Pod-fördelning

Använd pod-anti-affinitet för att sprida StatefulSet-pods över tillgänglighetszoner:

spec:
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: mysql
            topologyKey: topology.kubernetes.io/zone

Resursbehov och gränser

Ange lämpliga resurser för konsekvent prestanda:

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
    ephemeral-storage: "10Gi"
  limits:
    cpu: "4"
    memory: "8Gi"
    ephemeral-storage: "20Gi"

Avancerade mönster

Statisk applikation med init-containers

Använd init-containers för databashantering:

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

Multi-container-pods för sidecars

Lägg till backup-sidecars eller övervakningsagenter:

spec:
  template:
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        # ... mysql-konfiguration ...
      - name: backup-sidecar
        image: mysql-backup:latest
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          readOnly: true

ConfigMaps för dynamisk konfiguration

Separera konfiguration från StatefulSet-definition:

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

Användbara länkar