Veröffentlicht am

Kubernetes Storage Performance: CSI-Treiber konfigurieren

Teilen:
Authors

Kubernetes Storage Performance: CSI-Treiber richtig konfigurieren

TL;DR

  • CSI-Treiber entkoppeln die Storage-Logik von Kubernetes und ermoeglichen Zugriff auf NVMe, Cloud-SSDs oder verteilte Systeme wie Ceph
  • Die StorageClass-Parameter volumeBindingMode: WaitForFirstConsumer und der richtige reclaimPolicy-Wert sind entscheidend fuer Performance und Datensicherheit
  • PostgreSQL profitiert massiv von ReadWriteOnce-Volumes auf lokalen NVMe-SSDs -- bis zu 3x hoehere TPS gegenueber Standard-Netzwerkspeicher
  • Benchmarkt euer Storage immer mit fio vor dem Produktiv-Einsatz, nicht erst wenn die Datenbank langsam ist
  • Monitoring ueber Prometheus mit dem csi-Exporter und node_disk_*-Metriken ist Pflicht

Warum CSI-Treiber den Unterschied machen

Kubernetes bringt von Haus aus keine Speicherlogik mit. Es delegiert alles an CSI-Treiber (Container Storage Interface). Das ist Absicht: ein offener Standard, der es Storage-Anbietern erlaubt, ihre Systeme direkt an Kubernetes anzubinden, ohne dass Upstream-Code geaendert werden muss.

In der Praxis heisst das: Die Performance eurer Datenbanken haengt nicht von Kubernetes ab, sondern davon, welchen CSI-Treiber ihr einsetzt und wie ihr ihn konfiguriert. Ein falsch konfigurierter CSI-Treiber kann euer NVMe-Backend auf HDD-Niveau ausbremsen.

Die drei gaengigsten Varianten im Ueberblick:

Storage-TypCSI-Treiber BeispieleIOPS (typisch)LatenzReplikationEinsatzbereich
Lokale NVMe-SSDTopoLVM, local-path500k+< 0.1 msKeine (App-Level)Hoechste I/O, Datenbanken
Cloud Block Storageaws-ebs-csi, azuredisk-csi16k-64k0.5-2 msProvider-seitigStandard-Workloads
Distributed StorageRook-Ceph, OpenEBS, Longhorn10k-100k1-5 msIntegriertHA-Anforderungen
Netzwerk-Dateisystemnfs-subdir-provisioner1k-10k5-50 msAbhaengigShared Read, Logs

Die Wahl haengt vom Workload-Profil ab. Ein OLTP-System wie PostgreSQL braucht niedrige Latenz und hohe Random-Read-IOPS. Ein Data Warehouse profitiert mehr von sequentiellem Durchsatz.

StorageClass: Die unterschaetzte Stellschraube

Die StorageClass ist das zentrale Konfigurationsobjekt. Hier definiert ihr nicht nur den Provisioner, sondern auch Performance-Tier, Caching-Verhalten und Volume-Binding.

Ein haeufiger Fehler: volumeBindingMode: Immediate in Multi-AZ-Setups. Das Volume wird sofort provisioniert, bevor der Pod gescheduled wird. Wenn der Pod dann in einer anderen Zone landet, muss er auf ein Remote-Volume zugreifen -- mit entsprechender Latenz.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: disk.csi.azure.com
parameters:
  skuName: PremiumV2_LRS
  cachingMode: None
  DiskIOPSReadWrite: "50000"
  DiskMBpsReadWrite: "250"
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
mountOptions:
  - noatime
  - nodiratime

Drei Punkte, die oft uebersehen werden:

cachingMode: None -- Fuer Datenbanken mit eigenem Buffer-Pool (PostgreSQL shared_buffers, MySQL InnoDB Buffer Pool) ist Host-Caching kontraproduktiv. Es fuehrt zu Double-Buffering und verschwendet RAM.

reclaimPolicy: Retain -- Im Produktivbetrieb wollt ihr nicht, dass ein versehentlich geloeschter PVC euer Volume mitreisst. Retain ist sicherer als Delete, erfordert aber manuelle Bereinigung.

mountOptions: noatime -- Spart bei leseintensiven Workloads messbar I/O, weil der Kernel nicht bei jedem Lesezugriff den Access-Timestamp aktualisiert.

PostgreSQL auf Kubernetes: PVC und Tuning

Fuer PostgreSQL kombiniert ihr die StorageClass mit einem passenden PVC und einem StatefulSet. Hier ein produktionsnahes Setup:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 200Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              cpu: "2"
              memory: "8Gi"
            limits:
              cpu: "4"
              memory: "16Gi"
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: postgres-data

Auf Anwendungsebene solltet ihr die PostgreSQL-Konfiguration an das Storage anpassen. Die wichtigsten Parameter:

  • shared_buffers: 25% des verfuegbaren RAMs (bei 16 Gi also 4 Gi)
  • effective_cache_size: 75% des RAMs
  • wal_buffers: 64 MB bei SSD-Storage
  • random_page_cost: 1.1 fuer SSDs (Standard ist 4.0, optimiert fuer HDDs)
  • effective_io_concurrency: 200 fuer NVMe, 2 fuer HDD

Der Unterschied zwischen random_page_cost: 4.0 (Default) und 1.1 ist enorm. Der Query Planner waehlt mit dem richtigen Wert deutlich haeufiger Index Scans statt Sequential Scans, was bei SSD-Storage schneller ist.

Storage Benchmarking mit fio

Bevor ihr eine Datenbank auf neuen Storage setzt, benchmarked ihr das Volume. Erstellt einen Pod mit fio:

kubectl run fio-bench --rm -it --restart=Never \
  --image=nixery.dev/shell/fio \
  --overrides='{
    "spec": {
      "containers": [{
        "name": "fio-bench",
        "image": "nixery.dev/shell/fio",
        "command": ["fio"],
        "args": [
          "--name=rand-rw",
          "--ioengine=libaio",
          "--direct=1",
          "--bs=8k",
          "--size=4G",
          "--numjobs=4",
          "--iodepth=32",
          "--rw=randrw",
          "--rwmixread=70",
          "--runtime=60",
          "--time_based",
          "--group_reporting",
          "--filename=/data/testfile"
        ],
        "volumeMounts": [{
          "name": "test-vol",
          "mountPath": "/data"
        }]
      }],
      "volumes": [{
        "name": "test-vol",
        "persistentVolumeClaim": {
          "claimName": "postgres-data"
        }
      }]
    }
  }'

Die Parameter simulieren ein typisches Datenbank-Workload-Profil: 8k Blockgroesse, 70% Reads, 30% Writes, Random-Zugriff mit 4 parallelen Jobs. Achtet auf drei Werte im Output:

  • IOPS (read + write): Sollte bei NVMe ueber 100k liegen
  • Latenz (clat, p99): Unter 1 ms fuer lokale SSDs, unter 5 ms fuer Cloud Block Storage
  • Durchsatz (BW): Mindestens 500 MB/s fuer sequentielle Workloads

Wenn die Werte deutlich unter euren Erwartungen liegen, prueft zuerst die StorageClass-Parameter, dann den CSI-Treiber-Log (kubectl logs -n kube-system -l app=csi-driver).

Monitoring: Engpaesse frueh erkennen

Storage-Probleme aeussern sich oft schleichend. Die Datenbank wird langsamer, aber niemand schaut auf die I/O-Metriken. Richtet euch mindestens diese Prometheus-Alerts ein:

  • node_disk_io_time_weighted_seconds_total steigt ueber 1.0 (Volume ist saturiert)
  • kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes ueber 85% (Volume laeuft voll)
  • PostgreSQL-spezifisch: pg_stat_bgwriter_buffers_backend steigt (Buffer Pool zu klein, direktes I/O)

Ergaenzt das mit Grafana-Dashboards, die IOPS, Latenz und Durchsatz pro PVC anzeigen. Das CSI-Driver-Monitoring variiert je nach Treiber, aber die meisten stellen Metriken ueber den Standard-/metrics-Endpoint bereit.

Distributed Storage: Wann Rook-Ceph Sinn ergibt

Nicht jeder Workload braucht lokale NVMe-Performance. Wenn euer primaeres Ziel Hochverfuegbarkeit ist und ihr keine Replikation auf Anwendungsebene betreiben wollt (oder koennt), ist Rook-Ceph eine solide Wahl.

Rook deployt Ceph als Kubernetes-Operator und nutzt die lokalen Disks der Worker Nodes als Storage-Backend. Das ergibt ein verteiltes, repliziertes Block-Storage-System, das aus Kubernetes-Sicht wie ein normaler CSI-Treiber funktioniert.

Die Trade-offs sind klar:

AspektLokale NVMeRook-Ceph
LatenzMinimal (< 0.1 ms)Moderat (1-5 ms)
IOPSMaximalGut, abhaengig von Disk-Anzahl
ReplikationKeine (App muss das loesen)2x oder 3x automatisch
FailoverManuell oder Operator-gesteuertAutomatisch
BetriebsaufwandGering (keine eigene Logik)Hoch (Ceph-Cluster managen)
Mindest-Nodes13 (fuer Replikation)

Fuer kleine Teams mit weniger als drei Ops-Ingenieuren ist Longhorn oft die bessere Alternative. Einfacher zu betreiben, weniger Konfiguration, und fuer mittelgrosse Workloads ausreichend performant.

Haeufige Fehler und wie ihr sie vermeidet

Fehler 1: Volume und Pod in verschiedenen Availability Zones. Passiert bei volumeBindingMode: Immediate. Der Fix: Immer WaitForFirstConsumer verwenden, damit das Volume in der gleichen Zone wie der Pod provisioniert wird.

Fehler 2: Kein Resource Limit auf dem Datenbank-Pod. Ohne Memory Limit kann der OOM-Killer zuschlagen und den Pod mit offenen Transaktionen toeten. Das fuehrt zu langen Recovery-Zeiten. Setzt immer explizite Limits.

Fehler 3: ext4 statt XFS fuer grosse Volumes. Ab ca. 1 TB zeigt XFS bessere Performance bei parallelen I/O-Operationen. Die meisten CSI-Treiber unterstuetzen fsType: xfs als Parameter in der StorageClass.

Fehler 4: Keine Volume Snapshots. CSI-Treiber unterstuetzen VolumeSnapshots. Nutzt sie fuer konsistente Backups vor Migrationen oder Upgrades. Das ist wesentlich schneller als ein vollstaendiges pg_dump.

Weitergehende Themen

Dieses Setup ist ein solides Fundament. Je nach Anforderung gibt es weitere Bereiche, die sich lohnen:

Wenn ihr Unterstuetzung bei der Storage-Architektur oder PostgreSQL-Performance-Optimierung auf Kubernetes braucht, meldet euch gerne fuer ein technisches Gespraech unter /kontakt.

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