6.3 StorageClass와 동적 프로비저닝
수동 프로비저닝은 관리자가 수요를 미리 파악해야 성립한다. 개발자들이 요청할 크기와 개수를 예측해 PV를 만들어 두고, 맞는 것이 없으면 요청이 들어올 때마다 하나씩 만들어 준다. 팀이 늘고 PVC가 수백 개가 되면 이 방식은 유지되지 않는다. 동적 프로비저닝(dynamic provisioning)은 순서를 뒤집는다. PVC가 먼저 오고, 클러스터가 그 요청에 맞는 볼륨을 그 자리에서 만든다. 이때 “어떻게 만들 것인가"를 정의해 두는 리소스가 StorageClass다.
StorageClass: 볼륨을 만드는 레시피
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "6000"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: trueprovisioner는 볼륨을 실제로 만들 드라이버를 지정한다. parameters는 그 드라이버에게 전달되는 고유 옵션으로, 디스크 종류, IOPS, 파일시스템, 암호화 여부 같은 것이 들어간다. key 목록은 Kubernetes가 아니라 각 드라이버의 문서가 정의한다. reclaimPolicy는 이 클래스로 만들어질 PV들의 reclaim policy이며 기본값이 Delete다.
provisioner와 parameters는 생성 후 수정되지 않는 필드라, 정책을 바꾸려면 새 클래스를 만들어 전환한다. 그래서 클래스 이름은 gp3-immediate처럼 구현을 드러내기보다 fast-ssd, standard처럼 성격을 드러내는 편이 오래 유지된다.
사용자 쪽은 PVC에 storageClassName: fast-ssd만 적으면 된다. 조건에 맞는 기존 PV를 찾는 대신, provisioner가 스토리지 시스템에 새 볼륨을 만들고 그에 대응하는 PV 오브젝트를 생성해 PVC와 바인딩한다. 관리자가 PV를 한 장씩 만들던 일이 클래스 정의 한 번으로 대체된다.
volumeBindingMode: 프로비저닝 시점과 스케줄링
Immediate(기본값)는 PVC가 생성되는 즉시 볼륨을 만들고 바인딩한다. 스토리지에 위치 개념이 없다면 문제가 없지만, 클라우드 블록 스토리지처럼 볼륨이 특정 availability zone에 속하는 경우 곤란해진다. PVC 생성 시점에는 Pod가 어느 노드로 갈지 아무도 모르는데 볼륨의 zone은 이미 확정된다. 이후 Pod의 리소스 요구나 affinity 때문에 스케줄러가 다른 zone의 노드를 선택해야 하는 상황이면, 볼륨이 있는 zone과 Pod가 갈 노드가 어긋나 Pod가 Pending에 빠진다.
WaitForFirstConsumer는 이 순서를 고친다. PVC를 쓰는 첫 Pod가 스케줄될 때까지 프로비저닝을 미루고, 스케줄러가 리소스, affinity, taint까지 모두 고려해 노드를 확정한 뒤 그 노드의 토폴로지에 볼륨을 만든다. 토폴로지 제약이 있는 스토리지에서는 사실상 이 모드가 표준이다.
flowchart TD
P["PVC 생성"] --> M{"volumeBindingMode"}
M -->|"Immediate"| I["즉시 프로비저닝<br/>zone 선확정"]
I -.->|"zone 어긋나면"| F["Pod Pending"]
M -->|"WaitForFirstConsumer"| W["첫 Pod 스케줄 대기"]
W --> S["스케줄러가 노드 확정"]
S --> C["해당 zone에 볼륨 생성"]
allowedTopologies를 추가하면 프로비저닝을 허용할 zone 자체를 제한하는 것도 가능하다. 특정 zone에만 워커 노드를 두는 클러스터에서 볼륨이 노드 없는 zone에 만들어지는 일을 차단하는 용도다.
기본 StorageClass
PVC가 storageClassName을 아예 적지 않으면, storageclass.kubernetes.io/is-default-class: "true" annotation이 붙은 기본 StorageClass가 적용된다. 반대로 storageClassName: ""(빈 문자열)를 명시하면 동적 프로비저닝을 쓰지 않겠다는 뜻이 되어, 클래스가 없는 기존 PV와의 정적 바인딩만 시도한다. 생략과 빈 문자열이 서로 다른 의미라는 점이 자주 혼동을 부른다.
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
fast-ssd (default) ebs.csi.aws.com Delete WaitForFirstConsumer true
standard ebs.csi.aws.com Delete WaitForFirstConsumer false기본 클래스가 여러 개 표시되어 있으면 가장 최근에 만들어진 것이 적용된다. 그리고 기본 클래스가 없어서 클래스 미지정으로 남아 있던 PVC는 이후 기본 클래스가 생기면 소급해서 그 클래스를 지정받는다. 이 소급 지정은 Kubernetes 1.28에서 GA가 됐다.
allowVolumeExpansion
allowVolumeExpansion: true인 클래스에서 만들어진 PVC만 나중에 용량을 키울 수 있다. 확장은 PVC의 spec.resources.requests.storage를 키우는 것으로 시작하며, 실제 진행 절차와 파일시스템 확장은 스냅샷과 볼륨 확장을 다루는 장에서 이어진다. 반대 방향은 없다. Kubernetes는 볼륨 축소를 지원하지 않으므로, 확장이 열려 있다고 처음부터 과하게 크게 만드는 것도 그대로 비용으로 남는다.