Trvalé úložiště v Kubernetes: CSI, PV, StorageClass a StatefulSet
Kubernetes se zrodil jako platforma pro bezstavovou zátěž, ale realitu aplikací podnikové a velmi odlišné: databáze, fronty zpráv, perzistentní cache systémy, sdílené systémy souborů. Všechny vyžadují úložiště, které přežije životní cyklus podu. Spravujte toto úložiště spolehlivým, vysoce výkonným a přenosným způsobem mezi poskytovateli cloudu odlišná a jedna z nejkonkrétnějších výzev každodenní výroby.
V tomto článku prozkoumáme celou vrstvu úložiště Kubernetes: z kontejnerového úložiště Rozhraní (CSI), které standardizuje integraci s poskytovateli, PersistentVolume a StorageClass pro dynamické zřizování, až StatefulSet pro spravovat databáze jako PostgreSQL, Cassandra a Redis na Kubernetes.
Co se naučíte
- Model úložiště Kubernetes: Volume, PersistentVolume, PersistentVolumeClaim
- Jak funguje rozhraní CSI (Container Storage Interface) a nejpoužívanější ovladače
- StorageClass a dynamické zřizování: konfigurace pro AWS EBS, GCE PD, Azure Disk
- Režim přístupu: ReadWriteOnce, ReadOnlyMany, ReadWriteMany – kdy použít který
- StatefulSet: stabilní identita, automatické PVC, řádná průběžná aktualizace
- Jak spustit PostgreSQL na Kubernetes s StatefulSet
- Zálohování a obnova PersistentVolumes pomocí Velero
Model úložiště Kubernetes
Kubernetes definuje hierarchii abstrakce pro úložiště, která odděluje „co je potřeba“ (PersistentVolumeClaim) z „jak a poskytnuto“ (PersistentVolume a StorageClass). Toto umožňuje vývojářům požadovat úložiště, aniž by znali podrobnosti o poskytovateli podkladový mrak.
Skladovací primitiva
| Zdroj | Košťata | Kdo to řídí | Popis |
|---|---|---|---|
| Objem | Lusk | Vývojář | Pomíjivé úložiště spojené s životním cyklem Pod |
| Trvalý objem (PV) | Shluky | Správce / Poskytovatel | Úložný kus v clusteru, životní cyklus nezávislý na Pods |
| PersistentVolumeClaim (PVC) | Jmenný prostor | Vývojář | Požadavek na úložiště z podu |
| StorageClass | Shluky | Admin | Definuje "typ" úložiště a poskytovatele |
Životní cyklus PersistentVolume
Životní cyklus FV prochází různými stavy. Jejich pochopení je zásadní odstraňování problémů:
- K dispozici: PV existuje a je zdarma, není spojen s žádným PVC
- Vázaný: PV byl nalepen na PVC, který splňuje požadavky
- Vydáno: PVC bylo odstraněno, ale PV ještě není k dispozici (data stále existují)
- Selhalo: FV selhalo při automatickém obnovení
# 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
Rozhraní kontejnerového úložiště (CSI)
Před CSI musel každý poskytovatel úložiště udržovat pluginy zabudované do svého kódu Zdroj Kubernetes (in-tree pluginy). To vytvořilo silné a účinné spojení obtížné aktualizovat pluginy nezávisle na Kubernetes. CSI to řeší se standardním rozhraním gRPC, které umožňuje třetím stranám vytvářet ovladače úložiště jako nezávislé Kubernetes Pods.
Hlavní ovladače CSI
| Ovladače CSI | Poskytovatelé | Typ úložiště | ReadWriteMany |
|---|---|---|---|
| aws-ebs-csi-ovladač | AWS | Blokovat (gp3, io2) | No |
| aws-efs-csi-ovladač | AWS | NFS (EFS) | Si |
| gce-pd-csi-ovladač | GCP | Blok (pd-ssd, pd-balanced) | Ne (pouze RWX FileStore) |
| azuredisk-csi-ovladač | Blankyt | Blokovat (Premium SSD) | No |
| azurefile-csi-ovladač | Blankyt | NFS (soubory Azure) | Si |
| csi-věža-ceph | Rook/Ceph | Blok/FS/Objekt | Ano (CephFS) |
| longhorn | Rančeři | Blok distribuován | Ano (s NFS) |
Instalace ovladače CSI EBS na 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: Dynamic Provisioning
Statické zajišťování (ruční vytváření PV) je ve výrobě nepraktické. s dynamické poskytování, Kubernetes automaticky vytvoří PV, když přijde PVC vytvořené pomocí ovladače CSI nakonfigurovaného v StorageClass.
StorageClass pro 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 pro 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: Proč WaitForFirstConsumer
Vždy používejte WaitForFirstConsumer místo Immediate kdy
cluster má více zón dostupnosti. S Immediate, je vytvořen PV
v zóně, kde je naplánován poskytovatel, která se může od zóny lišit
kde bude modul naplánován. Výsledek: Podu se nepodařilo připojit svazek.
WaitForFirstConsumer vytváří PV ve stejné oblasti jako Pod.
PersistentVolumeClaim v praxi
PVC a jak Pod vyžaduje skladování. PVC určuje velikost, režim přístupu a StorageClass. Kubernetes najde nebo vytvoří kompatibilní PV a naváže ho na 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"}}}}'
Režim přístupu: Vyberte ten správný
| Režim přístupu | Zkratka | co to znamená | Typické použití |
|---|---|---|---|
| ReadWriteOnce | RWO | Pouze jeden uzel pro čtení/zápis | Databáze, jednoinstanční aplikace |
| ReadOnlyMany | ROX | Mnoho uzlů pouze pro čtení | Sdílená statická data, konfigurace |
| ReadWriteMany | RWX | Mnoho uzlů pro čtení/zápis | Shared NFS, upload handler |
| ReadWriteOncePod | RWOP | Pouze jeden modul pro čtení/zápis | Exkluzivní úložiště pro jeden modul (K8s 1.29+) |
StatefulSet: Pracovní zátěž se stabilní identitou
Nasazení jsou skvělá pro bezstavovou zátěž, ale pro databáze a aplikace vyžadují stabilní identitu (předvídatelný název hostitele, vyhrazený svazek, pořadí spouštění) slouží to StatefulSet. Klíčové rozdíly oproti nasazení:
- Stabilní identita: Pody mají předvídatelná jména:
myapp-0,myapp-1,myapp-2 - Pořadí spouštění: Pody se spouštějí v pořadí (0, pak 1, pak 2) a vypínají se v opačném pořadí
- Vyhrazené PVC: Každý modul má svůj vlastní průchod z PVC
volumeClaimTemplates - Bezhlavá služba: Každý modul má stabilní záznam DNS:
myapp-0.myapp.namespace.svc.cluster.local
PostgreSQL na Kubernetes se StatefulSet
Zde je kompletní a produkčně připravené nastavení pro PostgreSQL se StatefulSet, včetně ConfigMap pro konfiguraci, Secret pro přihlašovací údaje a 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 pro konfiguraci PostgreSQL
# 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
Snímek a záloha svazku
Snímky svazků umožňují vytvářet zálohy PersistentVolumes k určitému časovému okamžiku pomocí nativních možností poskytovatele cloudu (snímek EBS, snímek GCE PD atd.).
# 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 pro kompletní zálohování clusteru
Velero je referenční nástroj pro zálohování a obnovu celých clusterů Kubernetes, včetně PersistentVolumes:
# 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
Úložiště pro pracovní zátěže AI/ML
Tréninkové úlohy ML mají speciální požadavky na úložiště: paralelní přístup vysoká propustnost pro velké datové sady, často od více pracovníků GPU současně.
# 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
Doporučené postupy pro úložiště Kubernetes
Kontrolní seznam produkčního úložiště
- Vždy používejte reclaimPolicy: Zachovat pro výrobní data.
Deleteautomaticky vymaže data při smazání PVC - volumeBindingMode: WaitForFirstConsumer: Vyhněte se problémům s vazbou mezi AZ ve vícezónových clusterech
- allowVolumeExpansion: true: Nakonfigurujte StorageClasses, abyste umožnili rozšíření svazku bez prostojů
- Sledujte využití disku: Nakonfigurujte upozornění na Prometheus, když PVC překročí 80 % kapacity
- Automatické snímky: Nakonfigurujte VolumeSnapshotClass a plánované zálohování
- Otestujte obnovu: Nevyzkoušená a nepoužitelná záloha. Provádějte měsíční testy obnovy
- Oddělte PVC podle rolí: Jeden PVC pro data, jeden pro protokoly, jeden pro dočasné zálohy
- StatefulSet s anti-afinitou: Distribuujte repliky mezi různé uzly a zóny
Anti-Pattern: Nedělejte to
- Nepoužívejte hostPath v produkci: Připojí modul ke konkrétnímu uzlu a není přenosný
- Nepoužívejte emptyDir pro trvalá data: Vymaže se, když se modul restartuje
- Nepoužívejte reclaimPolicy: Smazat pro produkční data: Omylem můžete všechno ztratit
- Nemontujte stejné PVC (RWO) na více podů: Způsobuje poškození dat
Sledování úložiště s 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"
Závěry a další kroky
Úložiště Kubernetes je jednou z nejkritičtějších vrstev pro podnikové aplikace výroby. Rozhraní kontejnerového úložiště standardizuje integraci s jakýmkoliv dynamické poskytování s StorageClass eliminuje manuální práci, např StatefulSets poskytují primitiva nezbytná pro správu databází se stabilními identitami.
Klíčem k robustnímu skladování ve výrobě je kombinace architektonických možností správné (reclaimPolicy Retain, WaitForFirstConsumer, anti-affinity), proaktivní monitorování s Prometheus a pravidelně testovaná strategie zálohování s Velero nebo VolumeSnapshot.







