Veröffentlicht am

Datenbanken auf Kubernetes: StatefulSets richtig nutzen

Teilen:
Authors

TL;DR

StatefulSets geben Pods stabile Hostnamen und persistenten Storage — genau das, was Datenbanken brauchen. Mit Headless Services, PersistentVolumeClaims und einem pg_dump-CronJob betreibst du PostgreSQL auf Kubernetes produktionsreif. Managed Services sind trotzdem oft die bessere Wahl, wenn dein Team klein ist.


Datenbanken auf Kubernetes mit StatefulSets

Datenbanken auf Kubernetes waren lange ein Tabu. Mittlerweile gibt es gute Gründe dafür — und gute Gründe dagegen. Dieser Guide zeigt dir, wie du PostgreSQL mit einem StatefulSet aufsetzt und wann du besser die Finger davon lässt.

Prüfe zuerst, ob dein Cluster StorageClasses bereitstellt:

kubectl get storageclasses
kubectl get pv

Ohne dynamisches Provisioning funktionieren StatefulSets nicht sinnvoll.

StatefulSet vs Deployment

Ein Deployment behandelt alle Pods als austauschbar. Für einen Webserver ist das perfekt. Für eine Datenbank ist es fatal.

EigenschaftDeploymentStatefulSet
Pod-NameZufällig (app-7f8d4)Stabil (postgres-0, postgres-1)
StartreihenfolgeParallelSequentiell (0, 1, 2...)
StorageShared oder keinerEigenes PVC pro Pod
DNSÜber Service-ClusterIPIndividuell pro Pod
LöschverhaltenPods beliebig ersetzbarPVCs bleiben erhalten

Der stabile Hostname ist entscheidend: postgres-0 bleibt immer postgres-0, auch nach einem Neustart. Replikation und Clustering setzen das voraus.

Headless Service einrichten

StatefulSets brauchen einen Headless Service (ohne ClusterIP). Dieser erzeugt DNS-Einträge für jeden einzelnen Pod:

apiVersion: v1
kind: Service
metadata:
  name: postgres-headless
  namespace: database
  labels:
    app: postgres
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
      name: postgresql

Damit erreichst du jeden Pod direkt: postgres-0.postgres-headless.database.svc.cluster.local.

PostgreSQL StatefulSet

Hier das vollständige StatefulSet mit PersistentVolumeClaim-Template:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: database
spec:
  serviceName: postgres-headless
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: appdb
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-credentials
                  key: password
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi
          livenessProbe:
            exec:
              command: ["pg_isready", "-U", "$(POSTGRES_USER)"]
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            exec:
              command: ["pg_isready", "-U", "$(POSTGRES_USER)"]
            initialDelaySeconds: 5
            periodSeconds: 5
  volumeClaimTemplates:
    - metadata:
        name: postgres-data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi

Wichtig ist volumeClaimTemplates: Jeder Pod bekommt ein eigenes PVC. Wird der Pod gelöscht, bleibt das PVC bestehen — die Daten überleben.

Erstelle vorher das Secret:

kubectl create namespace database

kubectl create secret generic postgres-credentials \
  -n database \
  --from-literal=username=appuser \
  --from-literal=password=$(openssl rand -base64 24)

Backup mit pg_dump CronJob

Ohne Backup ist deine Datenbank wertlos. Ein CronJob sichert PostgreSQL täglich:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
  namespace: database
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: pg-dump
              image: postgres:16-alpine
              command:
                - /bin/sh
                - -c
                - |
                  TIMESTAMP=$(date +%Y%m%d-%H%M%S)
                  pg_dump -h postgres-0.postgres-headless \
                    -U $PGUSER -d appdb \
                    --format=custom \
                    -f /backups/appdb-${TIMESTAMP}.dump
                  # Backups älter als 7 Tage löschen
                  find /backups -name "*.dump" -mtime +7 -delete
              env:
                - name: PGUSER
                  valueFrom:
                    secretKeyRef:
                      name: postgres-credentials
                      key: username
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: postgres-credentials
                      key: password
              volumeMounts:
                - name: backup-volume
                  mountPath: /backups
          volumes:
            - name: backup-volume
              persistentVolumeClaim:
                claimName: postgres-backups

Der CronJob läuft jeden Tag um 03:00 Uhr und behält Backups für 7 Tage.

Wann Kubernetes-Datenbanken Sinn machen

Nicht jede Datenbank gehört auf Kubernetes. Hier eine ehrliche Einschätzung:

Kubernetes eignet sich für Datenbanken, wenn:

  • Dein Team Kubernetes-Erfahrung hat und Operatoren versteht
  • Du eine einheitliche Deployment-Pipeline willst (GitOps)
  • Du On-Premises arbeitest ohne Zugang zu Managed Services
  • Dev/Staging-Umgebungen, die Production nachbilden sollen

Managed Services sind besser, wenn:

  • Dein Team keine Erfahrung mit Storage und StatefulSets hat
  • High Availability und automatische Failover nötig sind
  • Du in einer Cloud arbeitest (RDS, Cloud SQL, Azure Database)
  • Compliance-Anforderungen professionelle Wartung verlangen

Für Production auf Kubernetes empfehlen sich Operatoren wie CloudNativePG oder Zalando Postgres Operator — sie automatisieren Failover, Replikation und Backups.

FAQ

Überleben meine Daten einen Pod-Neustart?

Ja. Das PVC ist vom Pod-Lifecycle entkoppelt. Selbst wenn du den Pod löschst, bleibt das PersistentVolume mit den Daten bestehen. Erst wenn du das PVC explizit löschst, werden die Daten freigegeben.

Kann ich PostgreSQL auf mehrere Replicas skalieren?

Technisch ja — setze replicas: 3. Aber PostgreSQL unterstützt kein Multi-Master nativ. Du brauchst Streaming Replication oder einen Operator wie CloudNativePG, der Primary/Replica-Topologie automatisch verwaltet.

Was passiert bei einem Node-Ausfall?

Kubernetes startet den Pod auf einem anderen Node. Das PVC wird vom alten Node detached und am neuen Node attached. Bei Cloud-Storage (EBS, Persistent Disk) dauert das 1-3 Minuten. Bei lokalem Storage (local-path) gehen die Daten verloren.

Wie überwache ich die Datenbank-Performance?

Deploye den postgres-exporter als Sidecar-Container im StatefulSet. Er exportiert PostgreSQL-Metriken im Prometheus-Format. Kombiniere das mit Grafana-Dashboards für Verbindungen, Query-Laufzeiten und Cache-Hit-Ratios.


Nächster Schritt: Du brauchst Unterstützung bei der Migration deiner Datenbanken auf Kubernetes oder willst wissen, ob sich StatefulSets für deinen Use Case lohnen?

Legacy zu Kubernetes migrieren?

Wir begleiten Ihre Migration von VMs zu Containern – ohne Produktionsausfall. Erfahrung aus 50+ Migrationsprojekten.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen