
쿠버네티스에서는 단순히 호스트 서버의 볼륨을 연결하는 것 외에 다양한 형태로 데이터를 저장 및 참조하는 방법들을 제공한다.
이 장에서는 다음과 같은 리소스를 살펴본다:
- PersistentVolume
- PersistentVolumeClaim
- StorageClass
PV는 데이터 저장소를 추상화시킨 리소스이다. 쿠버네티스 클러스터 관리자가 데이터 저장소를 사용하기 위해 미리 마련한 저장 자원을 나타낸다.
PV에는 구체적인 저장소 정보가 담겨있는데, 예를 들면:
이처럼 다양한 환경에서 다양한 저장소 타입을 제공하기 위해 쿠버네티스에서는 PersistentVolume(PV)라는 추상화된 리소스를 사용하고 각 환경에 따라 그에 맞는 타입을 선택한다.
Chapter 5에서 본 호스트 서버의 볼륨을 연결하는 에제를 PersistentVolume으로 표현하면 다음과 같다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-volume
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /tmp

Network File System도 PV로 사용할 수 있다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-nfs
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteMany # 여러 Pod가 동시에 접근 가능하도록 설정
persistentVolumeReclaimPolicy: Retain # PVC 삭제 시 PV 유지
storageClassName: "nfs-storage"
mountOptions:
- nfsvers=3
nfs:
server: 172.30.1.29
path: "/share/MD0_DATA/some_path"
AWS EBS를 사용하는 예제는 다음과 같다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: aws-ebs
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
awsElasticBlockStore:
volumeID: vol-1234567890abcdef0
fsType: ext4
PersistentVolumeClaim(PVC)는 저장소 사용자가 PV를 요청하는 리소스이다. 클러스터 관리자가 PersistentVolume을 통해 데이터 저장소를 준비하면 쿠버네티스 사용자(또는 개발자)가 PVC 요청을 통해 해당 리소스를 선점하다. 이름에서 알 수 있듯이 PersistentVolume의 사용을 요청(Claim)하는 역할을 담당한다.

작동 순서는 다음과 같다.
- StorageClass 정의
- 관리자가 StorageClass를 먼저 정의한다.
- 이는 “어떤 방식으로 동적으로 볼륨을 생성할 것인가”에 대한 규칙을 담는다. 예: AWS EBS, NFS, Azure Disk 등.
- provisioner, parameters, reclaimPolicy 등을 지정한다.
- 사용자가 PVC(PersistentVolumeClaim) 생성
- 사용자는 어떤 StorageClass를 쓸 것인지 명시하고, 원하는 용량(resources.requests.storage)과 접근 모드(accessModes)를 명시한 PVC를 생성한다.
- 이때 아직 PV는 명시적으로 존재하지 않아도 된다. 동적 프로비저닝이 가능한 경우 자동으로 생성된다.
- PV Controller가 동작
- PVC가 생성되면, kube-controller-manager 내의 PV Controller가 이를 감지하고,
- 해당 StorageClass의 정의에 따라 자동으로 PV(PersistentVolume) 를 생성한다.
- PV와 PVC 바인딩
- 생성된 PV는 PVC와 조건(용량, 접근 모드 등)이 일치할 경우 자동으로 바인딩된다.
- 이때 PVC는 특정 PV를 점유(Claim)한 상태가 된다.
- Pod에서 PVC 사용
- 사용자는 Pod 정의에서 volume 항목에 PVC 이름을 명시해 마운트한다.
- 결국 Pod → PVC → PV → 실제 스토리지로 연결된다.
- 실제 Storage와 연결
- PV의 spec에 따라 AWS EBS, GCE PD, Azure Disk 등 실제 스토리지에 연결된다.
- 쿠버네티스 노드 상에서 마운트가 이루어진다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: manual
accessModes:
-ReadWriteOnce
resources:
requests:
storage: 1Gi
PVC 리소스를 생성하면 요청한 저장소 타입클래스 이름(storageClassName)에 맞는 PV를 연결해준다.
이제 Pod에서
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /test-volume
name: vol
volumes:
- name: vol
persistentVolumeClaim:
claimName: my-pvc
로 마운트 할 수 있다.
StorageClass 리소스는 클러스터 관리자에 의해 사용자들이 선택할 수 있는 스토리지 종류를 열거한 것이다.
사용자는 클러스터 관리자가 제공하는 StorageClass를 이용하여 동적으로 볼륨을 제공 받을 수 있다. 원래 데이터 저장소를 사용하려면 먼저 쿠버네티스 관리자가 데이터 저장소를 미리 준비해야 한다. 만약 가용한 볼륨이 존재하지 않는다면, Pod가 생성되지 않고 Pending 상태로 대기하게 된다.
하지만 StorageClass를 이용하면 볼륨을 생성하기를 기다릴 필요 없이 동적으로 데이터 저장소를 제공받을 수 있다!
kubectl get sc
StorageClass 리소스는 클러스터 레벨의 리소스로, 네임스페이스 지정을 할 필요가 없다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc-sc
spec:
storageClassName: local-path
accessModes:
-ReadWriteOnce
resources:
requests:
storage: 1Gi
PVC가 생성되면 StorageClass의 정의에 따라 자동으로 PV가 동적으로 생성되며, 조건이 일치하면 곧바로 PVC와 바인딩된다. 이후 Pod에서 해당 PVC를 사용하면 실제 스토리지가 마운트된다.
※ 단, volumeBindingMode: WaitForFirstConsumer로 설정된 StorageClass의 경우엔 Pod 스케줄링이 될 때까지 PV 생성을 기다린다. (그건 지연 바인딩 이라고 따로 다루는 예외 케이스임.)
첫째로, PV를 StorageClass가 대신 특정 디렉터리 위치 아래(var/lib/...)로만 만들어주기 때문에 일반 사용자가 로컬 호스트 서버의 아무 위치나 디렉터리를 사용하지 못하게 막을 수 있다.
둘째로, local-path와 같이 간단하게 PV를 설정하는 경우는 별 차이가 없겠지만 NFS StorageClass와 같이 NFS 서버정보, 마운트 옵션, 마운트 디렉터리 등 PV를 생성하기 위해 복잡한 인프라 정보를 알아야 하는 경우, 사용자가 StorageClass에게 요청만 하면 나머지는 StorageClass가 알아서 PV를 만들어주어 PVC에 연결해 준다.
이번에는 네트워크로 연결된 NFS 볼륨을 동적으로 생성하는 StorageClass를 만들어보자.
책에서는 외부 NFS를 사용하지 않고 Helm으로 내부 NFS를 사용했는데, 필자는 NAS가 있으므로 External Procisioner를 사용할 것이다.
먼저, nfs-subdir-external-provisioner를 helm으로 설치하고, 내 NAS의 IP와 공유 디렉토리를 지정한다.
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=192.168.0.10 \
--set nfs.path=/share/MD0_DATA/k8s_test \
--set storageClass.name=nfs-storage
그리고 아래와 같이 StorageClass를 만들어준다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "true" # PVC 삭제 시 디렉터리를 아카이브할지 여부
reclaimPolicy: Delete
volumeBindingMode: Immediate
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
storageClassName: nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
이제 PVC를 생성하면 PV가 자동으로 생성된다.