Persistente opslag in Kubernetes: CSI, PV, StorageClass en StatefulSet
Kubernetes is geboren als platform voor staatloze workloads, maar de realiteit van applicaties onderneming en heel verschillend: databases, berichtenwachtrijen, persistente cachesystemen, gedeelde bestandssystemen. Ze vereisen allemaal opslag die de levenscyclus van een Pod overleeft. Beheer deze opslag op een betrouwbare, krachtige en draagbare manier tussen cloudproviders anders en een van de meest concrete uitdagingen van de dagelijkse productie.
In dit artikel verkennen we de gehele Kubernetes-opslaglaag: van Container Storage Interface (CSI) die de integratie met providers, PersistentVolume en StorageClass voor dynamische provisioning, tot StatefulSet voor beheer databases zoals PostgreSQL, Cassandra en Redis op Kubernetes.
Wat je gaat leren
- Het Kubernetes-opslagmodel: Volume, PersistentVolume, PersistentVolumeClaim
- Hoe de Container Storage Interface (CSI) werkt en de meest gebruikte stuurprogramma's
- StorageClass en dynamische inrichting: configuratie voor AWS EBS, GCE PD, Azure Disk
- Toegangsmodus: ReadWriteOnce, ReadOnlyMany, ReadWriteMany - wanneer gebruik je welke
- StatefulSet: stabiele identiteit, automatische PVC's, ordelijke rollende update
- PostgreSQL uitvoeren op Kubernetes met StatefulSet
- Back-up en herstel van PersistentVolumes met Velero
Het Kubernetes-opslagmodel
Kubernetes definieert een abstractiehiërarchie voor opslag die scheidt "wat nodig is" (PersistentVolumeClaim) van "zoals en verstrekt" (PersistentVolume en StorageClass). Dit stelt ontwikkelaars in staat om opslag aan te vragen zonder de details van de provider te kennen onderliggende wolk.
Primitieven voor opslag
| Bron | Bezems | Wie beheert het | Beschrijving |
|---|---|---|---|
| Volume | Peul | Ontwikkelaar | Kortstondige opslag gekoppeld aan de levenscyclus van de Pod |
| Aanhoudend volume (PV) | Clusters | Beheerder/Provisor | Opslagstuk in het cluster, levenscyclus onafhankelijk van Pods |
| PersistentVolumeClaim (PVC) | Naamruimte | Ontwikkelaar | Opslag van een Pod aanvragen |
| OpslagKlasse | Clusters | Beheerder | Definieert het opslagtype en de inrichting |
Levenscyclus van een persistent volume
De levenscyclus van een PV doorloopt verschillende fasen. Het is essentieel om ze te begrijpen probleemoplossing:
- Beschikbaar: De PV bestaat en is gratis, niet gekoppeld aan enige PVC
- Gebonden: De PV is verlijmd op een PVC dat aan de eisen voldoet
- Uitgegeven: Het PVC is geëlimineerd, maar de PV is nog niet beschikbaar (de gegevens zijn er nog steeds)
- Mislukt: De PV is niet automatisch teruggevorderd
# Verifica lo stato dei PersistentVolume nel cluster
kubectl get pv -o wide
# Output tipico:
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS
# pv-db-001 100Gi RWO Retain Bound production/postgres fast-ssd
# pv-db-002 100Gi RWO Retain Available fast-ssd
# Descrizione dettagliata di un PV
kubectl describe pv pv-db-001
# Verifica i PVC in un namespace
kubectl get pvc -n production
kubectl describe pvc postgres-data -n production
Containeropslaginterface (CSI)
Vóór CSI moest elke opslagprovider plug-ins onderhouden die in hun code waren ingebouwd Kubernetes-bron (plug-ins in de boomstructuur). Hierdoor ontstond een sterke en efficiënte koppeling moeilijk om plug-ins onafhankelijk van Kubernetes te updaten. De CSI lost dit op met een standaard gRPC-interface waarmee derden opslagstuurprogramma's kunnen maken als onafhankelijke Kubernetes Pods.
Belangrijkste CSI-chauffeurs
| CSI-chauffeurs | Aanbieders | Opslagtype | LeesSchrijfVeel |
|---|---|---|---|
| aws-ebs-csi-stuurprogramma | AWS | Blokkeren (gp3, io2) | No |
| aws-efs-csi-stuurprogramma | AWS | NFS (EFS) | Si |
| gce-pd-csi-stuurprogramma | GCP | Blok (pd-ssd, pd-gebalanceerd) | Nee (alleen RWX FileStore) |
| azuredisk-csi-stuurprogramma | Azuur | Blok (Premium SSD) | No |
| azurefile-csi-stuurprogramma | Azuur | NFS (Azure-bestanden) | Si |
| csi-rook-ceph | Rook/Cef | Blok/FS/Object | Ja (CephFS) |
| langhoorn | Ranchers | Blok verdeeld | Ja (met NFS) |
Installatie van het CSI EBS-stuurprogramma op EKS
# Installa il driver CSI EBS su Amazon EKS
# Prima, crea un IAM Role con le policy necessarie
aws eks create-addon \
--cluster-name my-cluster \
--addon-name aws-ebs-csi-driver \
--service-account-role-arn arn:aws:iam::ACCOUNT_ID:role/EBSCSIRole
# Verifica il daemonset del driver CSI
kubectl get daemonset -n kube-system ebs-csi-node
kubectl get deployment -n kube-system ebs-csi-controller
StorageClass: dynamische inrichting
Statische provisioning (het handmatig aanmaken van PV's) is onpraktisch in de productie. Met dynamische provisioning, Kubernetes creëert automatisch de PV wanneer er een PVC komt gemaakt met behulp van het CSI-stuurprogramma dat is geconfigureerd in de StorageClass.
StorageClass voor AWS EBS
# storage-classes-aws.yaml
# StorageClass per dischi gp3 (performance ottimale)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer # IMPORTANTE: evita cross-AZ mounting
reclaimPolicy: Retain # Protegge i dati in produzione
allowVolumeExpansion: true
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
kmsKeyId: "arn:aws:kms:eu-west-1:ACCOUNT:key/KEY_ID"
---
# StorageClass per io2 (database ad alto IOPS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ultra-fast-ssd
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
parameters:
type: io2
iops: "32000"
encrypted: "true"
---
# StorageClass per EFS (ReadWriteMany, NFS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: shared-storage
provisioner: efs.csi.aws.com
reclaimPolicy: Retain
parameters:
provisioningMode: efs-ap
fileSystemId: fs-XXXXXXXX
directoryPerms: "700"
StorageClass voor GKE (Google Kubernetes Engine)
# storage-classes-gke.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd-gke
provisioner: pd.csi.storage.gke.io
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true
parameters:
type: pd-ssd
replication-type: regional-pd # replica su 2 zone
availability-class: regional-hard-failover
volumeBindingMode: Waarom WaitForFirstConsumer
Altijd gebruiken WaitForFirstConsumer in plaats van Immediate wanneer
het cluster heeft meerdere Beschikbaarheidszones. Met Immediate, wordt de PV aangemaakt
in de zone waar de provisioner is gepland, wat kan verschillen van de zone
waar de Pod zal worden gepland. Resultaat: De Pod kan het volume niet activeren.
WaitForFirstConsumer creëert de PV in hetzelfde gebied als de Pod.
PersistentVolumeClaim in de praktijk
PVC en hoe een Pod moet worden bewaard. De PVC specificeert de grootte en de toegangsmodus en OpslagKlasse. Kubernetes vindt of creëert een compatibele PV en bindt deze aan de PVC.
# pvc-database.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: production
labels:
app: postgres
tier: database
annotations:
# Snapshot policy (con alcuni storage providers)
storageclass.kubernetes.io/is-default-class: "false"
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
# Espandi a 200Gi in futuro con:
# kubectl patch pvc postgres-data -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'
Toegangsmodus: kies de juiste
| Toegangsmodus | Afkorting | Wat betekent het | Typisch gebruik |
|---|---|---|---|
| LeesSchrijfOnce | RWO | Slechts één lees-/schrijfknooppunt | Databases, applicaties met één exemplaar |
| ReadOnlyMany | ROX | Veel alleen-lezen knooppunten | Gedeelde statische gegevens, configuraties |
| LeesSchrijfVeel | RWX | Veel lees-/schrijfknooppunten | Gedeelde NFS, uploadhandler |
| ReadWriteOncePod | RWOP | Slechts één lees-/schrijfpod | Exclusieve opslag voor enkele Pod (K8s 1.29+) |
StatefulSet: werklast met stabiele identiteit
Implementaties zijn geweldig voor stateless workloads, maar ook voor databases en applicaties vereisen een stabiele identiteit (voorspelbare hostnaam, speciaal volume, opstartvolgorde) serveert het StatefulSet. De belangrijkste verschillen vergeleken met implementaties:
- Stabiele identiteit: De Pods hebben voorspelbare namen:
myapp-0,myapp-1,myapp-2 - Opstartvolgorde: Pods worden in de juiste volgorde gestart (0, vervolgens 1 en vervolgens 2) en in omgekeerde volgorde uitgeschakeld
- Speciale PVC's: Elke Pod krijgt zijn eigen PVC-via
volumeClaimTemplates - Dienst zonder hoofd: Elke Pod heeft een stabiele DNS-invoer:
myapp-0.myapp.namespace.svc.cluster.local
PostgreSQL op Kubernetes met StatefulSet
Hier is een complete en productieklare installatie voor PostgreSQL met StatefulSet, inclusief ConfigMap voor configuratie, geheim voor inloggegevens en headless service:
# postgres-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: production
labels:
app: postgres
spec:
ports:
- port: 5432
name: postgres
clusterIP: None # Headless Service - abilita il DNS per Pod individuali
selector:
app: postgres
---
# Service per accesso al master (read/write)
apiVersion: v1
kind: Service
metadata:
name: postgres-master
namespace: production
spec:
ports:
- port: 5432
selector:
app: postgres
role: master
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: production
spec:
serviceName: postgres # deve corrispondere al nome del headless Service
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
# Anti-affinity: distribuisce le repliche su nodi diversi
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: postgres
topologyKey: kubernetes.io/hostname
initContainers:
# init container per configurare i permessi del volume
- name: init-postgres
image: postgres:16
command:
- bash
- "-c"
- |
chown -R 999:999 /var/lib/postgresql/data
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_DB
value: myapp
- 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
- name: postgres-config
mountPath: /etc/postgresql/postgresql.conf
subPath: postgresql.conf
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
livenessProbe:
exec:
command:
- pg_isready
- -U
- $(POSTGRES_USER)
- -d
- $(POSTGRES_DB)
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- pg_isready
- -U
- $(POSTGRES_USER)
- -d
- $(POSTGRES_DB)
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: postgres-config
configMap:
name: postgres-config
# PVC template: ogni Pod ottiene il proprio volume da 100Gi
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
ConfigMap voor PostgreSQL-configuratie
# postgres-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
namespace: production
data:
postgresql.conf: |
# Performance tuning per 4GB RAM
shared_buffers = 1GB
work_mem = 64MB
maintenance_work_mem = 256MB
effective_cache_size = 3GB
# WAL settings
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1GB
# Checkpoint
checkpoint_completion_target = 0.9
max_wal_size = 4GB
min_wal_size = 1GB
# Connection settings
max_connections = 200
# Logging
log_min_duration_statement = 1000 # log query lente >1s
log_checkpoints = on
log_connections = on
log_disconnections = on
Volumemomentopname en back-up
Met Volume Snapshots kunt u point-in-time back-ups van PersistentVolumes maken met behulp van de eigen mogelijkheden van de cloudprovider (EBS-snapshot, GCE PD-snapshot, enz.).
# Installa le CRD per Volume Snapshot (se non presenti)
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
# VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: ebs-snapshot-class
driver: ebs.csi.aws.com
deletionPolicy: Retain
---
# Crea uno snapshot del volume di PostgreSQL
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgres-snapshot-20260801
namespace: production
spec:
volumeSnapshotClassName: ebs-snapshot-class
source:
persistentVolumeClaimName: postgres-data-postgres-0
---
# Verifica lo stato dello snapshot
kubectl get volumesnapshot -n production
# Restore da snapshot: crea un nuovo PVC da snapshot
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data-restored
namespace: production
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
dataSource:
name: postgres-snapshot-20260801
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
Velero voor volledige clusterback-up
Velero is de referentietool voor back-up en herstel van volledige Kubernetes-clusters, inclusief persistente volumes:
# Installa Velero con il plugin EBS
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.8.0 \
--bucket my-velero-backups \
--backup-location-config region=eu-west-1 \
--snapshot-location-config region=eu-west-1 \
--secret-file ./credentials-velero
# Crea un backup del namespace production con i volume
velero backup create production-backup-20260801 \
--include-namespaces production \
--snapshot-volumes \
--wait
# Verifica il backup
velero backup describe production-backup-20260801
velero backup logs production-backup-20260801
# Schedule: backup giornaliero a mezzanotte
velero schedule create daily-production \
--schedule="0 0 * * *" \
--include-namespaces production \
--snapshot-volumes \
--ttl 720h # mantieni per 30 giorni
# Restore in un nuovo cluster
velero restore create --from-backup production-backup-20260801 \
--namespace-mappings production:production-restored
Opslag voor AI/ML-workloads
Voor ML-trainingsworkloads gelden speciale opslagvereisten: parallelle toegang hoge doorvoer voor grote datasets, vaak van meerdere GPU-werknemers tegelijk.
# PVC con ReadWriteMany per training distribuito
# Usa EFS (AWS) o CephFS (on-premise) per RWX
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ml-dataset-storage
namespace: ml-training
spec:
accessModes:
- ReadWriteMany
storageClassName: shared-storage # EFS o CephFS
resources:
requests:
storage: 10Ti # 10TB per dataset ImageNet, etc.
---
# Job di training che accede ai dati in parallelo
apiVersion: batch/v1
kind: Job
metadata:
name: distributed-training
namespace: ml-training
spec:
parallelism: 8 # 8 worker GPU in parallelo
completions: 8
template:
spec:
containers:
- name: trainer
image: pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime
resources:
limits:
nvidia.com/gpu: "1"
volumeMounts:
- name: dataset
mountPath: /data
readOnly: true # tutti i worker leggono, nessuno scrive
- name: checkpoints
mountPath: /checkpoints
volumes:
- name: dataset
persistentVolumeClaim:
claimName: ml-dataset-storage
readOnly: true
- name: checkpoints
persistentVolumeClaim:
claimName: ml-checkpoints-rwx # RWX per checkpoint condivisi
Best practices voor Kubernetes-opslag
Controlelijst voor productieopslag
- Gebruik altijd reclaimPolicy: Retain voor productiegegevens.
Deleteverwijdert gegevens automatisch wanneer PVC wordt verwijderd - volumeBindingMode: WaitForFirstConsumer: Voorkom cross-AZ-bindingsproblemen in clusters met meerdere zones
- allowVolumeExpansion: waar: Configureer StorageClasses om volume-uitbreiding zonder downtime mogelijk te maken
- Controleer schijfgebruik: Configureer waarschuwingen op Prometheus wanneer een PVC de capaciteit van 80% overschrijdt
- Automatische momentopnamen: Configureer VolumeSnapshotClass en geplande back-ups
- Test het herstel: Een ongeteste en nutteloze back-up. Voer maandelijkse hersteltests uit
- Scheid PVC's per rol: Eén PVC voor gegevens, één voor logboeken, één voor tijdelijke back-ups
- StatefulSet met anti-affiniteit: Distribueer replica's over verschillende knooppunten en zones
Antipatroon: doe dit niet
- Gebruik hostPath niet in productie: Verbindt de Pod met een specifiek knooppunt en is niet draagbaar
- Gebruik emptyDir niet voor persistente gegevens: Het verdwijnt wanneer de Pod opnieuw wordt opgestart
- Gebruik reclaimPolicy niet: Verwijder voor productiegegevens: Je kunt per ongeluk alles verliezen
- Monteer niet hetzelfde PVC (RWO) op meerdere Pods: Veroorzaakt datacorruptie
Opslagbewaking met Prometheus
# Metriche chiave da monitorare con kube-state-metrics
# Aggiungi alert a Prometheus
# Alert: PVC vicino alla capacita massima
groups:
- name: kubernetes-storage
rules:
- alert: PVCStorageUsageHigh
expr: |
kubelet_volume_stats_used_bytes /
kubelet_volume_stats_capacity_bytes > 0.80
for: 5m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.persistentvolumeclaim }} e all'80% della capacita"
description: "Namespace: {{ $labels.namespace }}"
- alert: PVCStorageFull
expr: |
kubelet_volume_stats_used_bytes /
kubelet_volume_stats_capacity_bytes > 0.95
for: 2m
labels:
severity: critical
annotations:
summary: "PVC {{ $labels.persistentvolumeclaim }} quasi piena!"
- alert: PVCNotBound
expr: |
kube_persistentvolumeclaim_status_phase{phase="Pending"} == 1
for: 10m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.persistentvolumeclaim }} in stato Pending da 10 minuti"
Conclusies en volgende stappen
Kubernetes-opslag is een van de meest kritische lagen voor bedrijfsapplicaties productie. De Container Storage Interface standaardiseert de integratie met elk ander platform provider, dynamische provisioning met StorageClass elimineert handmatig werk, bijv StatefulSets bieden de primitieven die nodig zijn om databases met stabiele identiteiten te beheren.
De sleutel tot robuuste opslag in de productie is een combinatie van architectonische keuzes correct (reclaimPolicy Retain, WaitForFirstConsumer, anti-affiniteit), proactieve monitoring met Prometheus en een regelmatig geteste back-upstrategie met Velero of VolumeSnapshot.







