쿠버네티스 Volume: PV, PVC, StorageClass
hostPath 볼륨에서 출발해 PersistentVolume, PersistentVolumeClaim, StorageClass가 각각 어떤 문제를 해결하는지 정리한다.
시리즈 · Kubernetes2 / 4
- 쿠버네티스 기본 오브젝트: Pod, ReplicaSet, Deployment, Service
- 쿠버네티스 Volume: PV, PVC, StorageClass
- kubeadm으로 쿠버네티스 클러스터 업그레이드하기
- CKA 2025 개정 시험 합격 후기
쿠버네티스에서 볼륨을 마운트하는 방법을 PersistentVolume, PersistentVolumeClaim, StorageClass 순서로 정리한다.
Volume
Pod도 Docker 컨테이너와 마찬가지로 임시 공간을 사용한다. Pod가 삭제되면 내부 데이터도 함께 사라지므로, 애플리케이션 로그나 DB 데이터처럼 유지해야 하는 데이터는 Volume을 연결해 Pod가 삭제되어도 남도록 한다.
가장 간단하게 호스트 디렉토리와 마운트해 보자. 0~100 사이 숫자를 랜덤하게 뽑아 Pod의 /opt/number.out에 저장하는 예시다. volumes에서 호스트의 /data를 볼륨으로 정의하고, 컨테이너의 volumeMounts에서 이를 /opt에 마운트한다.
apiVersion: v1
kind: Pod
metadata:
name: random-number-generator
spec:
containers:
- image: alpine
name: alpine
command: ["/bin/sh", "-c"]
args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
volumeMounts:
- mountPath: /opt
name: data-volume
volumes:
- name: data-volume
hostPath:
path: /data
type: Directory

멀티 노드에서의 한계
Single Node라면 문제없다. 하지만 Pod가 여러 Node에서 실행되면 결과가 각 Node의 /data에 흩어지므로, 결과를 보려면 Node마다 접근해야 한다. 이럴 때는 EBS 같은 외부 스토리지에 저장할 수 있다.
volumes:
- name: data-volume
awsElasticBlockStore:
volumeID: <volume-id>
fsType: ext4
다만 이 방식은 볼륨 정의가 Pod 스펙에 묶여 있다. Pod를 새로 만들 때마다 스토리지 설정을 다시 작성해야 한다.
PersistentVolume (PV)
이 문제를 해결하는 것이 PV이다. PV는 Pod와 별개의 생명주기를 갖는 스토리지 리소스이다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /path/to/directory/on/host
type: DirectoryOrCreate
persistentVolumeReclaimPolicy는 연결된 PVC가 삭제되었을 때 PV를 어떻게 처리할지 정의한다.
PersistentVolumeClaim (PVC)
PV로 볼륨을 미리 만들어 두고, PVC로 필요한 PV를 요청해 사용한다고 생각하면 된다. PVC와 PV는 1:1로 바인딩되며, PV가 PVC의 accessModes와 용량 요구를 만족해야 한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
Pod에서는 다음과 같이 PVC를 지정한다.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: nginx
volumeMounts:
- name: my-volume
mountPath: /usr/share/nginx/html
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: myclaim
Pod가 사용 중인 PVC는 삭제를 요청해도 바로 삭제되지 않는다.
PV/PVC만 사용하면 필요한 용량만큼의 디스크를 미리 확보해 PV로 만들어 두어야 한다(static provisioning). PVC가 요청하는 만큼의 디스크가 동적으로 생기면 더 편리한데(dynamic provisioning), 이를 위한 것이 StorageClass이다.
StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: google-storage
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard # type에 따라 SSD 등 디스크 종류가 달라진다
replication-type: none
StorageClass를 사용하면 PV를 직접 만들 필요가 없다. PVC가 요구하는 조건에 맞춰 클라우드에서 디스크와 PV가 자동으로 생성된다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
storageClassName: google-storage
resources:
requests:
storage: 500Mi
전체 관계를 그림으로 보면 다음과 같다.

그림 출처: Kubecost - Kubernetes Storage Class