# 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`는 데이터 등급에 맞춘다 동적 프로비저닝의 기본 `reclaimPolicy`는 `Delete`다. 이는 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 기준도 같이 만든다