Stocare persistentă în Kubernetes: CSI, PV, StorageClass și StatefulSet
Kubernetes s-a născut ca o platformă pentru sarcinile de lucru fără stat, dar realitatea aplicațiilor întreprinderi și foarte diferite: baze de date, cozi de mesaje, sisteme cache persistente, sisteme de fișiere partajate. Toate necesită stocare care să supraviețuiască ciclului de viață al unui Pod. Gestionați această stocare într-un mod fiabil, de înaltă performanță și portabil între furnizorii de cloud diferite și una dintre cele mai concrete provocări ale producției de zi cu zi.
În acest articol vom explora întregul strat de stocare Kubernetes: de la Container Storage Interfață (CSI) care standardizează integrarea cu furnizorii, PersistentVolume și StorageClass pentru aprovizionare dinamică, până la StatefulSet pentru gestionați baze de date precum PostgreSQL, Cassandra și Redis pe Kubernetes.
Ce vei învăța
- Modelul de stocare Kubernetes: Volume, PersistentVolume, PersistentVolumeClaim
- Cum funcționează Container Storage Interface (CSI) și cele mai utilizate drivere
- StorageClass și aprovizionare dinamică: configurație pentru AWS EBS, GCE PD, Azure Disk
- Mod de acces: ReadWriteOnce, ReadOnlyMany, ReadWriteMany - când să folosiți care
- StatefulSet: identitate stabilă, PVC-uri automate, actualizare ordonată
- Cum să rulați PostgreSQL pe Kubernetes cu StatefulSet
- Backup și restaurare de PersistentVolumes cu Velero
Modelul de stocare Kubernetes
Kubernetes definește o ierarhie de abstractizare pentru stocare care separă „ceea ce este necesar” (PersistentVolumeClaim) de la „as și furnizat” (PersistentVolume și StorageClass). Aceasta permite dezvoltatorilor să solicite stocare fără a cunoaște detaliile furnizorului nor subiacent.
Primitive de stocare
| Resursă | Mături | Cine o gestionează | Descriere |
|---|---|---|---|
| Volum | Pod | Dezvoltator | Stocare efemeră legată de ciclul de viață al Podului |
| Volum persistent (PV) | Clustere | Administrator / Furnizor | Piesă de stocare în cluster, ciclul de viață independent de Pod-uri |
| PersistentVolumeClaim (PVC) | Spațiu de nume | Dezvoltator | Se solicită stocare de la un Pod |
| StorageClass | Clustere | Admin | Definește „tipul” de stocare și furnizorul |
Ciclul de viață al unui volum persistent
Ciclul de viață al unui PV trece prin diferite stări. Înțelegerea lor este esențială depanare:
- Disponibil: PV există și este gratuit, nu este asociat cu niciun PVC
- Legat: PV a fost lipit de un PVC care îndeplinește cerințele
- Lansat: PVC-ul a fost eliminat, dar PV nu este încă disponibil (datele sunt încă acolo)
- A eșuat: PV nu a reușit recuperarea automată
# 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
Interfață de stocare a containerelor (CSI)
Înainte de CSI, fiecare furnizor de stocare trebuia să mențină pluginuri încorporate în codul lor Sursă Kubernetes (pluginuri în arbore). Acest lucru a creat o cuplare puternică și eficientă este dificil de actualizat pluginurile independent de Kubernetes. CSI rezolvă asta cu o interfață standard gRPC care permite terților să creeze drivere de stocare ca poduri Kubernetes independente.
Principalele drivere CSI
| Drivere CSI | Furnizorii | Tip de depozitare | CiteșteScrieMulte |
|---|---|---|---|
| aws-ebs-csi-driver | AWS | Blocare (gp3, io2) | No |
| aws-efs-csi-driver | AWS | NFS (EFS) | Si |
| gce-pd-csi-driver | GCP | Blocare (pd-ssd, pd-echilibrat) | Nu (numai RWX FileStore) |
| azuredisk-csi-driver | Azur | Blocare (SSD premium) | No |
| azurefile-csi-driver | Azur | NFS (Fișiere Azure) | Si |
| csi-rook-ceph | Rook/Ceph | Bloc/FS/Obiect | Da (CephFS) |
| longhorn | Fermierii | Bloc distribuit | Da (cu NFS) |
Instalarea driverului CSI EBS pe 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: Aprovizionare dinamică
Aprovizionarea statică (crearea manuală a PV) este nepractică în producție. Cu furnizare dinamică, Kubernetes creează automat PV atunci când vine un PVC creat, folosind driverul CSI configurat în StorageClass.
StorageClass pentru 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 pentru 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: De ce WaitForFirstConsumer
Utilizați întotdeauna WaitForFirstConsumer în loc de Immediate când
clusterul are mai multe zone de disponibilitate. Cu Immediate, PV este creat
în zona în care este programat furnizorul, care poate fi diferită de zonă
unde va fi programat Podul. Rezultat: Podul nu reușește să monteze volumul.
WaitForFirstConsumer creează PV în aceeași zonă cu Podul.
PersistentVolumeClaim în practică
PVC și modul în care un Pod necesită depozitare. PVC-ul specifică dimensiunea, modul de acces și StorageClass. Kubernetes găsește sau creează un PV compatibil și îl leagă 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"}}}}'
Mod de acces: Alegeți cel potrivit
| Mod de acces | Abreviere | Ce înseamnă | Utilizare tipică |
|---|---|---|---|
| ReadWriteOnce | RWO | Un singur nod de citire/scriere | Baze de date, aplicații cu o singură instanță |
| ReadOnlyMany | ROX | Multe noduri numai pentru citire | Date statice partajate, configurații |
| CiteșteScrieMulte | RWX | Multe noduri de citire/scriere | NFS partajat, handler de încărcare |
| ReadWriteOncePod | RWOP | Un singur Pod de citire/scriere | Stocare exclusivă pentru un singur Pod (K8s 1.29+) |
StatefulSet: sarcină de lucru cu identitate stabilă
Implementările sunt excelente pentru sarcinile de lucru fără stat, dar pentru baze de date și aplicații care necesită o identitate stabilă (nume de gazdă previzibil, volum dedicat, ordine de pornire) o serveste StatefulSet. Diferențele cheie în comparație cu implementările:
- Identitate stabilă: Podurile au nume previzibile:
myapp-0,myapp-1,myapp-2 - Ordinea de pornire: Podurile sunt pornite în ordine (0, apoi 1, apoi 2) și se închid în ordine inversă
- PVC-uri dedicate: Fiecare Pod primește propriul său PVC prin
volumeClaimTemplates - Serviciu fără cap: Fiecare Pod are o intrare DNS stabilă:
myapp-0.myapp.namespace.svc.cluster.local
PostgreSQL pe Kubernetes cu StatefulSet
Iată o configurare completă și gata de producție pentru PostgreSQL cu StatefulSet, inclusiv ConfigMap pentru configurare, Secret pentru acreditări și serviciu fără cap:
# 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 pentru Configurare 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
Captură de volum și copie de rezervă
Instantaneele de volum vă permit să creați copii de rezervă punctuale ale volumelor persistente folosind capabilitățile native ale furnizorului de cloud (instantaneu EBS, instantaneu GCE PD etc.).
# 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 pentru backup complet al clusterului
Velero este instrumentul de referință pentru backup și restaurare a întregului cluster Kubernetes, inclusiv 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
Stocare pentru încărcături de lucru AI/ML
Sarcinile de lucru de antrenament ML au cerințe speciale de stocare: acces paralel debit mare pentru seturi mari de date, adesea de la mai mulți lucrători GPU simultan.
# 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
Cele mai bune practici pentru stocarea Kubernetes
Lista de verificare a stocării producției
- Utilizați întotdeauna reclaimPolicy: Retain pentru datele de producție.
Deleteșterge automat datele atunci când PVC-ul este șters - volumBindingMode: WaitForFirstConsumer: Evitați problemele de legare între AZ în clustere cu mai multe zone
- allowVolumeExpansion: true: Configurați StorageClasses pentru a permite extinderea volumului fără timp de nefuncționare
- Monitorizați utilizarea discului: Configurați alerte pe Prometheus atunci când un PVC depășește capacitatea de 80%.
- Instantanee automate: Configurați VolumeSnapshotClass și backup-urile programate
- Testați restaurarea: O copie de rezervă netestată și inutilă. Efectuați lunar teste de restaurare
- Separați PVC-urile după rol: Un PVC pentru date, unul pentru jurnal, unul pentru backup temporar
- StatefulSet cu anti-afinitate: Distribuiți replici în diferite noduri și zone
Anti-Pattern: Nu face asta
- Nu utilizați hostPath în producție: Leagă podul de un anumit nod și nu este portabil
- Nu utilizați emptyDir pentru date persistente: Se șterge când Pod-ul este repornit
- Nu utilizați reclaimPolicy: ștergeți pentru datele de producție: Poți pierde totul din greșeală
- Nu montați același PVC (RWO) pe mai multe poduri: Provoacă coruperea datelor
Monitorizarea stocării cu 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"
Concluzii și pașii următori
Stocarea Kubernetes este unul dintre cele mai critice straturi pentru aplicațiile de întreprindere producție. Interfața de stocare container standardizează integrarea cu orice furnizor, furnizarea dinamică cu StorageClass elimină munca manuală, de ex StatefulSets furnizează primitivele necesare pentru a gestiona bazele de date cu identități stabile.
Cheia pentru depozitarea robustă în producție este o combinație de alegeri arhitecturale corect (reclaimPolicy Retain, WaitForFirstConsumer, anti-afinity), monitorizare proactivă cu Prometheus și o strategie de backup testată în mod regulat cu Velero sau VolumeSnapshot.
Articole viitoare din seria Kubernetes la scară
Serii înrudite
- Rețea Kubernetes: CNI, Cilium cu eBPF și Politică de rețea
- MLOps și Machine Learning în producție — stocare pentru seturi de date ML
- PostgreSQL și căutare vectorială pentru AI







