Kubernetes PersistentVolume(PV) / PersistentVolumeClaim(PVC): “파드 YAML에서 스토리지 설정을 분리”하는 이유와 동작 원리
앞에서 Kubernetes의 Volume(예: hostPath)을 파드 정의 파일에 직접 붙여서 데이터를 유지하는 방법을 봤습니다.
하지만 그 방식은 규모가 커질수록 문제가 생깁니다.
- 파드(또는 Deployment)마다 스토리지 상세 설정을 반복해야 함
- 스토리지 백엔드가 바뀌면(예: hostPath → NFS → EBS) 모든 파드 YAML을 수정
- 운영 환경에서는 스토리지 정책(암호화/스냅샷/삭제 정책/권한/성능 등)을 앱 개발자에게 맡기기 어렵고, 중앙 통제가 필요
이 문제를 해결하려고 Kubernetes는 스토리지를 “중앙에서 관리하는 리소스”로 분리했습니다.
- PV(PersistentVolume): 클러스터에 존재하는 “스토리지 자원(풀)” (관리자/플랫폼 팀이 주로 관리)
- PVC(PersistentVolumeClaim): 사용자가 “필요한 스토리지를 요청(Claim)”하는 객체
- Kubernetes는 PVC의 요구사항(용량/접근모드/스토리지클래스 등)에 맞는 PV를 찾아 1:1로 바인딩합니다.
1) PV/PVC의 핵심 그림
[관리자/플랫폼 팀] [개발자/사용자]
PV 생성/준비 -----------------> PVC 생성(요청)
|
| (K8s가 매칭/바인딩)
v
PVC <-> PV (1:1 Binding)
|
v
Pod/Deployment에서 PVC 마운트
중요한 스코프 차이:
- PV는 클러스터 스코프(네임스페이스 없음)
- PVC는 네임스페이스 스코프(특정 네임스페이스에 속함)
그래서 “PV/PVC는 서로 다른 오브젝트이며 역할이 분리된다”는 설명이 나옵니다.
2) PV 만들기 (정적 프로비저닝 예시)
강의 흐름대로 먼저 “정적(static) PV”를 하나 만들어보면 개념이 확 잡힙니다.
여기서는 학습용으로 hostPath를 쓰지만, 멀티 노드/운영에서는 hostPath는 권장되지 않습니다.
PV 예시 (hostPath, 학습용)
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data
type: DirectoryOrCreate
핵심 필드 정리:
capacity.storage: PV가 제공하는 용량(예: 1Gi)accessModes: 접근 모드(아래에서 자세히)persistentVolumeReclaimPolicy: PVC가 삭제됐을 때 PV를 어떻게 처리할지hostPath: 실제 저장소(학습용). 운영에서는 NFS/CSI(EBS/GCE PD/Azure Disk 등)로 대체
적용/확인:
kubectl apply -f pv.yaml
kubectl get pv
kubectl describe pv pv-vol1
3) AccessModes, VolumeMode 정확히 이해하기
강의 스크립트에 “읽기 전용 한 번 / 읽기 쓰기 한 번 / 읽기 쓰기 많음” 같은 표현이 나오는데, Kubernetes의 표준 값은 다음입니다.
AccessModes
ReadWriteOnce (RWO): 한 노드에만 Read/Write로 마운트 가능 (파드 여러 개여도 “같은 노드”면 가능)ReadOnlyMany (ROX): 여러 노드에서 ReadOnly로 마운트 가능ReadWriteMany (RWX): 여러 노드에서 Read/Write로 마운트 가능 (NFS/CEPHFS 같은 공유 스토리지에서 흔함)
PV <-> PVC 는 1:1 맵핑이지만, PVC는 여러 파드에 할당이 될 수 있고, 그 경우 파드는 여러 노드에 스케줄링 될 수 있음
포인트: AccessModes는 “파드 개수”가 아니라 노드 기준으로 이해하는 게 안전합니다.
VolumeMode (PVC/PV에 존재)
Filesystem(기본): 일반적인 디렉터리 마운트Block: raw block device로 제공(특수 케이스)
4) PVC 만들기: “나는 500Mi가 필요해요”
사용자는 PV를 직접 참조하지 않고, PVC로 요구사항을 선언합니다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
적용/확인:
kubectl apply -f pvc.yaml
kubectl get pvc
kubectl describe pvc myclaim
5) 바인딩(Binding)은 어떻게 일어나나?
PVC가 생성되면 Kubernetes는 다음을 기준으로 PV를 찾고 1:1 바인딩합니다.
- 요청 용량
requests.storage≤ PV의capacity.storage accessModes가 호환되는지volumeMode(Filesystem/Block) 호환storageClassName매칭 여부(있다면 중요)- (선택) 라벨 셀렉터로 특정 PV 지정 가능
“작은 PVC가 큰 PV에 묶일 수 있다”의 의미
PVC가 500Mi를 요청해도, PV가 1Gi이면 조건을 만족하므로 바인딩될 수 있습니다.
하지만 남는 500Mi를 다른 PVC가 가져갈 수는 없습니다.
PV-PVC는 1:1이기 때문에, 결과적으로 “남는 용량이 낭비”될 수 있습니다.
매칭되는 PV가 없다면?
PVC는 Pending 상태로 남아있고, 이후 조건을 만족하는 PV가 생기면 자동으로 바인딩됩니다.
확인 포인트:
kubectl get pv,pvc
# STATUS: Available / Bound / Released 등을 확인
6) PVC를 Pod에서 사용하기
사용자가 준 예시 그대로, Pod의 volumes에 persistentVolumeClaim.claimName을 지정하면 됩니다.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
확인:
kubectl apply -f pod.yaml
kubectl exec -it mypod -- sh -c 'echo hello > /var/www/html/index.html; ls -al /var/www/html'
7) Deployment/ReplicaSet에서도 동일: “Pod Template”에 넣는다
Deployment는 결국 “Pod 템플릿(spec.template)”을 반복 생성하므로, 같은 방식으로 PVC를 붙입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: myclaim
주의:
ReadWriteOncePVC를 여러 replica가 동시에 다른 노드에서 쓰려고 하면 문제가 생길 수 있습니다.
이 경우 설계상RWX스토리지(NFS/CEPHFS 등) 또는 앱 구조 변경이 필요합니다.
8) PVC 삭제하면 PV는 어떻게 되나? (Reclaim Policy)
PVC를 삭제:
kubectl delete pvc myclaim
그 다음 PV의 처리 방식은 persistentVolumeReclaimPolicy에 따라 달라집니다.
Retain(기본으로 많이 쓰임): PV는 남겨두고, 데이터도 그대로. 다만 “다른 PVC가 즉시 재사용”하진 못하는 경우가 많아(Released 상태) 관리자가 정리/재설정 필요Delete: PVC 삭제 시 PV도 삭제(동적 프로비저닝에서 흔함). 실제 스토리지도 함께 삭제되도록 연동Recycle: 과거 방식. “간단히 파일 삭제(rm -rf)” 수준으로 지우는 컨셉이었는데, 현대 환경(스냅샷/암호화/권한/클라우드 API/완전 삭제 보장)에 부족해 사실상 사용 중단/폐기 흐름입니다.
운영에서는 보통:
- 테스트/임시 환경:
Delete - 중요한 데이터/수동 검증 필요:
Retain
이런 식으로 정책화합니다.
9) 왜 요즘은 “PV를 직접 만들기”보다 “StorageClass + 동적 프로비저닝”이 기본인가?
정적 PV 방식은 “관리자가 PV를 미리 만들어 풀을 준비”해야 합니다. 규모가 커지면 운영 부담이 큽니다.
그래서 현대 Kubernetes에서는 대부분:
- StorageClass를 만들고(“이 CSI 드라이버로 이런 타입의 디스크를 만들자”)
- 사용자는 PVC만 만들면
- CSI가 자동으로 볼륨을 생성/Attach/Mount 해주는 동적 프로비저닝(Dynamic Provisioning) 을 씁니다.
즉, “PV를 수동 생성”하는 모델에서
“PVC 요청을 트리거로 PV를 자동 생성”하는 모델로 넘어간 겁니다.
10) 트러블슈팅 체크리스트 (실습에서 자주 막히는 지점)
PVC가 Pending에서 안 넘어갈 때
kubectl describe pvc myclaim
kubectl get events --sort-by=.metadata.creationTimestamp
주요 원인:
- 매칭되는 PV가 없음(정적 환경)
storageClassName불일치accessModes/volumeMode불일치- 동적 프로비저닝인데 CSI/StorageClass가 준비 안 됨
실제로 어떤 PV에 붙었는지
kubectl get pvc myclaim -o wide
kubectl describe pvc myclaim
kubectl describe pv <bound-pv-name>
정리
- 파드 YAML에 스토리지 설정을 직접 박으면, 규모가 커질수록 운영/변경이 어려움
- PV는 “클러스터 레벨의 스토리지 자원”, PVC는 “네임스페이스 레벨의 스토리지 요청”
- PVC는 조건(용량/접근모드/클래스 등)에 맞는 PV에 1:1 바인딩
- PVC 삭제 후 PV 처리 방식은 Reclaim Policy(Retain/Delete/Recycle) 로 결정
- 현대 운영에서는 대부분 StorageClass + CSI + 동적 프로비저닝이 기본
Practice Test - https://uklabs.kodekloud.com/topic/practice-test-persistent-volume-claims-2/
'CKA' 카테고리의 다른 글
| Storage - 정리 (0) | 2026.01.05 |
|---|---|
| Storage - Storage Class (0) | 2026.01.03 |
| Storage - Volume (0) | 2026.01.03 |
| Storage - Kubernetes CSI(Container Storage Interface) (0) | 2026.01.03 |
| Storage - Docker의 Storage Driver vs Volume Driver (0) | 2026.01.03 |