Kubernetes의 영구 스토리지: CSI, PV, StorageClass 및 StatefulSet
Kubernetes는 Stateless 워크로드를 위한 플랫폼으로 탄생했지만 애플리케이션의 현실은 데이터베이스, 메시지 대기열, 영구 캐시 시스템, 공유 파일 시스템. 모두 Pod의 수명주기 동안 지속되는 스토리지가 필요합니다. 클라우드 제공업체 간에 안정적이고 고성능이며 휴대 가능한 방식으로 이 스토리지를 관리하세요. 일상적인 생산에서 가장 구체적인 과제 중 하나입니다.
이 기사에서는 컨테이너 스토리지부터 전체 Kubernetes 스토리지 계층을 살펴보겠습니다. 공급자와의 통합을 표준화하는 인터페이스(CSI), PertantVolume 및 동적 프로비저닝을 위한 StorageClass(최대) StatefulSet 에 대한 Kubernetes에서 PostgreSQL, Cassandra, Redis와 같은 데이터베이스를 관리합니다.
무엇을 배울 것인가
- Kubernetes 스토리지 모델: Volume, PertantVolume, PertantVolumeClaim
- CSI(컨테이너 스토리지 인터페이스) 작동 방식과 가장 많이 사용되는 드라이버
- StorageClass 및 동적 프로비저닝: AWS EBS, GCE PD, Azure 디스크 구성
- 액세스 모드: ReadWriteOnce, ReadOnlyMany, ReadWriteMany - 언제 무엇을 사용할지
- StatefulSet: 안정적인 ID, 자동 PVC, 순차적 롤링 업데이트
- StatefulSet을 사용하여 Kubernetes에서 PostgreSQL을 실행하는 방법
- Velero를 사용한 PertantVolume 백업 및 복원
Kubernetes 스토리지 모델
Kubernetes는 "필요한 것"을 구분하는 스토리지의 추상화 계층 구조를 정의합니다. (PerciousVolumeClaim)은 "제공된 대로"(PerciousVolume 및 StorageClass)입니다. 이 개발자가 제공업체 세부정보를 알지 못해도 저장용량을 요청할 수 있습니다. 기본 클라우드.
스토리지 프리미티브
| 의지 | 빗자루 | 누가 관리하나요? | 설명 |
|---|---|---|---|
| 용량 | 현물 상환 지불 | 개발자 | Pod의 수명 주기와 연결된 임시 스토리지 |
| 영구 볼륨(PV) | 클러스터 | 관리자/프로비저너 | 클러스터의 스토리지 부분, 포드와 독립적인 수명주기 |
| 영구볼륨클레임(PVC) | 네임스페이스 | 개발자 | 포드에서 스토리지 요청 |
| 스토리지클래스 | 클러스터 | 관리자 | 스토리지 "유형" 및 프로비저너를 정의합니다. |
퍼시스턴트볼륨의 수명주기
PV의 수명주기는 다양한 상태를 거칩니다. 그들을 이해하는 것이 필수적이다 문제 해결:
- 사용 가능: PV가 존재하고 무료이며 PVC와 연결되지 않습니다.
- 경계: PV는 요구 사항을 충족하는 PVC에 접착되었습니다.
- 출시된: PVC는 제거되었지만 PV는 아직 사용할 수 없습니다(데이터는 그대로 유지됨).
- 실패한: PV가 자동 회수에 실패했습니다.
# 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
컨테이너 스토리지 인터페이스(CSI)
CSI 이전에는 모든 스토리지 제공업체가 코드에 내장된 플러그인을 유지해야 했습니다. Kubernetes 소스(트리 내 플러그인). 이로 인해 강력하고 효율적인 결합이 이루어졌습니다. Kubernetes와 독립적으로 플러그인을 업데이트하기가 어렵습니다. CSI는 이 문제를 해결합니다. 타사에서 스토리지 드라이버를 생성할 수 있는 표준 gRPC 인터페이스 포함 독립적인 Kubernetes Pod로.
주요 CSI 드라이버
| CSI 드라이버 | 공급자 | 저장 유형 | 읽기쓰기많은 |
|---|---|---|---|
| aws-ebs-csi-드라이버 | AWS | 블록(gp3, io2) | No |
| aws-efs-csi-드라이버 | AWS | NFS(EFS) | Si |
| gce-pd-csi-드라이버 | GCP | 차단(pd-ssd, pd-balanced) | 아니요(RWX FileStore에만 해당) |
| azuredisk-csi-드라이버 | 하늘빛 | 블록(프리미엄 SSD) | No |
| azurefile-csi-드라이버 | 하늘빛 | NFS(애저 파일) | Si |
| csi-루크-세프 | 룩/세프 | 블록/FS/객체 | 예(CephFS) |
| 롱혼 | 목장주 | 블록 분산 | 예(NFS 사용) |
EKS에 CSI EBS 드라이버 설치
# 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: 동적 프로비저닝
정적 프로비저닝(PV 수동 생성)은 프로덕션 환경에서 실용적이지 않습니다. 와 동적 프로비저닝을 통해 Kubernetes는 PVC가 제공되면 자동으로 PV를 생성합니다. StorageClass에 구성된 CSI 드라이버를 사용하여 생성됩니다.
AWS EBS용 StorageClass
# 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"
GKE용 StorageClass(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: WaitForFirstConsumer가 필요한 이유
항상 사용 WaitForFirstConsumer 대신에 Immediate 언제
클러스터에는 여러 가용 영역이 있습니다. 와 함께 Immediate, PV가 생성됩니다
프로비저너가 예정된 영역(영역과 다를 수 있음)
포드가 예약될 위치입니다. 결과: 포드가 볼륨을 마운트하지 못했습니다.
WaitForFirstConsumer Pod와 동일한 영역에 PV를 생성합니다.
PertantVolumeClaim의 실제 사례
PVC 및 포드에 스토리지가 필요한 방식 PVC는 크기, 액세스 모드를 지정합니다. 및 StorageClass. Kubernetes는 호환 가능한 PV를 찾거나 생성하여 이를 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"}}}}'
액세스 모드: 올바른 모드 선택
| 액세스 모드 | 약어 | 그것은 무엇을 의미합니까? | 일반적인 사용 |
|---|---|---|---|
| 읽기쓰기한 번 | RWO | 단 하나의 읽기/쓰기 노드 | 데이터베이스, 단일 인스턴스 애플리케이션 |
| 읽기전용다수 | 록스 | 많은 읽기 전용 노드 | 공유 정적 데이터, 구성 |
| 읽기쓰기많은 | RWX | 많은 읽기/쓰기 노드 | 공유 NFS, 업로드 핸들러 |
| 읽기쓰기OncePod | RWOP | 단 하나의 읽기/쓰기 포드 | 단일 포드용 전용 스토리지(K8s 1.29+) |
StatefulSet: 안정적인 ID를 갖춘 워크로드
배포는 상태 비저장 워크로드에 적합하지만 안정적인 ID 필요(예측 가능한 호스트 이름, 전용 볼륨, 부팅 순서) 그것을 제공 StatefulSet. 배포와 비교한 주요 차이점은 다음과 같습니다.
- 안정적인 정체성: 포드에는 예측 가능한 이름이 있습니다.
myapp-0,myapp-1,myapp-2 - 부팅 순서: 포드는 순서대로(0, 1, 2) 시작되고 역순으로 종료됩니다.
- 전용 PVC: 각 포드는 다음을 통해 자체 PVC를 얻습니다.
volumeClaimTemplates - 헤드리스 서비스: 각 포드에는 안정적인 DNS 항목이 있습니다.
myapp-0.myapp.namespace.svc.cluster.local
StatefulSet을 사용하는 Kubernetes의 PostgreSQL
다음은 StatefulSet을 사용하여 프로덕션에 바로 사용할 수 있는 PostgreSQL 설정입니다. 구성을 위한 ConfigMap, 자격 증명을 위한 Secret 및 헤드리스 서비스를 포함합니다.
# 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
PostgreSQL 구성을 위한 ConfigMap
# 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
볼륨 스냅샷 및 백업
볼륨 스냅샷을 사용하면 PertantVolume의 특정 시점 백업을 생성할 수 있습니다. 클라우드 제공업체의 기본 기능(EBS 스냅샷, GCE PD 스냅샷 등)을 사용합니다.
# 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
Velero는 전체 Kubernetes 클러스터의 백업 및 복원을 위한 참조 도구입니다. PertantVolume을 포함하여:
# 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
AI/ML 워크로드를 위한 스토리지
ML 학습 워크로드에는 병렬 액세스라는 특별한 스토리지 요구 사항이 있습니다. 종종 여러 GPU 작업자가 동시에 대규모 데이터 세트에 대한 높은 처리량을 제공합니다.
# 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
Kubernetes 스토리지 모범 사례
생산 스토리지 체크리스트
- 항상 reclaimPolicy 사용: 유지 생산 데이터용.
DeletePVC가 삭제되면 자동으로 데이터를 삭제합니다. - 볼륨 바인딩 모드: WaitForFirstConsumer: 다중 영역 클러스터에서 AZ 간 바인딩 문제 방지
- 허용VolumeExpansion: 참: 가동 중지 시간 없이 볼륨 확장을 허용하도록 StorageClass 구성
- 디스크 사용량 모니터링: PVC가 용량의 80%를 초과하는 경우 Prometheus에 대한 경고 구성
- 자동 스냅샷: VolumeSnapshotClass 및 예약된 백업 구성
- 복원을 테스트합니다. 테스트되지 않고 쓸모없는 백업입니다. 매월 복원 테스트 수행
- 역할별로 PVC를 구분합니다. 데이터용 PVC 1개, 로그용 PVC 1개, 임시 백업용 PVC 1개
- 반친화성을 갖춘 StatefulSet: 다양한 노드와 영역에 복제본 배포
안티 패턴: 하지 마세요
- 프로덕션에서는 HostPath를 사용하지 마세요. Pod를 특정 노드에 연결하며 이식 가능하지 않습니다.
- 영구 데이터에는 emptyDir을 사용하지 마세요. Pod가 다시 시작되면 지워집니다.
- reclaimPolicy를 사용하지 마십시오: 프로덕션 데이터의 경우 삭제: 실수로 모든 것을 잃을 수도 있다
- 여러 Pod에 동일한 PVC(RWO)를 마운트하지 마세요. 데이터 손상을 일으킴
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"
결론 및 다음 단계
Kubernetes 스토리지는 엔터프라이즈 애플리케이션에 가장 중요한 계층 중 하나입니다. 생산. 컨테이너 스토리지 인터페이스는 모든 제품과의 통합을 표준화합니다. 공급자, StorageClass를 사용한 동적 프로비저닝으로 수동 작업이 제거됩니다. StatefulSet은 안정적인 ID로 데이터베이스를 관리하는 데 필요한 기본 요소를 제공합니다.
프로덕션 환경에서 강력한 스토리지의 핵심은 아키텍처 선택의 조합입니다. 올바른(reclaimPolicy Retain, WaitForFirstConsumer, 반선호도), 사전 모니터링 Prometheus를 사용하고 Velero 또는 VolumeSnapshot을 사용하여 정기적으로 테스트되는 백업 전략을 사용합니다.







