학습 목표
PV·PVC·StorageClass로 Pod 영속 볼륨을 요청·바인딩할 수 있다.
emptyDir·PVC·ConfigMap volume 용도 차이를 설명할 수 있다.
StatefulSet이 stable Pod 이름·순서·PVC 템플릿을 제공하는 이유를 설명할 수 있다.
headless Service와 Pod DNS로 stateful 워크로드를 연결할 수 있다.
문제 상황
- Deployment Pod가 재시작하면 로컬 디스크 데이터가 사라진다
- emptyDir은 Pod와 함께 삭제된다
- DB를 Deployment 3 replicas로 띄웠더니 데이터가 Pod마다 다르다
- 공유·순서·고정 ID가 필요하다
hostPath로 노드 디렉터리를 마운트했다- Pod 재스케줄 시 다른 노드면 데이터 없음, 보안 이슈
- PVC
Pending이 며칠간 해결되지 않는다- StorageClass·프로비저너·용량·accessMode 불일치
- Kafka·ZooKeeper처럼 pod-0, pod-1 순서가 중요한데 Deployment는 랜덤 이름
- StatefulSet + headless Service 패턴
앞서 ConfigMap·Secret·Service를 봤다. 이번 편은 데이터가 Pod를 넘어 남아야 할 때의 볼륨과 StatefulSet이다.
1. Kubernetes 스토리지 개요
Pod는 기본적으로 ephemeral — 컨테이너 파일시스템은 재시작·재스케줄 시 초기화.

| 리소스 | 역할 |
|---|---|
| Volume | Pod spec — 어떻게 마운트할지 |
| PVC | 사용자 요청 (용량·StorageClass·accessMode) |
| PV | 클러스터 실제 볼륨 풀 (또는 동적 생성 결과) |
| StorageClass | 동적 프로비저닝 — provisioner, parameters |
Pod volumeMount → PVC data-db → PV pv-001 → cloud disk / NFS
- 동적 프로비저닝 — PVC 생성 시 StorageClass가 PV 자동 생성 (일반적)
- 정적 — 관리자가 PV 미리 만들고 PVC가 바인딩 (legacy·특수)
2. Volume 종류

| volume | 수명 | 용도 |
|---|---|---|
| emptyDir | Pod | scratch, sidecar 공유, 캐시 (재시작 시 유지, Pod 삭제 시 소멸) |
| configMap / secret | Pod | 설정·자격증명 파일 (앞서 8편) |
| PVC | PVC/PV | DB·로그·업로드 — Pod 재스케줄 후에도 유지 |
| projected | Pod | 여러 소스 한 마운트 |
# emptyDir — ephemeral scratch
volumes:
- name: cache
emptyDir:
sizeLimit: 512Mi
volumeMounts:
- name: cache
mountPath: /tmp/cache
# PVC — persistent data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-db
| emptyDir | PVC | |
|---|---|---|
| Pod 삭제 | 데이터 삭제 | 데이터 유지 |
| 노드 이동 | scratch만 | 같은 PV 재attach (RWO) |
| 비용 | 노드 디스크 | 영속 스토리지 |
3. PVC와 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs # cloud CSI driver
parameters:
type: gp3
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-db
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
| accessMode | 약어 | 의미 |
|---|---|---|
| ReadWriteOnce | RWO | 한 노드 read/write (대부분 block) |
| ReadOnlyMany | ROX | 여러 노드 read |
| ReadWriteMany | RWX | 여러 노드 read/write (NFS, EFS 등) |
| StorageClass 옵션 | 설명 |
|---|---|
| WaitForFirstConsumer | Pod 스케줄 후 볼륨 생성 — AZ 맞춤 |
| Immediate | PVC 즉시 프로비저닝 |
| allowVolumeExpansion | PVC 용량 확장 (CSI) |
kubectl get storageclass
kubectl get pvc,pv
kubectl describe pvc data-db
- Pending — StorageClass 없음, 프로비저너 미설치, 용량 부족
- Released PV —
persistentVolumeReclaimPolicy: Retain vs Delete
4. StatefulSet이 필요한 이유
Deployment — Pod 교체 가능, 이름·IP 랜덤, 순서 없음.
StatefulSet — stable 네트워크 ID, 순서 있는 rollout, per-Pod PVC.

| Deployment | StatefulSet | |
|---|---|---|
| Pod 이름 | api-7d4f8-xyz |
web-0, web-1, web-2 |
| PVC | 공유 또는 없음 | volumeClaimTemplate → Pod마다 PVC |
| 생성 순서 | 병렬 | 0 → 1 → 2 순차 |
| 삭제 순서 | 병렬 | 2 → 1 → 0 역순 |
| DNS | 일반 Service | headless — Pod별 A 레코드 |
- Kafka, etcd, MySQL primary-replica — peer discovery에 고정 hostname 필요
- 단순 stateless API — Deployment가 기본
5. StatefulSet과 headless Service
apiVersion: v1
kind: Service
metadata:
name: db
spec:
clusterIP: None # headless
selector:
app: db
ports:
- port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db # headless Service 이름 (필수)
replicas: 3
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
| Pod | DNS (cluster) | PVC |
|---|---|---|
db-0 |
db-0.db.default.svc.cluster.local |
data-db-0 |
db-1 |
db-1.db.default.svc.cluster.local |
data-db-1 |
db-2 |
db-2.db.default.svc.cluster.local |
data-db-2 |
- serviceName — StatefulSet과 headless Service 연결
- volumeClaimTemplates — Pod 생성 시 PVC 자동 (
data-db-0…) - Pod 삭제해도 PVC 유지 (데이터 보존) — reclaim 정책 확인
kubectl apply -f code/statefulset-storage.yaml
kubectl get sts,po,pvc,svc
kubectl exec db-0 -- hostname -f
6. StatefulSet 운영
| 항목 | 동작 |
|---|---|
| scale up | web-2 생성 전 web-1 Ready |
| scale down | 높은 ordinal부터 삭제 |
| rolling update | partition — N 이상만 새 버전 |
| Pod 삭제 | 같은 ordinal로 재생성, 같은 PVC |
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # web-0, web-1 old · web-2+ new image
- OnDelete — 수동 삭제 시에만 업데이트 (레거시)
- ReadWriteOnce — 한 PV는 한 Pod만 — StatefulSet Pod는 보통 노드 분산 필요
7. 실무 선택
| 워크로드 | 권장 |
|---|---|
| Stateless API | Deployment + emptyDir cache |
| 단일 DB | StatefulSet 1 replica 또는 관리형 RDS (17편) |
| Kafka·ZooKeeper | StatefulSet + headless + per-Pod PVC |
| 공유 파일 | RWX StorageClass (NFS, EFS) |
| 로그·임시 | emptyDir 또는 stdout (Observability) |
| 체크 | |
|---|---|
| PVC Pending | StorageClass, CSI driver, quota |
| Pod Pending | RWO + 다른 노드에 PV 묶임 |
| 백업 | PV snapshot (CSI), 앱 일관성 |
| DR | snapshot·restore, 관리형 복제 |
8. 정리
- emptyDir — Pod 수명 임시 · PVC — 영속 데이터
- PVC → PV → StorageClass — 동적 프로비저닝이 기본
- StatefulSet — stable name, 순서, volumeClaimTemplate
- headless Service —
pod-N.serviceDNS - stateless는 Deployment, 데이터·순서 필요 시 StatefulSet
다음에 다룰 것
- 스케줄링과 리소스
- requests/limits, QoS, node affinity, HPA
해당 내용은 Kubernetes Documentation (kubernetes.io) 의 내용을 기반으로 합니다.
'플랫폼, 데브옵스와 클라우드' 카테고리의 다른 글
| CI/CD 개요 (0) | 2026.06.28 |
|---|---|
| 스케줄링과 리소스 (0) | 2026.06.27 |
| ConfigMap과 Secret (0) | 2026.06.25 |
| Service와 Ingress (0) | 2026.06.24 |
| Pod와 Deployment (0) | 2026.06.23 |