[IT-정보] 스테이트풀셋 트러블슈팅 가이드 – 장애 원인 분석부터 복구까지

스테이트풀셋 관련 쿠버네티스 구조를 설명하는 대표 이미지

갑작스러운 스테이트풀셋 장애, 당황하지 않고 대처하는 법

새벽 2시, 조용한 사무실에 울리는 슬랙 알림 소리에 눈을 떴어요. 데이터베이스를 관리하는 스테이트풀셋(StatefulSet)의 파드가 CrashLoopBackOff 상태로 변해있네요. 혼자서 서버를 운영하는 담당자라면 이 순간 심장이 덜컥 내려앉는 기분을 잘 알 거예요. 일반적인 디플로이먼트(Deployment)라면 파드를 새로 띄우면 그만이지만, 데이터와 연결된 스테이트풀셋은 상황이 전혀 달라요.

스토리지 연결이 끊겼을 수도 있고, 순서대로 뜨지 않는 파드 때문에 서비스 전체가 마비되었을 수도 있죠. 잘못된 조치는 소중한 데이터를 영구적으로 손실시킬 위험까지 있어요. 단순히 명령어를 입력하는 것을 넘어, 왜 이런 일이 발생했는지 근본적인 원인을 파악하는 것이 무엇보다 중요해요.

이 글은 혼자서 쿠버네티스를 운영하며 장애를 마주했을 때, 당황하지 않고 차근차근 문제를 해결할 수 있도록 돕기 위해 작성했어요. 복잡한 이론보다는 실무에서 바로 써먹을 수 있는 진단 순서와 복구 절차에 집중했어요. 이 글을 끝까지 읽고 나면, 장애 상황에서도 침착하게 로그를 읽고 원인을 좁혀나가는 자신감을 얻게 될 거예요.

이 글에서 함께 살펴볼 내용이에요:

  • 스테이트풀셋 장애의 대표적인 증상과 유형
  • 장애 진단을 위한 필수 사전 지식과 체크리스트
  • 단계별 원인 분석과 구체적인 복구 시나리오
  • 자주 발생하는 실수와 실무자 FAQ
  • 장애 재발을 막기 위한 안정적인 설정법

장애 진단 전 반드시 갖춰야 할 준비물과 핵심 개념

무작정 명령어를 입력하기 전에, 지금 내가 마주한 문제가 스테이트풀셋의 어떤 특성 때문에 발생했는지 이해해야 해요. 스테이트풀셋은 일반적인 애플리케이션과 달리 고유한 식별자(Ordinal Index)안정적인 스토리지를 유지해야 한다는 특징이 있어요. 이 연결 고리 중 하나라도 끊어지면 장애로 이어지기 때문이에요.

문제를 해결하기 위해 가장 먼저 준비해야 할 것은 클러스터에 접근할 수 있는 kubectl 권한과 로그를 확인할 수 있는 환경이에요. 또한, 현재 사용 중인 스토리지 클래스(StorageClass)의 특성과 파드가 어떤 네트워크 정책(Network Policy)의 영향을 받는지 미리 파악해 두면 진단 속도가 훨씬 빨라져요.

디플로이먼트와 스테이트풀셋 비교

내가 겪고 있는 문제가 스테이트풀셋 특유의 문제인지 확인하려면 아래 표를 참고해 보세요.

구분 항목 디플로이먼트(Deployment) 스테이트풀셋(StatefulSet)
파드 이름 무작위 문자열 생성 고정된 인덱스(예: web-0)
스토리지 연결 모든 파드가 공유 가능 파드마다 전용 볼륨 연결
배포 순서 동시에 무작위 배포 번호 순서대로 순차 배포
주요 장애 유형 리소스 부족, 설정 오류 볼륨 마운트 실패, 네트워크 식별 불가
💡 알아두기
스테이트풀셋에서 가장 흔한 장애 원인은 스토리지(PVC)와 파드의 연결이 끊어지거나, 이전 파드가 제대로 종료되지 않아 볼륨을 점유하고 있는 경우예요. 이 점을 항상 염두에 두고 진단을 시작하세요.

장애를 마주했을 때 가장 먼저 확인해야 할 체크리스트를 정리해 드릴게요. 이 항목들이 준비되어 있지 않다면 진단 과정에서 길을 잃기 쉬워요.

  • kubectl describe pod 명령어를 통해 이벤트 로그를 볼 준비가 되었는가?
  • 현재 클러스터의 노드(Node) 상태가 Ready 상태인가?
  • 사용 중인 PVC(PersistentVolumeClaim)가 정상적으로 Bound 상태인가?
  • 스테이트풀셋에 연결된 헤드리스 서비스(Headless Service)가 존재하는가?

단계별 원인 분석과 실무 복구 프로세스

이제 본격적으로 문제를 해결해 볼 시간이에요. 스테이트풀셋 트러블슈팅은 막연한 추측이 아니라, 데이터가 흐르는 경로를 따라가며 범인을 찾는 과정과 같아요. 아래의 5단계 절차를 따라가면 복잡한 문제도 논리적으로 해결할 수 있어요.

STEP 1. 파드의 상태와 이벤트 로그 분석하기

가장 먼저 해야 할 일은 파드가 어떤 상태에 빠졌는지 확인하는 거예요. kubectl get pods -l app=<앱이름> 명령어로 파드들의 목록을 확인하세요. 여기서 Pending, CrashLoopBackOff, Terminating 중 어떤 상태인지 파악하는 게 첫 단추예요.

상태를 확인했다면 바로 kubectl describe pod <파드이름> 명령어를 실행하세요. 화면 하단의 Events 섹션을 유심히 살펴봐야 해요. 만약 FailedMount라는 문구가 보인다면 이건 100% 스토리지 문제입니다. FailedScheduling이 보인다면 노드에 리소스가 부족하거나 스케줄링 제약 조건이 걸려 있는 것이죠. 이벤트 로그는 파드가 겪고 있는 고통을 직접적으로 보여주는 가장 정직한 지표예요.

STEP 2. 스토리지 및 PVC 계층 정밀 진단

스테이트풀셋 장애의 절반 이상은 스토리지에서 발생해요. 파드가 뜨지 않는다면 PVC(PersistentVolumeClaim) 상태를 반드시 확인해야 합니다. kubectl get pvc를 입력했을 때 상태가 Pending으로 떠 있다면, 스토리지 클래스가 제대로 작동하지 않거나 볼륨을 생성할 권한이 없는 상태예요.

만약 PVC는 Bound 상태인데 파드가 마운트에 실패한다면, Multi-Attach Error를 의심해 보세요. 이는 이전 파드가 노드에서 완전히 사라지지 않아, 새로운 파드가 같은 볼륨을 가져가지 못할 때 발생해요. 특히 AWS EBS나 Azure Disk 같은 클라우드 볼륨을 사용할 때 자주 나타나는 현상이죠. 이럴 때는 해당 볼륨을 점유하고 있는 이전 노드를 확인하거나, 최악의 경우 볼륨을 강제로 해제하는 작업이 필요할 수도 있어요.

⚠️ 주의
문제를 해결하겠다고 kubectl delete pvc를 함부로 실행하지 마세요! 스테이트풀셋의 PVC는 파드와 생명주기가 분리되어 있어, 삭제 시 실제 데이터가 담긴 PV(PersistentVolume)까지 함께 날아갈 수 있어요. 데이터 백업 여부를 먼저 확인하세요.

STEP 3. 네트워크 및 DNS 식별성 검증

스테이트풀셋은 각 파드가 고유한 네트워크 주소를 가져야 해요. 이를 위해 헤드리스 서비스(Headless Service)가 필수적이죠. 만약 다른 파드가 이 스테이트풀셋의 파드에 접근하지 못한다면, 서비스 설정이 잘못되었을 확률이 높아요.

먼저 헤드리스 서비스의 클러스터 IP가 10.96.x.x처럼 특정 IP를 갖지 않고, 파드들의 개별 IP로 직접 연결되는지 확인하세요. 또한, nslookup <파드이름>.<서비스이름>.<네임스페이스>.svc.cluster.local 명령어를 통해 DNS 해석이 정상적으로 되는지 테스트해 봐야 해요. 만약 DNS 해석이 실패한다면 CoreDNS 설정 문제이거나, 서비스의 selector가 파드의 라벨과 일치하지 않는 문제일 수 있어요.

STEP 4. 애플리케이션 로그를 통한 런타임 오류 추적

인프라(스토리지, 네트워크)에 문제가 없다면, 이제 애플리케이션 자체의 문제로 시선을 돌려야 해요. 파드가 CrashLoopBackOff 상태라면 컨테이너 내부에서 프로세스가 실행되자마자 죽고 있다는 뜻이에요.

이때는 kubectl logs <파드이름> --previous 명령어가 구세주가 될 거예요. –previous 옵션은 현재 실행 중인 파드가 아니라, 바로 직전에 죽었던 파드의 로그를 보여줍니다. 로그를 통해 데이터베이스의 권한 오류(Permission Denied), 설정 파일(ConfigMap) 누락, 혹은 메모리 부족(OOMKilled) 여부를 확인할 수 있어요. 특히 데이터베이스 엔진의 로그를 통해 테이블 손상이나 로그 파일 불일치 같은 내부 오류를 찾아내는 것이 핵심이에요.

STEP 5. 스케줄링 및 리소스 제약 조건 확인

마지막으로 파드가 아예 생성조차 되지 않는다면 스케줄러의 눈치를 살펴봐야 해요. 노드에 Taints(테인트)가 걸려 있어서 파드가 들어갈 자리가 없거나, 파드가 요구하는 CPU/메모리 요청량(Requests)이 노드의 가용 자원보다 클 수 있어요.

또한, 스테이트풀셋의 podAntiAffinity 설정을 확인해 보세요. 만약 모든 파드를 서로 다른 노드에 배치하도록 설정했는데, 가용 노드가 하나뿐이라면 파드들은 계속해서 Pending 상태로 머물게 됩니다. 리소스 제한(Limits)이 너무 타이트하게 설정되어 있어서 애플리케이션이 구동되는 순간 메모리 부족으로 종료되는 경우도 흔하니, kubectl top pod 명령어로 실제 사용량을 모니터링하며 적절한 값을 찾아야 해요.

💡 알아두기
실무에서는 장애 복구 시나리오를 미리 작성해 두는 것이 좋습니다. 예를 들어, ‘스토리지 마운트 실패 시 대응 매뉴얼’을 만들어 두면 긴급 상황에서 실수할 확률을 획기적으로 줄일 수 있어요.

자주 하는 실수와 해결법 및 자주 묻는 질문

장애 대응 과정에서는 당황한 나머지 의도치 않은 실수를 범하기 쉬워요. 아래의 사례들을 보며 여러분이 혹시 같은 실수를 반복하고 있지는 않은지 체크해 보세요.

자주 하는 실수와 해결법

실수: 파드가 안 뜬다고 해서 바로 PVC를 삭제함
왜 발생하는가: 파드를 초기화하면 문제가 해결될 것이라는 착각 때문이에요.
해결법: PVC는 데이터와 직접 연결되어 있으니, 절대 먼저 삭제하지 마세요. 대신 파드를 삭제하거나, 볼륨 마운트 오류가 있다면 노드의 마운트 상태를 먼저 확인하세요.

실수: kubectl delete pod만 반복하며 기다림
왜 발생하는가: 파드를 재시작하면 설정이 반영될 것이라 믿기 때문이에요.
해결법: 설정(ConfigMap, Secret)이나 스토리지에 문제가 있다면 파드를 아무리 지워도 똑같은 상태가 반복됩니다. 반드시 describe로 원인을 먼저 찾으세요.

실수: 헤드리스 서비스의 라벨 셀렉터를 대충 설정함
왜 발생하는가: 서비스가 단순히 연결만 되면 된다고 생각하기 때문이에요.
해결법: 스테이트풀셋은 고유한 네트워크 ID가 생명이에요. 서비스의 selector가 스테이트풀셋의 template.metadata.labels와 정확히 일치하는지 확인하세요.

실수: 리소스 Limit을 너무 높게 설정함
왜 발생하는가: 장애를 방지하려고 무조건 크게 잡는 것이 안전하다고 믿기 때문이에요.
해결법: Limit이 너무 크면 노드의 전체 리소스를 고갈시켜 다른 파드까지 죽게 만들어요. 실제 사용량에 근거한 적절한 Limit 설정이 필수예요.

실수: Terminating 상태인 파드를 무시함
왜 발생하는가: 이미 삭제 명령을 내렸으니 곧 사라질 것이라 생각하기 때문이에요.
해결법: 파드가 Terminating에 멈춰 있다면 볼륨 해제에 실패했거나 노드 통신에 문제가 생긴 거예요. 이 파드가 살아있는 한 새 파드는 볼륨을 가져갈 수 없습니다.

자주 묻는 질문

Q. 스테이트풀셋 파드가 계속 Pending 상태예요. 어떻게 하나요?

파드가 배치될 수 있는 적절한 노드를 찾지 못했다는 뜻이에요. kubectl describe pod을 통해 이벤트 로그를 보세요. 리소스 부족(Insufficient cpu/memory)인지, 아니면 노드에 설정된 테인트(Taint) 때문에 못 들어가는 것인지 확인해야 해요.

Q. PVC 용량을 늘리고 싶은데 어떻게 해야 하나요?

사용 중인 스토리지 클래스가 allowVolumeExpansion: true 설정을 지원해야 해요. 지원한다면 PVC의 spec.resources.requests.storage 값을 수정한 뒤, 파드를 재시작하면 대부분의 클라우드 환경에서 자동으로 확장됩니다.

Q. 파드 번호 순서가 뒤죽박죽으로 뜨는데 정상인가요?
아니요, 스테이트풀셋은 반드시 0번부터 순차적으로 생성되어야 합니다. 만약 0번이 생성되지 않았는데 1번이 뜨려고 한다면, 0번 파드의 상태를 먼저 점검하세요. 0번이 정상적으로 Running이 되어야 다음 번호로 넘어갑니다.

Q. 데이터 손실 없이 스테이트풀셋을 업데이트할 수 있나요?
네, 스테이트풀셋은 RollingUpdate 전략을 기본으로 사용해요. 하지만 업데이트 전에 반드시 스냅샷을 찍거나 데이터 백업을 완료하는 습관을 들이는 것이 가장 안전해요.

Q. 노드가 죽었는데 파드가 다른 노드로 안 옮겨가요.

스테이트풀셋은 데이터 안정성을 위해 파드가 이전 노드에서 완전히 종료(Unmount)되었는지 확인하려 합니다. 노드가 완전히 사라졌다면, 파드에 node.kubernetes.io/out-of-service 같은 taint를 설정하여 강제로 이동시키는 고급 기술이 필요할 수 있어요.

장애 없는 운영을 위한 마지막 체크리스트

오늘 배운 내용을 잊지 않도록, 장애가 발생했을 때 바로 꺼내 볼 수 있는 핵심 요약을 정리해 드릴게요. 이 순서만 지켜도 최악의 상황은 면할 수 있어요.

✅ 핵심 요약

  • 1단계: kubectl get pods로 현재 파드의 상태 유형(Pending, CrashLoop 등) 파악
  • 2단계: kubectl describe pod으로 이벤트 로그에서 실패 원인(Mount, Scheduling 등) 추출
  • 3단계: 스토리지 문제라면 PVC의 Bound 상태와 볼륨 점유 여부 확인
  • 4단계: 애플리케이션 오류라면 --previous 옵션으로 이전 로그 분석
  • 5단계: 네트워크 문제라면 헤드리스 서비스의 라벨과 DNS 해석 결과 검증
  • 6단계: 복구 후에는 반드시 리소스 사용량과 로그를 재검증하여 안정성 확인

이제 무엇을 해야 할까요? 지금 당장 운영 중인 클러스터에 문제가 없다면, 다음 단계를 준비해 보세요.

  • 오늘 할 일: 현재 사용 중인 스테이트풀셋의 PVC와 서비스 라벨이 제대로 설정되어 있는지 다시 한번 검토하기
  • 이번 주 할 일: 개발 환경에서 파드를 강제로 종료시키거나 스토리지 연결을 끊어보는 장애 시뮬레이션 수행하기
  • 실행 직전 할 일: 장애 발생 시 즉시 실행할 수 있는 명령어들을 메모장에 정리해 두기

스테이트풀셋 운영은 까다롭지만, 그만큼 데이터의 가치를 지키는 아주 중요한 작업이에요. 이 글이 여러분의 밤잠을 조금이라도 편안하게 만들어 주었기를 바랍니다. 실습 환경에서 직접 적용해 보다가 도저히 풀리지 않는 문제가 생기면, 고민하지 말고 댓글로 질문을 남겨 주세요. 함께 고민해 드릴게요!

함께 읽으면 좋은 글:
쿠버네티스 스테이트풀셋 기본 개념 완벽 정리
클러스터 구축부터 운영 입문까지: 데브옵스 로드맵

댓글 남기기