Files
project-infra/docs/standards/infra/storage-pvc.md
T

11 KiB

storage / PVC 기준

목적

PVC는 단순히 "데이터를 남기기 위한 옵션"이 아니라,

  • 어떤 워크로드가 상태를 가지는지
  • 그 상태의 수명과 복구 단위가 무엇인지
  • 어떤 storage class / access mode / reclaim policy / binding mode가 필요한지
  • snapshot / expansion 지원이 필요한지 를 먼저 고정한 뒤에 사용한다.

이 문서의 목표는 다음과 같다.

  • 상태 저장 워크로드와 무상태 워크로드를 저장소 기준으로 명확히 구분한다
  • separate PVC 남발을 막는다
  • K3s 기본 local-path provisioner의 운영 사용 범위를 통제한다
  • PVC lifecycle과 backup/restore 단위를 먼저 고정한다
  • StorageClass / VolumeSnapshotClass / reclaim policy / binding mode를 선언적으로 명시한다

공식 의미 (Kubernetes 기준)

  • PV는 클러스터의 저장소 리소스이며 Pod lifecycle과 독립적이다.
  • PVC는 저장소에 대한 요청(size, access mode, StorageClass, volumeMode 등)이다.
  • StorageClass는 동적 프로비저닝 파라미터, reclaimPolicy, allowVolumeExpansion, volumeBindingMode, mountOptions를 정의한다.
  • reclaimPolicy는 PV 해제 시 동작을 결정한다. 동적 프로비저닝 PV의 기본값은 Delete다. 운영 데이터가 있으면 StorageClass에서 Retain을 명시한다.
  • volumeBindingMode의 기본값은 Immediate이며, topology-aware / late-binding이 필요하면 WaitForFirstConsumer를 사용한다.
  • hostPath는 single-node testing 전용이다. 운영 클러스터에서 사용하지 않는다.
  • K3s는 Rancher Local Path Provisioner를 기본 제공해 노드 로컬 저장소를 사용할 수 있지만, RWO만 지원하고 snapshot/expansion은 지원하지 않는다.
  • VolumeSnapshot / VolumeSnapshotContent / VolumeSnapshotClass는 CSI snapshot을 위한 K8s API다. deletionPolicy: Retain / Delete를 정책에 맞게 선택한다.
  • StatefulSet은 persistentVolumeClaimRetentionPolicy로 삭제/스케일다운 시 PVC 보존 여부를 제어할 수 있다.

기본 규칙

1. PVC는 상태가 있을 때만 사용

다음 중 하나가 아니면 PVC를 붙이지 않는다.

  • 재시작 후에도 유지되어야 하는 데이터가 있음
  • Pod 교체와 무관하게 보존되어야 하는 파일/데이터가 있음
  • 복구 대상이 되는 저장 상태가 있음
  • 애플리케이션이 명시적으로 영속 저장소를 요구함

금지:

  • "혹시 몰라서" PVC 추가
  • 로그/캐시/임시 파일을 습관적으로 PVC에 저장
  • stateless 앱에 관성적으로 PVC 부착

2. PVC 존재만으로 StatefulSet을 결정하지 않는다

PVC가 있다고 무조건 StatefulSet은 아니다.

먼저 묻는다.

  • Pod마다 고유한 저장소가 필요한가?
  • stable network identity가 필요한가?
  • 순서 있는 확장/축소가 필요한가?

아니면:

  • Deployment + 단일 PVC(RWO, replicas 1) 또는 Deployment + RWX PVC 도 가능하다.

3. separate PVC는 "데이터 수명과 복구 단위가 다를 때만"

하나의 워크로드가 여러 PVC를 가져도 되는 경우는 아래와 같다.

  • 데이터 종류별 수명주기가 다름
  • backup/restore 단위가 다름
  • 성능 요구(StorageClass) 또는 IOPS 특성이 다름
  • 보안/접근 제어 단위가 다름
  • 장애 시 독립적으로 보존/삭제되어야 함

금지:

  • 디렉터리 몇 개를 기계적으로 PVC로 분리
  • mount path별로 습관적으로 PVC 추가
  • 이유 없이 "앱 데이터/설정/로그"를 모두 개별 PVC로 분리

4. 기본 원칙은 "적게, 명확하게"

기본적으로는 하나의 워크로드 / 하나의 상태 저장 목적 / 하나의 PVC를 먼저 검토한다. 분리는 정당한 이유(#3)가 있을 때만 한다.

5. StorageClass는 항상 명시적으로 지정

PVC는 storageClassName을 항상 명시한다. 클러스터 default annotation에 의존하지 않는다.

기본:

  • 운영 표준 StorageClass 3~5개를 미리 정의 (예: fast-ssd-retain, standard-delete, archive-retain, rwx-shared)
  • 성능/복제/노드 종속성 차이가 있으면 workload별로 구분
  • 각 StorageClass는 provisioner, reclaimPolicy, volumeBindingMode, allowVolumeExpansion을 모두 선언

6. StorageClass volumeBindingMode 기본값은 WaitForFirstConsumer

운영 표준은 WaitForFirstConsumer다.

이유:

  • Pod가 스케줄되는 노드의 topology(zone, node-local disk, GPU affinity 등)에 맞춰 PV를 바인딩한다
  • Immediate는 PVC 생성 즉시 PV를 바인딩하므로, 이후 Pod가 해당 노드/zone에 스케줄되지 못하는 상황이 생긴다
  • K3s local-path provisioner는 노드 로컬이므로 반드시 WaitForFirstConsumer여야 한다

Immediate 허용 예외:

  • 네트워크 스토리지(Ceph, NFS, S3 CSI 등)이고 topology 제약이 없는 경우
  • 사전에 PV를 warm-up 해야 하는 특수 케이스

7. StorageClass reclaimPolicy는 데이터 등급에 맞춘다

동적 프로비저닝의 기본 reclaimPolicyDelete다. 이는 PVC 삭제 시 PV와 데이터가 사라진다는 뜻이다.

기본:

  • production stateful data (DB, object store backend, identity store 등) → Retain
  • dev/test, ephemeral cache, rebuild-safe data → Delete
  • Retain을 쓰면 PVC 삭제 후 남은 PV를 정리하는 책임이 운영자에게 생긴다. runbook에 정리 절차를 명시한다.

8. allowVolumeExpansion은 기본 true로 두되 축소는 불가

PVC 확장 요구는 자주 생긴다. StorageClass에서 allowVolumeExpansion: true를 기본으로 둔다.

주의:

  • PVC 용량 축소는 K8s가 지원하지 않는다
  • 파일시스템 online expansion 지원 여부는 CSI 드라이버마다 다르다
  • 확장 후 Pod 재시작이 필요한 드라이버가 있다

9. AccessMode는 실제 요구에 맞게 고른다

기본:

  • 단일 writer면 ReadWriteOnce (RWO)
  • 동일 노드의 여러 Pod가 공유 필요시 ReadWriteOncePod (K8s 1.27+) 또는 RWO
  • 여러 Pod/노드 동시 read/write가 진짜 필요할 때만 ReadWriteMany (RWX)
  • 읽기 전용 공유는 ReadOnlyMany (ROX)

편의상 RWX를 기본값으로 두지 않는다. RWX는 NFS/CephFS 같은 별도 스토리지 백엔드를 요구한다.

10. K3s local-path provisioner는 운영에서 기본값 아님

K3s 기본 local-path provisioner의 하드 제약:

  • RWO 전용 (RWX 불가)
  • VolumeSnapshot 미지원
  • VolumeExpansion 미지원
  • 노드 로컬이므로 Pod가 특정 노드에 pin 됨 → 노드 장애 시 데이터 접근 불가
  • backup은 노드 파일시스템에 직접 접근해야 함

기본:

  • dev/test: 허용
  • production: Longhorn, OpenEBS, Rook-Ceph, 또는 클라우드 CSI driver(EBS, PD, Azure Disk 등)로 교체
  • 불가피하게 prod에서 local-path를 쓸 경우 backup-restore.md와 반드시 연동하고 노드 affinity/zone 분리를 명시

11. hostPath 직접 사용 금지

운영 PV/PVC에 hostPath를 사용하지 않는다.

예외:

  • 학습/단일 노드 로컬 테스트
  • 매우 제한된 디버깅 용도 (CSI driver 진단 등)

운영 표준으로 채택하지 않는다.

12. VolumeSnapshotClass를 StorageClass와 1:1로 매칭

snapshot 대상 PVC가 있는 StorageClass는 대응되는 VolumeSnapshotClass를 반드시 정의한다.

기본:

  • driver는 StorageClass의 provisioner와 맞춤
  • deletionPolicy는 운영 데이터면 Retain, ephemeral이면 Delete
  • snapshot class는 labels로 RPO/retention 정책과 연결

13. PVC lifecycle은 workload 생성 전에 문서화

PVC를 만들기 전에 아래를 정한다.

  • 누가 생성하는가 (Helm, Kustomize, Operator, manual)
  • 누가 삭제하는가 (GitOps sync, 운영자 수동)
  • scale down 시 어떻게 되는가
  • workload 삭제 시 어떻게 되는가
  • backup 대상인가 (어떤 RPO/RTO)
  • restore 단위인가 (PVC / VolumeSnapshot / backup tool 별)

"삭제하면 같이 정리되겠지"를 금지한다.

14. StatefulSet의 PVC retention policy를 명시적으로 검토

StatefulSet을 쓰는 경우 persistentVolumeClaimRetentionPolicy.whenDeleted / whenScaled를 기본값에 두지 않는다.

기본:

  • 운영 데이터: 둘 다 Retain
  • ephemeral 데이터: 둘 다 Delete
  • 혼용시 명시적 이유를 주석에 남김

15. Pod와 PVC는 같은 namespace 소유권

PVC는 Pod와 같은 namespace에서 사용된다. 스토리지도 workload의 namespace 소유권을 따라간다.

금지:

  • "공용 저장소 namespace"에 무분별하게 PVC 몰아넣기
  • 여러 서비스가 의미 없이 같은 PVC를 기대하는 구조

16. 워크로드별 기본 선택

Workload 기본 PVC StorageClass AccessMode Snapshot
auth-server 없음 - - -
test-server 없음 - - -
ingress-controller 없음 - - -
migration-flyway (Job) 없음 - - -
Keycloak (external DB) 없음 - - -
PostgreSQL / CNPG 필수 fast-ssd-retain RWO 필수
Vault (raft) 필수 fast-ssd-retain RWO 필수
MinIO 필수 standard-retain RWO 보조 (replication 우선)

17. 로그와 임시 파일은 PVC 기본 금지

다음은 기본적으로 PVC에 저장하지 않는다.

  • application log (→ stdout + 로그 수집기)
  • temp file (→ emptyDir)
  • cache (→ emptyDir 또는 memory-backed)
  • rendered config copy
  • transient upload staging

정말 영속화가 필요하면 이유를 주석에 명시한다.

18. backup/restore와 반드시 연결

PVC를 허용한 워크로드는 반드시 아래와 연결한다.

  • backup-restore.md (Velero schedule, snapshot class, RPO/RTO)
  • operations-runbook-upgrade-rollback.md (복구 절차)

PVC가 생기면 복구 전략도 같이 생겨야 한다. 백업 없는 PVC는 merge 금지.

19. 파일시스템 / 블록 모드 명시

volumeMode는 기본 Filesystem이지만, DB raw block 같은 경우 Block을 쓸 수 있다. DB 운영이 요구하지 않으면 Filesystem 고정.

20. securityContext와 fsGroup

PVC를 쓰는 Pod는 securityContext.fsGroup 또는 fsGroupChangePolicy: OnRootMismatch를 명시해서 permission 문제를 예방한다. restricted PSA 하에서는 runAsNonRoot: true, runAsUser, fsGroup을 모두 설정한다.

프로젝트 기준 요약

  • PVC는 상태가 있을 때만, separate PVC는 수명/복구 단위가 다를 때만
  • StorageClass는 항상 명시, volumeBindingMode: WaitForFirstConsumer 기본, reclaimPolicy는 데이터 등급에 맞춤
  • 동적 프로비저닝 기본 reclaimPolicy=Delete를 인지하고 운영 데이터는 Retain 명시
  • K3s local-path는 RWO / no snapshot / no expansion — prod 기본값 아님
  • VolumeSnapshotClass를 StorageClass와 매칭해서 정의
  • StatefulSet PVC retention policy 명시
  • 로그/임시 파일은 PVC 기본 금지
  • PVC가 생기면 backup/restore 기준도 같이 만든다