22 StatefulSets: Stateful Workloads in Kubernetes

22.1 Einführung

StatefulSets sind eine spezialisierte Controller-Ressource zur Verwaltung zustandsbehafteter Pods. Während Deployments ideal für zustandslose Anwendungen sind, bieten StatefulSets die notwendigen Garantien für Workloads mit dauerhaftem Zustand, die persistente Netzwerk- und Speicherzuordnungen erfordern.

22.2 Was sind StatefulSets?

Ein StatefulSet verwaltet Pods mit stabiler Identität und zugeordnetem Speicher für zustandsbehaftete Anwendungen, die folgende Eigenschaften benötigen:

22.3 Unterschiede zu Deployments

Merkmal Deployment StatefulSet
Pod-Namen Zufällig generiert Vorhersagbar (name-0, name-1, etc.)
Pod-Reihenfolge Parallel Sequenziell
Speicher Ephemeral oder shared Persistent pro Pod
Netzwerk-Identität Unstabil Stabil und vorhersagbar
Updateverhalten Rolling, parallel Rolling, sequentiell (oder OnDelete)
Typische Anwendungen Zustandslose Apps Datenbanken, verteilte Systeme

Wichtiger Unterschied beim Pod-Management: Deployments ermöglichen automatisches Recreate von Pods mit neuen UIDs – StatefulSets hingegen erhalten die Pod-Identität durch fest zugewiesene PVCs und Hostnamen. Zwar werden Pods wie üblich neu erzeugt, behalten jedoch ihre logische Identität durch stabilen Namen und zugeordneten Speicher.

22.4 Kernkonzepte

22.4.1 Pod-Identität

StatefulSets weisen jedem Pod eine ordinale Identität zu: - myapp-0, myapp-1, myapp-2, etc. - Diese Namen bleiben über Pod-Lebenszyklen hinweg stabil - Bei einem Pod-Neustart erhält der neue Pod denselben Namen und dieselben PVCs

22.4.2 Headless Service

StatefulSets erfordern einen Headless Service (ClusterIP: None), der: - Direkte DNS-Auflösung zu einzelnen Pods ermöglicht - Stabile Netzwerk-Endpunkte bereitstellt - DNS-Namen im Format pod-name.service-name.namespace.svc.cluster.local erstellt - Keinen eigenen LoadBalancer oder ClusterIP bereitstellt, sondern direkten DNS-Zugriff auf einzelne Pod-IPs ermöglicht

22.4.3 Volume Claim Templates

StatefulSets verwenden Volume Claim Templates für persistenten Speicher: - Jeder Pod erhält sein eigenes PersistentVolumeClaim - Volumes überleben Pod-Neustarts - Beim Skalieren des StatefulSets werden für neu erzeugte Pods automatisch neue PVCs erstellt - Wichtig: PVCs werden nur einmal pro Pod erzeugt und nicht automatisch gelöscht – auch nach Herunterskalierung bestehen sie fort. Automatische Garbage Collection erfolgt nur bei expliziter PVC-Löschung. Ein manuelles Löschen ungenutzter PVCs nach Downscaling kann automatisiert werden, z.B. über CronJob/Controller.

PVC-Namensgebung: Der erzeugte PVC folgt dem Schema <claimName>-<statefulsetName>-<ordinal>, z.B. mysql-storage-mysql-0

22.5 Praktisches Beispiel: MySQL-Cluster

Hier ist ein vollständiges Beispiel für ein MySQL StatefulSet:

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  labels:
    app: mysql
spec:
  ports:
  - port: 3306
    name: mysql
  clusterIP: None
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
        - name: MYSQL_DATABASE
          value: "mydb"
        volumeMounts:
        - name: mysql-storage
          mountPath: /var/lib/mysql
        - name: config
          mountPath: /etc/mysql/conf.d
      volumes:
      - name: config
        configMap:
          name: mysql-config
  volumeClaimTemplates:
  - metadata:
      name: mysql-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
      storageClassName: fast-ssd

22.5.1 Zugehörige Ressourcen

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  password: cGFzc3dvcmQxMjM=  # base64 kodiert: "password123"
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  my.cnf: |
    [mysqld]
    server-id=1
    log-bin=mysql-bin
    binlog-format=row

22.6 Skalierung von StatefulSets

22.6.1 Hochskalierung

kubectl scale statefulset mysql --replicas=5

Verhalten: - Neue Pods werden sequenziell erstellt: mysql-3, dann mysql-4 - Jeder Pod wartet, bis der vorherige bereit ist - Neue PVCs werden automatisch erstellt

22.6.2 Herunterskalierung

kubectl scale statefulset mysql --replicas=2

Verhalten: - Pods werden in umgekehrter Reihenfolge gelöscht: mysql-4, dann mysql-3 - PVCs bleiben erhalten für mögliche spätere Wiederverwendung

22.7 Update-Strategien

Im Gegensatz zu Deployments erfolgen Updates nicht parallel, sondern geordnet gemäß Ordinalität der Pods.

22.7.1 Rolling Update (Standard)

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0

22.7.2 OnDelete

spec:
  updateStrategy:
    type: OnDelete

22.8 Überwachung und Debugging

22.8.1 Pod-Status prüfen

kubectl get pods -l app=mysql
kubectl describe statefulset mysql

22.8.2 PVC-Status überprüfen

kubectl get pvc
kubectl describe pvc mysql-storage-mysql-0

22.8.3 Logs analysieren

kubectl logs mysql-0
kubectl logs mysql-0 --previous  # Vorherige Pod-Instanz

22.9 Häufige Anwendungsfälle

22.9.1 Datenbank-Cluster

22.9.2 Messaging-Systeme

22.9.3 Verteilte Speichersysteme

22.10 Best Practices

22.10.1 Pod Disruption Budgets

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: mysql-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: mysql

Eviction Handling: Pod Disruption Budgets sind besonders wichtig für Eviction Handling durch Cluster-Autoscaler oder bei Node-Drains, da sie ungewollte Downtime verhindern.

22.10.2 Resource Limits setzen

resources:
  requests:
    memory: "1Gi"
    cpu: "500m"
  limits:
    memory: "2Gi"
    cpu: "1000m"

22.10.3 Readiness und Liveness Probes

livenessProbe:
  exec:
    command:
    - mysqladmin
    - ping
    - -h
    - localhost
  initialDelaySeconds: 30
  periodSeconds: 10
readinessProbe:
  exec:
    command:
    - mysql
    - -h
    - 127.0.0.1
    - -e
    - "SELECT 1"
  initialDelaySeconds: 5
  periodSeconds: 2

Kritischer Hinweis: Falsch konfigurierte Probes sind bei StatefulSets besonders kritisch, da ein einzelner fehlerhafter Pod nachfolgende Instanzen blockieren kann und die sequenzielle Natur der StatefulSets beeinträchtigt.

22.10.4 Storage Class Optimierung

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  fsType: ext4
  encrypted: "true"
volumeBindingMode: WaitForFirstConsumer

Volume Binding Mode: volumeBindingMode: WaitForFirstConsumer ist essenziell für Pod Scheduling bei StatefulSets mit lokalem Storage, da das Volume erst gebunden wird, wenn der Pod auf einem Node geplant wird.

22.11 Troubleshooting

22.11.1 Pod startet nicht

Symptom: Pod verbleibt im Status Pending oder Init

Diagnose:

kubectl describe pod mysql-0
kubectl get events --field-selector involvedObject.name=mysql-0

Häufige Ursachen und Lösungen: - PVC kann nicht gebunden werden → Storage Class und verfügbare PVs prüfen - Insufficient resources → Node-Kapazitäten überprüfen - Image pull errors → Registry-Zugriff und Image-Namen validieren - ConfigMap/Secret nicht verfügbar → Abhängigkeiten prüfen

22.11.2 Skalierung hängt fest

Symptom: Neue Pods werden nicht erstellt trotz Scale-Befehl

Diagnose:

kubectl get pod mysql-2 -o yaml
kubectl describe statefulset mysql

Mögliche Probleme und Lösungen: - Vorheriger Pod nicht bereit → Readiness Probe und Pod-Logs prüfen - Insufficient storage → PVC-Status und Storage Class überprüfen - Node capacity limits → Cluster-Ressourcen analysieren

22.11.3 Daten nach Pod-Restart verloren

Symptom: Persistente Daten sind nach Pod-Neustart nicht verfügbar

Diagnose und Überprüfung:

kubectl get pvc mysql-storage-mysql-0
kubectl describe pv <pv-name>

Lösungsansätze: - PVC korrekt gemountet? → Pod-Beschreibung und Mount-Points prüfen - Storage Class verfügbar? → StorageClass-Status validieren - Persistent Volume provisioniert? → PV-Status und Binding überprüfen

22.12 Erweiterte Funktionen

22.12.1 Parallel Pod Management

Für bestimmte Anwendungen kann parallele Pod-Verwaltung aktiviert werden:

spec:
  podManagementPolicy: Parallel

Wichtige Warnung: Verwenden Sie dies NICHT bei Clustern mit Start-Up-Dependencies (z.B. Master-Follower-Konfigurationen), da die Reihenfolge für ordnungsgemäße Initialisierung kritisch ist.

22.12.2 Custom Pod Management

StatefulSets können mit Operatoren erweitert werden für: - Automatisches Backup - Disaster Recovery - Advanced Health Checking - Custom Scaling Logic

22.12.3 Abgrenzung zu anderen Controllern

DaemonSets: Stellen sicher, dass genau ein Pod auf jedem Node läuft – häufig genutzt für Logging, Monitoring oder Netzwerkdienste.

Deployments: Verwalten zustandslose Anwendungen ohne persistente Identität oder Speicherzuordnung.

StatefulSets: Für zustandsbehaftete Anwendungen mit persistenter Identität, geordneter Pod-Verwaltung und stabilen Speicherzuordnungen – ideal für Datenbanken und Cluster-Services.

22.12.4 Volume Expansion

PVCs aus StatefulSets können – je nach StorageClass – nachträglich erweitert werden, z.B. durch Patchen des resources.requests.storage-Feldes. Der StorageClass-Treiber muss Volume Expansion (allowVolumeExpansion: true) unterstützen, was bei Cloud-Provisionern oft Standard ist. Dies ermöglicht dynamische Speichererweiterung ohne Pod-Neustart bei unterstützten Storage-Backends.

22.13 Sicherheitsaspekte

22.13.1 Network Policies

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: mysql-netpol
spec:
  podSelector:
    matchLabels:
      app: mysql
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: webapp
    ports:
    - protocol: TCP
      port: 3306

22.13.2 Pod Security Standards

spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
        fsGroup: 999
      containers:
      - name: mysql
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: false
          capabilities:
            drop:
            - ALL

Hinweis zu Pod Security: In Kubernetes ≥1.25 sind PodSecurityAdmission oder OPA Gatekeeper als Nachfolger der Pod Security Policies gebräuchlich.