StorageClass — 동적 프로비저닝으로 볼륨을 자동 생성하는 원리
PVC를 만들 때마다 관리자가 수동으로 PV를 생성해야 한다면, 수백 개의 서비스가 있는 클러스터에서는 어떻게 관리할 수 있을까요?
Static Provisioning은 관리자가 미리 PV를 생성해 두는 방식이지만, 서비스가 많아지면 관리가 불가능해집니다. StorageClass와 동적 프로비저닝은 PVC를 생성하면 자동으로 PV가 만들어지게 해서 이 문제를 해결합니다.
StorageClass란?
StorageClass는 "어떤 스토리지를, 어떤 방식으로 생성할지"를 정의하는 오브젝트입니다. PVC가 StorageClass를 지정하면, 해당 설정에 따라 PV가 자동으로 프로비저닝됩니다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # CSI 드라이버
parameters: # 프로비저너에 전달할 파라미터
type: gp3
iops: "3000"
throughput: "125"
reclaimPolicy: Delete # PVC 삭제 시 PV도 삭제
volumeBindingMode: WaitForFirstConsumer # Pod 스케줄 후 바인딩
allowVolumeExpansion: true # 볼륨 확장 허용
mountOptions: # 마운트 옵션
- discard
- noatime
CSI (Container Storage Interface)
CSI는 Kubernetes와 스토리지 시스템을 연결하는 표준 인터페이스입니다. 이전에는 스토리지 드라이버가 Kubernetes 코어에 포함되어 있었지만(in-tree), CSI를 통해 외부 플러그인(out-of-tree)으로 분리되었습니다.
CSI 아키텍처
PVC 생성 → API Server → CSI Controller → 스토리지 API 호출 → 볼륨 생성
↓
Pod 스케줄 → kubelet → CSI Node Plugin → 볼륨 연결(Attach) → 마운트
CSI 드라이버는 두 가지 컴포넌트로 구성됩니다.
- Controller Plugin: 볼륨 생성/삭제/스냅샷 (Deployment로 실행)
- Node Plugin: 볼륨 연결/마운트/포맷 (DaemonSet으로 실행)
주요 CSI 드라이버
| 프로비저너 | 스토리지 | 특징 |
|---|---|---|
ebs.csi.aws.com | AWS EBS | 블록 스토리지, RWO만 |
efs.csi.aws.com | AWS EFS | 파일 스토리지, RWX 지원 |
pd.csi.storage.gke.io | GCP Persistent Disk | 블록 스토리지 |
disk.csi.azure.com | Azure Disk | 블록 스토리지 |
nfs.csi.k8s.io | NFS | 파일 스토리지, RWX 지원 |
# 클러스터에 설치된 CSI 드라이버 확인
kubectl get csidrivers
클라우드별 StorageClass 예제
AWS
# 범용 SSD (gp3)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
데이터베이스 같은 고성능 워크로드에는 io2 타입을 사용하고, reclaimPolicy를 Retain으로 설정하여 데이터 안전성을 확보합니다.
---
# 고성능 SSD (io2)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: io2-high-perf
provisioner: ebs.csi.aws.com
parameters:
type: io2
iops: "10000"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
GCP
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
volumeBindingMode: WaitForFirstConsumer
NFS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
provisioner: nfs.csi.k8s.io
parameters:
server: nfs-server.default.svc.cluster.local
share: /exports
volumeBindingMode
PV가 언제 생성되고 바인딩되는지를 제어하는 중요한 설정입니다.
Immediate (기본값)
PVC가 생성되면 즉시 PV를 생성하고 바인딩합니다.
PVC 생성 → 즉시 PV 프로비저닝 → 바인딩
(AZ를 모름 → Pod과 다른 AZ에 생성될 수 있음!)
** 문제 **: PV가 us-east-1a에 생성되었는데 Pod이 us-east-1b에 스케줄되면 마운트가 실패합니다.
WaitForFirstConsumer (권장)
Pod이 스케줄될 때까지 PV 생성을 지연합니다.
PVC 생성 → Pending 상태 → Pod 스케줄 → Pod의 노드/AZ에서 PV 생성 → 바인딩
volumeBindingMode: WaitForFirstConsumer # 거의 항상 이것을 사용
이 설정으로 Pod과 PV의 토폴로지(AZ) 불일치 문제를 방지할 수 있습니다.
Default StorageClass
storageClassName을 지정하지 않은 PVC에 자동 적용됩니다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
annotations:
storageclass.kubernetes.io/is-default-class: "true" # Default 설정
provisioner: ebs.csi.aws.com
parameters:
type: gp3
# Default StorageClass 확인 (default) 표시됨
kubectl get sc
# NAME PROVISIONER RECLAIM VOLUMEBINDINGMODE
# standard (default) ebs.csi.aws.com Delete WaitForFirstConsumer
# fast-ssd ebs.csi.aws.com Retain WaitForFirstConsumer
동적 프로비저닝 전체 흐름
# 1. StorageClass 정의 (한 번만)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
볼륨 관련 설정을 이어서 정의합니다.
# 2. PVC 생성 (개발자)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
PVC가 생성되면 StorageClass에 따라 PV가 자동으로 프로비저닝되고, Deployment에서 해당 PVC를 볼륨으로 마운트하여 사용합니다.
---
# 3. Pod에서 사용 (개발자)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
볼륨 관련 설정을 이어서 정의합니다.
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
StorageClass 설계 전략
환경에 맞는 StorageClass를 미리 정의해 두면 개발자가 용도에 따라 선택할 수 있습니다.
# 개발/테스트용 — 저비용
fast-ssd:
type: gp3
reclaimPolicy: Delete
# 프로덕션 일반용
standard-prod:
type: gp3
reclaimPolicy: Retain
encrypted: true
고성능 DB 워크로드와 여러 Pod이 동시 접근해야 하는 경우를 위한 StorageClass도 미리 준비합니다.
# 프로덕션 DB용 — 고성능
high-perf-prod:
type: io2
iops: 10000
reclaimPolicy: Retain
encrypted: true
# 공유 파일시스템 (RWX)
shared-efs:
provisioner: efs.csi.aws.com
reclaimPolicy: Retain
트러블슈팅
# PVC가 Pending인 경우
kubectl describe pvc app-data
# 가능한 원인:
# - StorageClass가 존재하지 않음
# - CSI 드라이버가 설치되지 않음
# - AZ에 사용 가능한 볼륨이 없음
# - 할당량 초과
# CSI 드라이버 Pod 상태 확인
kubectl get pods -n kube-system | grep csi
# 볼륨 이벤트 확인
kubectl get events --field-selector reason=ProvisioningFailed
주의할 점
1. reclaimPolicy가 Delete인 StorageClass로 StatefulSet을 삭제하면 데이터가 사라진다
기본 StorageClass의 reclaimPolicy가 Delete인 경우가 많습니다. StatefulSet을 삭제하고 PVC도 삭제하면 연결된 PV(실제 디스크)가 자동으로 삭제됩니다. 프로덕션 데이터베이스용 StorageClass는 반드시 reclaimPolicy: Retain으로 설정하세요.
2. volumeBindingMode가 Immediate면 PV가 Pod과 다른 AZ에 생성될 수 있다
Immediate 모드에서는 PVC 생성 즉시 볼륨이 프로비저닝되는데, 이때 Pod이 어느 노드에 스케줄링될지 아직 결정되지 않은 상태입니다. 볼륨이 AZ-a에 생성되었는데 Pod이 AZ-b에 배치되면 마운트가 실패합니다. WaitForFirstConsumer로 설정하면 Pod이 스케줄링된 후에 같은 AZ에 볼륨을 생성합니다.
3. allowVolumeExpansion이 false인 StorageClass는 디스크 확장이 불가능하다
디스크가 가득 찼을 때 PVC의 용량을 늘리려고 spec.resources.requests.storage를 수정해도, StorageClass에 allowVolumeExpansion: true가 설정되어 있지 않으면 거부됩니다. 이미 생성된 PV의 StorageClass를 변경할 수는 없으므로, 처음부터 확장 가능하게 설정해두세요.
정리
StorageClass는 동적 프로비저닝의 핵심으로, PVC를 생성하면 자동으로 PV가 만들어지게 합니다. CSI 드라이버가 실제 스토리지 생성을 담당하고, volumeBindingMode: WaitForFirstConsumer로 토폴로지 문제를 방지합니다. 환경별/용도별 StorageClass를 미리 설계해 두면 스토리지 관리가 크게 단순화됩니다.