세 가지 핵심 — PV, PVC, StorageClass
스토리지는 세 리소스가 협업한다.
- PV (PersistentVolume) — 실제 데이터가 저장되는 공간. AWS EBS, 로컬 디스크 같은 진짜 스토리지 인프라. 파드 생명주기와 독립적이라 파드가 죽어도 데이터는 남는다.
- PVC (PersistentVolumeClaim) — 스토리지 요청서. "1Gi짜리 RWO 볼륨 주세요"라는 주문서.
- StorageClass — 어떤 방식으로 볼륨을 만들지 정의한 규칙. 운영 환경에선 이걸로 동적 할당을 한다.
사용자는 PVC로 요청하고, StorageClass 규칙에 따라 PV가 자동으로 만들어져(동적 프로비저닝) PVC에 바인딩된다. k3s를 설치하면 local-path StorageClass가 기본으로 생성돼 있다.
동적 프로비저닝의 흐름
가장 주의해야할 점은 PVC를 만들었다고 바로 PV가 생기는 게 아니다. PVC를 사용하는 파드가 떠야 비로소 PV가 만들어지고 바인딩된다(StorageClass의 WaitForFirstConsumer 모드인 경우). 그래서 PVC만 만들면 처음엔 Pending 상태로 보인다.
- StorageClass 정의(또는 기본 제공)
- PVC(요청서) 생성 → 아직
Pending - PVC를 마운트하는 파드 생성 → 이때 PV 생성·바인딩 →
Bound - 파드가 삭제돼도 PV는 남음.
reclaimPolicy에 따라 PVC 삭제 시 함께 삭제(Delete)할지 유지(Retain)할지 결정
local-path의 경우 local-path-provisioner 파드가 API 서버를 감시하다가, 자기가 관리하는 StorageClass를 요청하는 PVC가 생기면 PV를 만들어 관리한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-path
reclaimPolicy와 volumeBindingMode
| 설정 | 의미 |
|---|---|
| Delete | PVC가 삭제되면 PV와 실제 스토리지까지 자동 삭제 |
| Retain | PVC가 삭제돼도 PV·데이터 유지. 수동으로 정리해야 함 |
| Immediate | PVC 생성 즉시 PV 프로비저닝·바인딩 (파드 스케줄링 미고려) |
| WaitForFirstConsumer | PVC를 쓰는 파드가 스케줄될 때 그 노드 조건을 고려해 PV 생성. 로컬 스토리지는 Pod와 PV가 같은 노드에 있어야 하므로 이 모드를 쓴다. |
접근 모드 — RWO, RWX, RWOP
접근 모드는 "몇 개의 노드가 동시에, 어떻게 마운트할 수 있는가"를 정한다. 여기서 핵심은 노드 기준이라는 점이다(파드 기준이 아니다).
| 모드 | 설명 |
|---|---|
| ReadWriteOnce (RWO) | 하나의 노드에서만 읽기/쓰기 마운트. 같은 노드의 여러 파드는 가능. 다른 노드가 마운트 시도하면 Multi-Attach error. 블록 스토리지가 보통 RWO. |
| ReadWriteMany (RWX) | 여러 노드에서 동시 읽기/쓰기 마운트(동시성 문제 주의). 파일 스토리지(NFS 등)가 보통 RWX. |
| ReadWriteOncePod (RWOP) | 단 하나의 파드에서만 읽기/쓰기 마운트. Kubernetes 1.22+ |
- 파드 정의의 볼륨 이름·마운트 경로 불일치
- 파일시스템 권한(uid/gid)이 안 맞아 쓰기 불가 →
securityContext로 조정 - RWO인데 다른 노드가 마운트 시도 → RWX 지원 StorageClass로 변경
OpenEBS 호스트패스
OpenEBS 호스트패스는 파드가 떠 있는 호스트 노드의 특정 디렉터리를 볼륨으로 쓴다. 데이터가 그 노드에만 있으므로 다른 노드로 파드를 옮기는 게 불가능하다는 제약이 있다. 기본 저장 위치는 /var/openebs/local이다.
kubectl create ns openebs
kubectl apply -f https://openebs.github.io/charts/openebs-operator.yaml
kubectl get pods -n openebs
kubectl get storageclass # openebs-device, openebs-hostpath 확인
데이터 영속성 검증은 간단하다. 파드가 데이터를 쓰게 한 뒤, 파드를 지우고 새 파드가 같은 PVC를 마운트했을 때 이전 데이터가 남아 있는지 보면 된다.
kubectl exec [old-pod] -- cat /data/pod-out.txt # 데이터 확인
kubectl delete pod [old-pod]
kubectl exec [new-pod] -- cat /data/pod-out.txt # 이전 기록이 그대로 남아 있음
PVC/PV를 지우려면 그걸 쓰는 파드를 먼저 지워야 한다. PVC를 삭제하면 reclaimPolicy에 따라 PV도 함께 삭제될 수 있다.
IOPS 측정 — kubestr + fio
스토리지 성능은 IOPS(초당 I/O 횟수)로 본다. 쿠버네티스 환경에선 kubestr로 특정 StorageClass에 fio 벤치마크를 돌릴 수 있다.
kubestr fio -f fio-read.fio -s openebs-hostpath
# 노드 점검(drain): 그 노드의 파드를 다 빼냄
kubectl drain k8s-node-02 --ignore-daemonsets --delete-emptydir-data
# PV 없는 파드는 다른 노드로 옮겨가지만,
# 호스트패스 PV가 있는 파드는 그 노드에 묶여 Pending이 됨
kubectl uncordon k8s-node-02 # 노드 복귀
- 쿠버네티스란 무엇인가
- k3s로 클러스터 구성하기
- 쿠버네티스 네트워킹 — Service, DNS, MetalLB, Ingress
- 쿠버네티스 스토리지 — PV/PVC/StorageClass, OpenEBS
- Helm으로 애플리케이션 배포하기
- CI/CD + 모니터링 스택 구축