[Daily morning study] Kubernetes Persistent Volumes와 StorageClass 스토리지 관리

#daily morning study

Image


왜 스토리지가 필요한가

컨테이너는 기본적으로 에페머럴(ephemeral)하다. Pod가 재시작되거나 다른 노드로 이동하면 컨테이너 내부에 저장된 데이터는 모두 사라진다. 데이터베이스나 파일 업로드 서비스처럼 상태를 유지해야 하는 애플리케이션은 Pod 수명과 독립적인 영속성 스토리지가 필요하다.

Kubernetes는 이를 위해 세 가지 핵심 리소스를 제공한다:

  • PersistentVolume (PV): 클러스터에 프로비저닝된 실제 스토리지 조각
  • PersistentVolumeClaim (PVC): Pod가 PV에 대해 요청하는 명세
  • StorageClass: PV를 동적으로 프로비저닝하는 방법을 정의하는 템플릿

PersistentVolume (PV)

PV는 클러스터 관리자가 미리 준비하거나 StorageClass에 의해 자동 생성되는 스토리지 리소스다. 노드가 클러스터의 컴퓨팅 리소스인 것처럼 PV는 클러스터의 스토리지 리소스다.

PV는 Pod와 무관하게 독립적인 수명 주기를 가지므로, Pod가 삭제되어도 PV 안의 데이터는 보존된다.

PV 예시 (NFS 기반)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-example
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/exports
    server: 192.168.1.100

주요 필드 설명:

필드설명
capacity.storage스토리지 용량
accessModes마운트 방식
persistentVolumeReclaimPolicyPVC 삭제 후 PV 처리 방식

Access Modes

PV가 어떤 방식으로 마운트될 수 있는지를 정의한다.

모드약어설명
ReadWriteOnceRWO단일 노드에서 읽기/쓰기 가능
ReadOnlyManyROX여러 노드에서 읽기 전용 마운트
ReadWriteManyRWX여러 노드에서 읽기/쓰기 가능
ReadWriteOncePodRWOP단일 Pod에서만 읽기/쓰기 (Kubernetes 1.22+)

EBS 같은 블록 스토리지는 보통 RWO만 지원하고, NFS 같은 네트워크 스토리지는 RWX를 지원한다.


Reclaim Policy

PVC가 삭제될 때 PV를 어떻게 처리할지 결정한다.

정책설명
RetainPV를 그대로 보존. 관리자가 수동으로 처리해야 함
DeletePV와 외부 스토리지(EBS, GCE PD 등)를 모두 삭제
Recycle데이터를 삭제하고 PV를 재사용 가능 상태로 (deprecated)

프로덕션에서는 중요한 데이터가 있을 경우 Retain을 써서 실수로 데이터가 삭제되는 걸 막는 게 안전하다.


PersistentVolumeClaim (PVC)

PVC는 Pod가 PV에 대한 요구 사항을 선언하는 방식이다. 사용자는 PV의 구현 세부 사항을 몰라도 PVC를 통해 필요한 용량과 접근 방식만 명시하면 Kubernetes가 적합한 PV를 바인딩해준다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard

PVC를 Pod에서 사용하기

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: storage
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: my-pvc

PV-PVC 바인딩 프로세스

PVC 생성 요청
    ↓
Kubernetes Control Plane이 조건에 맞는 PV 탐색
  (capacity, accessModes, storageClassName 일치 여부)
    ↓
적합한 PV 발견 → 바인딩 (1:1 관계)
    ↓
PVC 상태: Pending → Bound
    ↓
Pod가 PVC를 마운트하여 사용

바인딩은 1:1 관계다. PV 하나에 PVC 하나만 바인딩될 수 있다. PVC가 삭제되면 PV는 Released 상태가 되며 Reclaim Policy에 따라 처리된다.


StorageClass와 동적 프로비저닝

정적 프로비저닝(관리자가 PV를 미리 만들어두는 방식)은 스토리지 요청이 많아질수록 관리 부담이 커진다. StorageClass를 사용하면 PVC 생성 시 자동으로 PV를 프로비저닝할 수 있다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

주요 필드:

필드설명
provisioner스토리지를 생성할 CSI 드라이버 또는 내장 플러그인
parametersprovisioner에 전달할 설정값 (벤더마다 다름)
reclaimPolicy이 StorageClass로 만들어진 PV의 기본 Reclaim 정책
allowVolumeExpansionPVC 용량 확장 허용 여부
volumeBindingModePV 바인딩 타이밍

volumeBindingMode

  • Immediate: PVC 생성 즉시 PV 프로비저닝 및 바인딩
  • WaitForFirstConsumer: PVC를 사용하는 Pod가 스케줄링될 때 해당 노드의 AZ(가용 영역)를 고려해 PV 생성

멀티 AZ 환경에서는 WaitForFirstConsumer를 써야 Pod와 PV가 같은 AZ에 생성된다. 그렇지 않으면 Pod가 스케줄된 AZ와 다른 AZ에 PV가 생성되어 마운트 실패가 발생할 수 있다.


동적 프로비저닝 흐름

사용자가 PVC 생성 (storageClassName: fast-ssd 명시)
    ↓
Kubernetes가 fast-ssd StorageClass 확인
    ↓
provisioner(aws-ebs CSI 드라이버)에 볼륨 생성 요청
    ↓
실제 EBS 볼륨 생성
    ↓
PV 오브젝트 자동 생성 및 PVC에 바인딩
    ↓
Pod에서 사용 가능

CSI (Container Storage Interface)

CSI는 Kubernetes와 외부 스토리지 시스템 사이의 표준 인터페이스다. 과거에는 스토리지 드라이버가 Kubernetes 코어에 포함되어 있었지만, CSI를 통해 벤더가 플러그인을 독립적으로 개발 및 배포할 수 있게 됐다.

주요 CSI 드라이버 예시:

드라이버설명
ebs.csi.aws.comAWS EBS
efs.csi.aws.comAWS EFS (NFS, RWX 지원)
pd.csi.storage.gke.ioGCE Persistent Disk
disk.csi.azure.comAzure Disk
rook-ceph.rbd.csi.ceph.comRook-Ceph (온프레미스)

볼륨 스냅샷

Kubernetes 1.17+ 에서 VolumeSnapshot을 통해 PVC의 스냅샷을 찍고 복원할 수 있다.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: db-snapshot
spec:
  volumeSnapshotClassName: csi-aws-vsc
  source:
    persistentVolumeClaimName: db-pvc

스냅샷에서 PVC를 복원하려면:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pvc-restored
spec:
  dataSource:
    name: db-snapshot
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

StatefulSet과 volumeClaimTemplates

StatefulSet에서는 각 Pod마다 별도의 PVC가 필요하다. volumeClaimTemplates를 사용하면 Pod 생성 시 자동으로 PVC가 만들어진다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 20Gi

이렇게 하면 mysql-0, mysql-1, mysql-2 각 Pod에 대해 data-mysql-0, data-mysql-1, data-mysql-2라는 PVC가 각각 생성된다. StatefulSet을 삭제해도 이 PVC들은 자동으로 삭제되지 않아 데이터를 보존할 수 있다.


용량 확장

StorageClass에 allowVolumeExpansion: true가 설정되어 있다면 PVC의 용량을 늘릴 수 있다.

kubectl patch pvc my-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

단, 볼륨 축소(shrink)는 지원하지 않는다.


정리

개념역할
PV실제 스토리지 리소스 (클러스터 수준)
PVCPod의 스토리지 요청 명세 (네임스페이스 수준)
StorageClass동적 PV 프로비저닝 방법 정의
CSI 드라이버외부 스토리지와 Kubernetes를 연결하는 표준 인터페이스

스토리지 설계 시 핵심 포인트:

  • RWX가 필요하면 EFS, NFS, Ceph 같은 분산 파일시스템을 써야 한다
  • 멀티 AZ 환경에서는 WaitForFirstConsumer 바인딩 모드 필수
  • 프로덕션 데이터는 Reclaim Policy를 Retain으로 설정해 실수로 삭제되지 않도록 한다
  • StatefulSet의 volumeClaimTemplates으로 Pod별 독립 스토리지를 간편하게 관리할 수 있다