쿠버네티스 마스터 노트 시리즈 6편. Pod이 일회용인 환경에서 데이터를 영속화하는 4단계 추상화(Volume → PV → PVC → StorageClass), emptyDir·hostPath·PV의 결정적 차이, AccessMode 3종(RWO·ROX·RWX), Dynamic Provisioning이 운영을 바꾼 이유, CSI 드라이버 표준, ReclaimPolicy 함정까지.
이 글은 쿠버네티스 마스터 노트 시리즈의 여섯 번째 편입니다. 5편(Config)까지 설정 주입이었다면, 이번엔 데이터를 어떻게 영속화하나 — Storage 4 계층.
Pod이 사라지면 안 사라져야 하는 데이터 — DB·로그·업로드 파일. K8s Storage 추상화가 그 답. 다만 PV·PVC·StorageClass 3 객체 관계가 처음엔 막연합니다.
처음 Storage가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, 계층이 4개입니다 — Volume·PV·PVC·StorageClass. 누가 누구를 참조? 둘째, Static vs Dynamic Provisioning 차이가 막연합니다.
해결법은 한 가지예요. "PVC가 PV를 요청하고, StorageClass가 PV를 자동 생성" 한 줄. 사용자(Pod)는 PVC만 작성, 운영자가 PV 만들거나 StorageClass가 자동. 이 그림이 잡히면 흐름이 보입니다.
4 계층 추상화
[Pod]
↓ uses
[PVC] (Persistent Volume Claim) — 사용자가 요청
↓ binds
[PV] (Persistent Volume) — 클러스터 자원
↓ provisioned by (옵션)
[StorageClass] — 자동 PV 생성 정책
↓ uses
[CSI Driver] — 실제 스토리지 (AWS EBS·NFS·Ceph 등)
Volume — Pod 안 단순 스토리지
가장 단순. Pod 정의 안에 직접:
spec:
containers:
- name: app
volumeMounts:
- name: cache
mountPath: /tmp/cache
volumes:
- name: cache
emptyDir: {} # 또는 hostPath / configMap / secret 등
emptyDir — 임시
volumes:
- name: cache
emptyDir: {}
Pod 생성 시 빈 디렉토리. Pod 삭제 시 함께 사라짐.
용도:
- 컨테이너 사이 파일 공유 (Sidecar)
- 임시 캐시
- 스크래치 공간
여기서 정말 중요한 시험 함정 — emptyDir = Pod 생명주기. Pod 다운 시 데이터 사라짐. 영속 X.
hostPath — 노드 디스크 직접
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
type: Socket
노드 호스트 디렉토리 마운트.
여기서 시험 함정이 하나 있어요. hostPath는 보안 위험. 노드 시스템 디렉토리 접근 가능. 운영 환경 거의 X. 모니터링·시스템 데몬에만.
PV — Persistent Volume (영속 자원)
클러스터 단위 영속 스토리지.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /data/pv-data
특성:
- 클러스터 단위 (namespace X)
- 관리자가 미리 만들거나 (Static) 동적 생성 (Dynamic)
- AccessMode·ReclaimPolicy 명시
PVC — Persistent Volume Claim (사용자 요청)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
# Pod에서 PVC 사용
spec:
containers:
- name: app
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
흐름:
1. PVC 생성 → "5Gi RWO 필요"
2. K8s가 매칭 PV 찾기 → bind
3. Pod에서 PVC 참조 → 자동 마운트
여기서 정말 중요한 시험 함정 — PVC는 namespace 단위, PV는 cluster 단위. PVC 한 namespace 안. 다른 namespace에서 같은 PV 사용 X.
AccessMode — 3종
| 모드 | 의미 | 약자 |
|---|---|---|
| ReadWriteOnce | 한 노드에서만 읽기·쓰기 | RWO |
| ReadOnlyMany | 여러 노드에서 읽기만 | ROX |
| ReadWriteMany | 여러 노드에서 읽기·쓰기 | RWX |
| ReadWriteOncePod (1.22+) | 한 Pod에서만 (RWO 강화) | RWOP |
스토리지별 지원
| 스토리지 | RWO | ROX | RWX |
|---|---|---|---|
| AWS EBS | O | X | X |
| GCP Persistent Disk | O | O | X |
| NFS | O | O | O |
| CephFS | O | O | O |
| GlusterFS | O | O | O |
| Azure Disk | O | X | X |
여기서 정말 중요한 시험 함정 — 블록 스토리지(EBS·Disk)는 RWX 미지원. RWX 필요 = NFS·CephFS·EFS 등 파일 시스템. 여러 Pod에서 같은 데이터 = RWX 필요.
StorageClass — Dynamic Provisioning
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iopsPerGB: "10"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
PVC가 StorageClass 지정 → 자동으로 PV 생성 (provisioner가 스토리지 생성).
# PVC
spec:
storageClassName: fast # ← StorageClass 이름
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 100Gi
흐름:
1. PVC 생성 (StorageClass: fast)
2. provisioner가 AWS EBS 100Gi 자동 생성
3. PV 자동 생성·PVC와 자동 bind
4. Pod 사용
Static vs Dynamic
| 측면 | Static | Dynamic |
|---|---|---|
| PV 생성 | 관리자 수동 | StorageClass 자동 |
| 환경 | 온프레미스·고정 자원 | 클라우드 |
| 권장 | 작은 클러스터 | 운영 표준 |
여기서 시험 함정이 하나 있어요. 클라우드 환경 = Dynamic이 표준. EKS·GKE는 기본 StorageClass 자동 설치 (gp2·standard 등).
ReclaimPolicy — PV 회수 정책
spec:
persistentVolumeReclaimPolicy: Retain # 또는 Delete / Recycle
| 정책 | 의미 |
|---|---|
| Retain | PVC 삭제 시 PV·데이터 보존 (수동 정리) |
| Delete | PVC 삭제 시 PV·실제 스토리지 삭제 |
| Recycle | (deprecated) |
여기서 정말 중요한 시험 함정 — Dynamic Provisioning 기본 = Delete. PVC 실수 삭제 시 데이터 영영 사라짐. DB·중요 데이터 = Retain 권장.
# 안전한 패턴
storageClassName: critical
reclaimPolicy: Retain
VolumeBindingMode
volumeBindingMode: Immediate # 또는 WaitForFirstConsumer
- Immediate — PVC 생성 즉시 PV bind
- WaitForFirstConsumer — Pod이 PVC 사용할 때까지 대기
여기서 시험 함정이 하나 있어요. WaitForFirstConsumer 권장 (Multi-AZ 환경). PV의 AZ가 Pod 스케줄링되는 노드 AZ와 일치하도록 대기. Immediate 시 잘못된 AZ에 PV 생성 가능.
CSI — Container Storage Interface
K8s 1.13+ 표준 스토리지 플러그인 인터페이스.
[K8s] → CSI Driver → [실제 스토리지]
- AWS EBS CSI Driver
- GCP PD CSI Driver
- Azure Disk CSI Driver
- Longhorn (분산 블록)
- Rook/Ceph
- OpenEBS
- NFS CSI Driver
이전엔 in-tree 플러그인 (K8s 코드에 직접). 지금은 모두 CSI로 분리. 외부 도구·확장 자유.
VolumeSnapshot — 백업
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: db-snapshot
spec:
volumeSnapshotClassName: csi-snapshot
source:
persistentVolumeClaimName: db-pvc
CSI 지원 시 PVC를 스냅샷으로. 복구 = 새 PVC를 스냅샷에서 생성.
운영 권장 패턴
DB·중요 데이터
storageClassName: standard
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
일회용·캐시
emptyDir: {}
# 또는
storageClassName: fast
reclaimPolicy: Delete
StatefulSet과 결합
spec:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
storageClassName: standard
resources:
requests:
storage: 10Gi
각 StatefulSet Pod마다 자동 PVC 생성 (data-pg-0, data-pg-1).
디버깅
# PV·PVC 상태
kubectl get pv
kubectl get pvc
kubectl describe pvc <name>
# StorageClass
kubectl get sc
# Pod의 PVC 사용 확인
kubectl describe pod <name>
상태:
Pending— 매칭 PV 없음 (Static) / Provisioner 동작 중 (Dynamic)Bound— 정상Lost— PV 사라짐
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 6편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- 4 계층 — Volume / PV / PVC / StorageClass
- Volume = Pod 안 단순 (emptyDir·hostPath·configMap·secret 등)
- emptyDir = Pod 생명주기
- hostPath = 보안 위험·운영 X (시스템 데몬만)
- PV = 클러스터 단위 영속 자원
- PVC = namespace 단위 사용자 요청
- PVC가 PV bind, Pod이 PVC 참조
- AccessMode 3종 — RWO·ROX·RWX
- 블록 스토리지(EBS·Disk) = RWX 미지원
- RWX 필요 = NFS·CephFS·EFS
- StorageClass = Dynamic Provisioning 자동
- 클라우드 환경 표준
- 클라우드 = 기본 StorageClass 자동 설치
- ReclaimPolicy — Retain (보존) / Delete (삭제)
- Dynamic 기본 = Delete (위험) → DB는 Retain
- VolumeBindingMode — Immediate / WaitForFirstConsumer (Multi-AZ 권장)
- CSI = 표준 스토리지 플러그인 인터페이스
- in-tree → CSI로 분리 (1.13+)
- VolumeSnapshot = PVC 백업 (CSI 지원 시)
allowVolumeExpansion= 크기 확장 가능- StatefulSet
volumeClaimTemplates= Pod별 자동 PVC
시리즈 다른 편
- 1편 — 아키텍처·Control Plane
- 2편 — Pod
- 3편 — Workloads
- 4편 — Services·Networking
- 5편 — ConfigMap·Secret
- 6편 — Storage (현재 글)
- 7편 — Scaling·Scheduling
- 8편 — Security
- 9편 — Helm
- 10편 — 실전 (매니지드·GitOps·Observability)
공식 문서: Kubernetes Storage / CSI Drivers 에서 더 깊이.
다음 글(7편)에서는 Scaling & Scheduling — HPA·VPA·Probes·Affinity·Taint/Toleration까지 풀어 갑니다.