Storage — PV, PVC, StorageClass
컨테이너는 일시적이지만 데이터는 영구적이어야 한다
1. K8s 스토리지 모델
1.1 왜 별도 스토리지인가
컨테이너의 파일시스템은 임시적(ephemeral). 컨테이너가 재시작되면 데이터가 사라짐. DB, 로그, 설정 등 영속 데이터를 위해 별도 스토리지가 필요함.
1.2 추상화 계층
flowchart TB
App["애플리케이션<br/>(Pod)"]
App -->|"Volume Mount"| PVC["PVC<br/>(사용자 요청)"]
PVC -->|"바인딩"| PV["PV<br/>(실제 스토리지)"]
PV --> Backend["스토리지 백엔드<br/>(NFS, Ceph, EBS, local)"]
SC["StorageClass"] -->|"동적 생성"| PV
| 오브젝트 | 역할 | 비유 |
|---|---|---|
| PV | 실제 스토리지 리소스 | 물리 디스크 |
| PVC | 사용자의 스토리지 요청 | "10GB 디스크 주세요" |
| StorageClass | 동적 프로비저닝 정책 | "요청하면 자동으로 만들어 줌" |
2. Volume 유형
| 유형 | 생명주기 | 용도 |
|---|---|---|
| emptyDir | Pod와 동일 | 임시 캐시, Pod 내 컨테이너 간 공유 |
| hostPath | 노드 | 노드 로컬 파일 접근 (⚠️ 위험) |
| configMap / secret | 오브젝트와 동일 | 설정/인증 주입 |
| PV/PVC | 독립적 | ✅ 영구 데이터 |
⚠️ hostPath는 프로덕션에서 지양. 특정 노드에 종속되어 Pod가 다른 노드로 이동하면 데이터에 접근할 수 없음.
3. PV & PVC
3.1 정적 프로비저닝
관리자가 PV를 미리 생성하고, 사용자가 PVC로 요청하면 매칭.
# PV (관리자)
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: 10.10.10.50
path: /exports/data
---
# PVC (사용자)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
3.2 Access Mode
| 모드 | 약자 | 설명 |
|---|---|---|
| ReadWriteOnce | RWO | 하나의 노드에서만 읽기/쓰기 |
| ReadOnlyMany | ROX | 여러 노드에서 읽기만 |
| ReadWriteMany | RWX | 여러 노드에서 읽기/쓰기 |
| ReadWriteOncePod | RWOP | 하나의 Pod에서만 (K8s 1.22+) |
3.3 Reclaim Policy
PVC가 삭제되었을 때 PV를 어떻게 처리할지:
| 정책 | 동작 |
|---|---|
| Retain | PV 보존 (수동 정리). 데이터 안전 |
| Delete | PV + 백엔드 스토리지 자동 삭제 |
| Recycle | ❌ 폐지됨 |
4. StorageClass & 동적 프로비저닝
4.1 왜 동적 프로비저닝인가
정적: 관리자가 PV를 미리 만들어야 함 → 비효율 동적: PVC 생성 시 StorageClass가 자동으로 PV를 만듦
# StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-rbd
provisioner: rbd.csi.ceph.com # CSI 드라이버
parameters:
clusterID: my-ceph-cluster
pool: kubernetes
reclaimPolicy: Delete
allowVolumeExpansion: true
---
# PVC (StorageClass 지정)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-data
spec:
storageClassName: ceph-rbd # ← StorageClass 참조
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
# → PV가 자동 생성되고 PVC에 바인딩됨
4.2 기본 StorageClass
# 기본 StorageClass 확인
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY DEFAULT
# ceph-rbd rbd.csi.ceph.com Delete ✅
# storageClassName을 생략하면 기본 StorageClass가 사용됨
5. CSI (Container Storage Interface)
스토리지 플러그인의 표준 인터페이스. 벤더가 CSI 드라이버를 만들면 K8s에서 해당 스토리지를 사용할 수 있음.
| CSI 드라이버 | 스토리지 | Proxmox 관련 |
|---|---|---|
| Ceph CSI | Ceph RBD, CephFS | ✅ Proxmox Ceph 연동 |
| NFS CSI | NFS | ✅ Proxmox NFS |
| AWS EBS CSI | AWS EBS | |
| Longhorn | 분산 블록 (Rancher) | |
| OpenEBS | 로컬 디스크 기반 |
6. StatefulSet + PVC
StatefulSet의 volumeClaimTemplates로 Pod마다 고유 PVC를 자동 생성.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
template:
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: ceph-rbd
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
# → data-postgres-0, data-postgres-1, data-postgres-2 PVC가 자동 생성
Pod가 삭제되어도 PVC는 유지됨 → Pod가 재생성되면 같은 PVC에 다시 연결.
다음 글
→ #6 ConfigMap, Secret & Configuration