K8s PV·PVC·StorageClass 심화

Kubernetes 스토리지 아키텍처

Kubernetes에서 컨테이너는 기본적으로 ephemeral(휘발성)입니다. Pod가 재시작되면 파일시스템의 모든 데이터가 사라집니다. 데이터베이스, 파일 업로드, 로그 등 영속적 데이터를 다루려면 PersistentVolume(PV), PersistentVolumeClaim(PVC), StorageClass를 이해해야 합니다.

이 글에서는 스토리지 3계층 아키텍처, 동적 프로비저닝, 볼륨 모드와 접근 모드, CSI 드라이버, 그리고 StatefulSet과의 결합 패턴을 다룹니다.

PV · PVC · StorageClass 관계

StorageClass (관리자 정의)
  ↓ 동적 프로비저닝
PersistentVolume (실제 스토리지)
  ↑ 바인딩
PersistentVolumeClaim (개발자 요청)
  ↑ 마운트
Pod → Container
  • PersistentVolume(PV): 클러스터 수준의 스토리지 리소스. NFS, AWS EBS, GCE PD 등 실제 볼륨
  • PersistentVolumeClaim(PVC): 개발자가 스토리지를 요청하는 선언. “10Gi SSD 주세요”
  • StorageClass: 스토리지 프로비저너와 파라미터 정의. PVC 생성 시 자동으로 PV 생성

StorageClass: 동적 프로비저닝의 핵심

# AWS EBS StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"
provisioner: ebs.csi.aws.com
parameters:
  type: gp3           # EBS 볼륨 타입
  iops: "5000"
  throughput: "250"    # MB/s
  encrypted: "true"
  fsType: ext4
reclaimPolicy: Retain  # PVC 삭제 후 PV 유지
allowVolumeExpansion: true  # 온라인 볼륨 확장 허용
volumeBindingMode: WaitForFirstConsumer  # Pod 스케줄링 시점에 바인딩
mountOptions:
  - noatime
  - nodiratime

주요 설정 상세

  • reclaimPolicy:
    • Delete (기본): PVC 삭제 시 PV와 실제 볼륨도 삭제
    • Retain: PVC 삭제 후에도 PV와 데이터 보존. 프로덕션 DB 필수
  • volumeBindingMode:
    • Immediate: PVC 생성 즉시 PV 바인딩 (AZ 불일치 위험)
    • WaitForFirstConsumer: Pod가 스케줄링될 때 바인딩 (AZ 일치 보장)
  • allowVolumeExpansion: true면 PVC 용량을 늘릴 수 있음 (축소는 불가)
# 다양한 환경별 StorageClass
---
# 개발용: 저렴한 HDD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: dev-standard
provisioner: ebs.csi.aws.com
parameters:
  type: gp2
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
---
# 프로덕션 DB: 고성능 SSD + 보존
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: prod-database
provisioner: ebs.csi.aws.com
parameters:
  type: io2
  iops: "10000"
  encrypted: "true"
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

PersistentVolumeClaim 실전 패턴

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
  namespace: production
spec:
  storageClassName: prod-database
  accessModes:
    - ReadWriteOnce     # 단일 노드에서만 읽기/쓰기
  resources:
    requests:
      storage: 100Gi

Access Modes 정리

  • ReadWriteOnce (RWO): 단일 노드에서 읽기/쓰기. EBS, GCE PD 등 블록 스토리지
  • ReadOnlyMany (ROX): 여러 노드에서 읽기 전용
  • ReadWriteMany (RWX): 여러 노드에서 읽기/쓰기. NFS, EFS, CephFS 등 파일 스토리지
  • ReadWriteOncePod (RWOP): K8s 1.27+ 단일 Pod 전용. 가장 안전한 격리

Pod에서 PVC 마운트

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels: { app: postgres }
  template:
    metadata:
      labels: { app: postgres }
    spec:
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
          env:
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
            - name: config
              mountPath: /etc/postgresql/conf.d
              readOnly: true
          resources:
            requests: { cpu: 500m, memory: 1Gi }
            limits: { cpu: "2", memory: 4Gi }
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: postgres-data
        - name: config
          configMap:
            name: postgres-config

StatefulSet + volumeClaimTemplates

StatefulSet은 volumeClaimTemplates로 Pod마다 고유한 PVC를 자동 생성합니다. 노드 관리 시에도 PVC가 Pod를 따라다닙니다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
spec:
  serviceName: redis-cluster
  replicas: 6
  selector:
    matchLabels: { app: redis-cluster }
  template:
    metadata:
      labels: { app: redis-cluster }
    spec:
      containers:
        - name: redis
          image: redis:7
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        storageClassName: fast-ssd
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi

이렇게 하면 data-redis-cluster-0, data-redis-cluster-1 … 각 Pod마다 독립적인 PVC가 생성됩니다.

볼륨 확장 (온라인 리사이즈)

# PVC 용량 늘리기 (축소 불가)
kubectl patch pvc postgres-data -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'

# 확장 상태 확인
kubectl describe pvc postgres-data
# Conditions:
#   Type: FileSystemResizePending → Resizing → 완료 후 사라짐

# 파일시스템 리사이즈는 CSI 드라이버가 자동 처리 (Pod 재시작 불필요)

볼륨 스냅샷

# VolumeSnapshot으로 백업
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: postgres-snapshot-20260227
spec:
  volumeSnapshotClassName: csi-aws-snapclass
  source:
    persistentVolumeClaimName: postgres-data
---
# 스냅샷에서 새 PVC 복원
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data-restored
spec:
  storageClassName: prod-database
  dataSource:
    name: postgres-snapshot-20260227
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 100Gi

모니터링

# kubelet이 노출하는 PVC 메트릭
kubelet_volume_stats_capacity_bytes      # 총 용량
kubelet_volume_stats_used_bytes          # 사용량
kubelet_volume_stats_available_bytes     # 남은 용량
kubelet_volume_stats_inodes              # inode 총수
kubelet_volume_stats_inodes_used         # inode 사용수

# Prometheus Alert: 볼륨 90% 사용 시 경고
groups:
  - name: volume-alerts
    rules:
      - alert: PVCAlmostFull
        expr: |
          kubelet_volume_stats_used_bytes
          / kubelet_volume_stats_capacity_bytes
          > 0.9
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "PVC {{ $labels.persistentvolumeclaim }} 90% 초과"

ResourceQuota로 네임스페이스별 스토리지 총량도 제한할 수 있습니다.

흔한 실수

1. volumeBindingMode: Immediate + 멀티 AZ

# ❌ PV가 us-east-1a에 생성되었는데 Pod가 us-east-1b에 스케줄링
#    → Pod가 Pending 상태로 멈춤
# ✅ WaitForFirstConsumer 사용

2. reclaimPolicy: Delete로 프로덕션 DB 운영

# ❌ PVC 실수로 삭제 → 데이터 영구 손실
# ✅ 프로덕션 DB는 반드시 Retain + 정기 스냅샷

3. RWO 볼륨 + Deployment replicas > 1

# ❌ EBS는 RWO → 여러 노드의 Pod에서 동시 마운트 불가
# ✅ StatefulSet 사용 또는 RWX 스토리지(EFS) 전환

마무리

Kubernetes 스토리지의 핵심 원칙: StorageClass로 동적 프로비저닝, WaitForFirstConsumer로 AZ 일치, 프로덕션은 Retain 정책, VolumeSnapshot으로 백업. Helm Chart에서 StorageClass를 values로 추상화하면 환경별(dev/staging/prod) 스토리지 설정을 유연하게 관리할 수 있습니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux