[트러블슈팅] Kubernetes StatefulSet 스토리지 확장: Non-cascade 전략으로 무중단 Immutable 제약 극복

Tech Story/DevOps & Container

[트러블슈팅] Kubernetes StatefulSet 스토리지 확장: Non-cascade 전략으로 무중단 Immutable 제약 극복

 

 
[ kt cloud Foundation플랫폼팀 이초환 님 ]

📋 요약

이 글에서는 Kubernetes StatefulSet 환경에서 volumeClaimTemplates의 Immutable 제약을 우회하여

서비스 중단 없이 PVC 스토리지를 확장하는 Non-cascade 전략과 단계별 절차를 다룹니다.

운영 중인 Pod의 가용성을 유지하면서 컨트롤러와 리소스의 생명주기를 분리하는 접근이

실제 인프라 확장 작업에서 얼마나 안전하고 효과적인지를 정리합니다.

#Kubernetes #StatefulSet #PVC #Non-cascade #OnlineVolumeExpansion


1. StatefulSet 환경에서 스토리지 확장이 어려운 이유

[트러블슈팅] Kubernetes StatefulSet 스토리지 확장: Non-cascade 전략으로 무중단 Immutable 제약 극복

일반적인 Stateless 애플리케이션(Deployment)은 디스크 부족 시 Pod를 교체하거나 외부 스토리지를 조정하는 것으로 비교적 간단히 해결됩니다. 하지만 StatefulSet(STS)은 다음과 같은 고유한 제약 조건을 가집니다.

1.1 volumeClaimTemplates의 Immutable 제약

StatefulSet의 volumeClaimTemplates는 생성 이후 수정이 불가능한 필드입니다.

즉, 단순히 Manifest의 용량 값을 수정해서는 반영되지 않으며,

API 서버 레벨에서 업데이트가 차단됩니다.

1.2 데이터 영속성 요구

데이터가 생명인 DB, 메시지 큐 등은 디스크를 버리고 새로 생성할 수 없으며,

기존 데이터를 유지한 채 '확장'만 수행해야 합니다.

1.3 운영 환경의 가용성 요구

운영 중인 인증·보안 시스템의 중단은 서비스 전반에 영향을 미칠 수 있기 때문에,

전체 재배포 방식은 현실적인 선택지가 되기 어렵습니다.


2. 핵심 전략: Online Expansion + Non-cascade 재배포

이번 확장 작업에서는 다음 두 가지 메커니즘을 조합했습니다.

2.1 Online Volume Expansion

PVC를 확장하면, CSI 드라이버를 통해 스토리지 볼륨이 동적으로 증가하고,

Kubelet이 파일시스템 리사이징을 수행합니다.

이를 통해 대부분의 경우 Pod 재시작 없이 용량이 반영됩니다.

2.2 cascade=orphan 기반 컨트롤러 교체

StatefulSet을 --cascade=orphan 옵션으로 삭제하면,

Pod를 유지한 채 컨트롤러만 제거할 수 있습니다.

이 방식을 활용하면 서비스 중단 없이 Immutable 필드가 포함된 StatefulSet을 재생성할 수 있습니다.


3. 상세 확장 절차 (Step-by-Step)

[트러블슈팅] Kubernetes StatefulSet 스토리지 확장: Non-cascade 전략으로 무중단 Immutable 제약 극복

Step 1: PVC 용량 업데이트

먼저 PVC를 확장합니다. 이 단계가 선행되지 않고 STS만 수정하려고 하면 API 서버 수준에서 거부됩니다.

# 특정 PVC의 용량을 100Gi로 확장
kubectl patch pvc <pvc-name> -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
  • 확인 사항: kubectl get pvc -w 명령어로 FileSystemResizePending 상태가 사라지고 용량이 반영되는지 확인합니다.

Step 2: StatefulSet 삭제 (Non-cascade 전략)

Immutable 속성이 설정된 STS 컨트롤러를 잠시 제거합니다.

  • ArgoCD: 삭제 팝업에서 Non-cascade를 반드시 체크합니다.

[트러블슈팅] Kubernetes StatefulSet 스토리지 확장: Non-cascade 전략으로 무중단 Immutable 제약 극복

  • CLI: kubectl delete sts <name> --cascade=orphan

이때 Pod들은 컨트롤러와의 연결만 끊길 뿐, 트래픽은 계속 처리하고 있습니다. 이 지점에서 서비스 가동 시간이 유지됩니다.

Step 3: Manifest 동기화 및 컨트롤러 재배포

수정된 용량을 반영한 새로운 STS Manifest를 배포합니다.

중요: 새로 배포되는 STS의 volumeClaimTemplates 용량은 Step 1에서 수정한 실제 PVC의 용량과 정확히 일치해야 합니다.

 

새로운 STS 컨트롤러가 생성되면, 현재 실행 중인 고아 Pod들의 레이블을 스캔하여 자신의 관리 하로 다시 편입(Adoption)시킵니다. 이 과정을 통해 STS의 Spec과 실제 인프라 상태가 일치하게 됩니다.

Step 4: 용량 반영 확인 및 예외 조치 (중요)

대부분의 경우 재시작 없이 용량이 반영됩니다. Pod 내부에서 용량을 확인합니다.

kubectl exec -it <pod-name> -- df -h
  • 반영 완료: 늘어난 용량이 확인되면 작업을 종료합니다.
  • 반영 미완료: 간혹 Online Expansion이 지연되거나 OS 레벨에서 인식하지 못할 경우, Pod를 순차적으로 재시작하여 다시 마운트되도록 유도합니다.
    • updateStrategy: RollingUpdate인 경우: kubectl rollout restart sts <name>
    • updateStrategy: OnDelete인 경우: kubectl delete pod <pod-name> (수동 순차 삭제)

4. 유의할 점

updateStrategyOnDelete이면, rollout restart가 먹히지않습니다. OnDelete 전략에서는 자동으로 재시작되지 않습니다. 용량 반영이 안 되어 재시작이 꼭 필요한 상황이라면 엔지니어가 직접 Pod를 하나씩 삭제하여 수동 롤링 업데이트를 수행해야 합니다.

작업 순서가 중요합니다. PVC를 먼저 확장하지 않고 STS Manifest만 수정하면, 쿠버네티스 API 서버가 "이미 존재하는 PVC와 새로 만들려는 STS 템플릿의 용량이 다르다"며 배포를 차단합니다. 따라서 '물리적 확장(PVC) -> 명세 업데이트(STS)' 순서를 반드시 지켜야 합니다.


5. 마치며

이번 사례의 핵심은 "컨트롤러와 리소스의 생명주기를 분리하여 생각하는 것"에 있습니다. 무조건적인 재배포보다 인프라의 현재 상태를 먼저 반영(PVC Patch)하고, 그에 맞춰 컨트롤러의 명세를 나중에 맞추는 방식이 훨씬 안전합니다.

 

kt cloud 플랫폼 바로가기

❓ 자주 묻는 질문 (FAQ)

Q1. STS(StatefulSet)을 지우면 Pod가 당연히 날아가는 것 아닌가요?
A1. 아닙니다. --cascade=orphan 또는 ArgoCD의 Non-cascade 옵션을 사용하면 ㅏKubernetes 가비지 컬렉터가 하위 리소스를 지우지 않도록 설정할 수 있습니다. 덕분에 트래픽 중단 없이 컨트롤러만 교체하는 작업이 가능해집니다.
Q2. 수동으로 Pod를 재시작하지 않아도 용량이 늘어나 있는 이유는 무엇인가요?
A2. 최신 Kubernetes 버전과 CSI 드라이버가 지원하는 Online Expansion덕분입니다. 마운트된 상태에서 커널이 볼륨 크기 변화를 감지하고 파일시스템을 즉시 확장하기 때문입니다. 
Q3. 작업 중 데이터 유실 위험은 없나요?
A3. 가이드의 방식대로 STS를 삭제할경우 STS를 지워도 PVC, PV는 유지되므로 데이터 자체는 안전합니다. 하지만 스토리지 작업의 특성상 만약의 상황을 대비해 백업본을 먼저 확보하는 것을 권장합니다. 

📚 관련/출처