LabHub

CKA — 쿠버네티스 관리자 · 스토리지 · 이론

볼륨은 세 층으로 나뉘어 있다

LabHub 에서 이어서 보기

한 줄 요약

쿠버네티스 스토리지는 StorageClass(청사진) → PersistentVolume(실물) → PersistentVolumeClaim(요구서) 세 층입니다. 이 셋이 서로 다른 사람의 관심사를 대변하기 때문에 나뉘어 있고, 어긋나면 PVC 가 조용히 Pending 에 머뭅니다.

왜 이게 필요했나

개발자는 "1Gi 짜리, 여러 노드에서 동시에 쓸 수 있는 디스크" 를 원합니다. 관리자는 "그건 NAS 의 /volume1/k8s 아래 NFS v4.1 로, hard 마운트에 nconnect 4" 라는 결정을 합니다. 이 두 관심사를 한 오브젝트에 섞으면 개발자가 스토리지 벤더를 알아야 합니다.

그래서 PVC 는 요구만 적고, StorageClass 가 그 요구를 실물로 바꾸는 방법을 정의하고, PV 가 실물을 대표합니다. 바인딩 조건은 단순합니다. storageClassName 이 같고, PV 의 accessModes 가 PVC 요구를 포함하고, PV 용량이 PVC 요청 이상이면 묶입니다. 하나라도 어긋나면 아무 에러 없이 Pending 입니다.

CSI 가 등장한 이유도 같은 분리 욕구입니다. 예전에는 스토리지 플러그인이 쿠버네티스 코어 코드 안에 있어서(in-tree) 새 스토리지를 붙이려면 쿠버네티스 자체를 고쳐야 했습니다. CSI 는 그 결합을 끊었고, 드라이버는 컨트롤러(Deployment, 볼륨 생성/삭제/확장/스냅샷)와 노드 플러그인(DaemonSet, 마운트/언마운트)으로 나뉩니다. 노드 플러그인이 DaemonSet 인 이유는 파드가 스케줄될 수 있는 모든 노드에 있어야 하기 때문입니다.

어떻게 동작하나

StorageClass 에서 실수하면 되돌리기 어려운 필드가 셋 있습니다.

| 필드 | 뜻 | 실수하면 |
| --- | --- | --- |
| reclaimPolicy | PVC 삭제 후 PV 의 운명 | Delete 로 두면 데이터까지 사라짐 |
| volumeBindingMode | 언제 볼륨을 만드나 | Immediate 면 파드와 다른 존에 볼륨이 생길 수 있음 |
| allowVolumeExpansion | 나중에 늘릴 수 있나 | false 로 만든 클래스의 PVC 는 영원히 확장 불가 |

WaitForFirstConsumer 는 파드가 스케줄될 노드를 안 다음 볼륨을 만듭니다. 멀티 존 환경에서 필수입니다.

접근 모드는 네 가지입니다. RWO(한 노드에서 읽기/쓰기), ROX(여러 노드 읽기 전용), RWX(여러 노드 읽기/쓰기), RWOP(단일 파드 전용). 블록 스토리지는 보통 RWO 까지이고, RWX 는 NFS 같은 파일 스토리지의 영역입니다.

기본 StorageClass 가 지정된 클러스터에서 그 클래스를 쓰지 않으려면 storageClassName 을 생략하는 것이 아니라 빈 문자열로 명시해야 합니다. 생략하면 기본값이 주입됩니다. 이 구분은 시험에도 나옵니다.

현장에서 만나는 모습

사례 1 — 마운트를 먼저 확인하라. 홈랩 클러스터에 kubectl get sc 를 쳤더니 No resources found 였습니다. StorageClass 가 없으면 PVC 는 영원히 Pending 이고, 상태를 가진 워크로드를 하나도 올릴 수 없습니다. 집에 있던 Synology NAS 를 붙이기로 했는데, 드라이버를 깔기 전에 파드에서 NFS 마운트가 되는지부터 확인했습니다. 첫 시도는 실패했습니다.

mount.nfs: Operation not permitted for 10.0.0.109:/volume1/k8s on /mnt/t

privileged: true 를 줬는데도 거부됐습니다. 빠진 것은 hostNetwork: true 였습니다. NFS 마운트는 rpcbind 와 통신하며 1024 미만의 예약 포트를 소스 포트로 쓰는데, 파드 네트워크 네임스페이스 안에서는 이 동작이 제약을 받습니다. privileged 는 capability 를 주지만 네트워크 네임스페이스 문제는 해결하지 못합니다. 순서를 지킨 덕에 원인이 드라이버가 아니라는 걸 곧바로 알 수 있었습니다.

사례 2 — 클래스를 용도별로 나눈다. csi-driver-nfs 4.13.4 를 올린 뒤 StorageClass 를 두 벌 만들었습니다.

hard 를 고른 이유가 특히 중요합니다. soft 는 타임아웃 시 I/O 오류를 애플리케이션에 반환하는데, 데이터베이스가 쓰기 도중 오류를 받으면 조용한 데이터 손상으로 이어질 수 있습니다. hard 는 서버가 돌아올 때까지 무한 재시도하므로 데이터는 안전한 대신, NAS 장애 시 파드가 오류 없이 멈춘 것처럼 보여 원인 파악이 어려워집니다. 모든 스토리지 설정은 이런 맞교환입니다.

subDir 패턴도 네임스페이스-PVC이름-PV이름 으로 두었습니다. PV 이름만 쓰면 NAS 파일 탐색기에서 pvc-da6bb53d-... 같은 UUID 뿐이라 나중에 무엇을 지워도 되는지 알 수 없습니다.

사례 3 — 용량은 강제되지 않는다. PVC 에 2Gi 를 요청했지만 NFS 에는 이를 강제할 수단이 없습니다. 파드가 100GB 를 써도 막히지 않습니다. PVC 의 용량은 스케줄링과 회계 목적의 메타데이터일 뿐이고 실제 제한은 백엔드가 해야 합니다. 블록 스토리지는 볼륨 크기가 곧 물리 한계라 자연스럽게 강제되지만, NFS 하위 디렉터리는 그렇지 않습니다.

그리고 archive 정책의 대가도 실물로 봤습니다. NFS export 루트를 열어 보니 이전 클러스터가 남긴 디렉터리가 32개, Prometheus 와 OpenSearch 데이터가 그대로였고 19.9T 중 9.4T 를 쓰고 있었습니다. 안전한 대신 아무도 지우지 않으면 계속 쌓입니다.

다음 실습에서 할 것

StorageClass 두 벌을 만들어 세 필드의 차이를 확인하고, PV 와 PVC 를 정적으로 바인딩하고, RWX 볼륨을 만들고, StatefulSet 의 volumeClaimTemplates 가 PVC 를 자동 생성하는 것을 봅니다. 마지막 단계는 기본 StorageClass 를 명시적으로 거부하는 방법입니다.