
데이터가 사라지는 공포, 스테이트풀셋이 필요한 이유
분명히 데이터베이스 파드를 잘 띄워두고 데이터를 몇 건 저장했는데, 재시작 한 번에 모든 정보가 증발해버린 경험이 있으신가요? 쿠버네티스의 기본 단위인 디플로이먼트(Deployment)를 사용해 데이터베이스를 운영하다 보면 흔히 마주치는 문제입니다. 디플로이먼트는 파드가 죽고 다시 살아날 때마다 새로운 이름과 새로운 환경을 부여하기 때문이에요.
상태가 없는(Stateless) 웹 서버라면 이런 방식이 아무런 문제가 되지 않아요. 하지만 데이터베이스, 메시지 큐, 분산 저장소처럼 데이터의 영속성과 파드의 고유한 식별이 생명인 서비스는 이야기가 완전히 달라집니다. 파드가 다시 살아나도 이전과 똑같은 이름(예: mysql-0)을 유지해야 하고, 이전에 쓰던 저장 장치(Volume)를 그대로 다시 연결할 수 있어야 해요.
이 문제를 해결하기 위해 탄생한 것이 바로 스테이트풀셋(StatefulSet)이에요. 스테이트풀셋은 파드에 순차적인 번호를 매기고, 각각의 파드가 자신만의 전용 저장소를 가질 수 있도록 설계되어 있어요. 이 글을 끝까지 읽으시면 단순히 명령어를 입력하는 수준을 넘어, 왜 이렇게 설정해야 하는지 원리부터 실무적인 검증 방법까지 완벽히 이해하게 될 거예요.
이번 가이드에서 함께 다룰 내용은 다음과 같아요.
- 스테이트풀셋을 구성하기 위한 사전 준비물과 핵심 개념
- 실제 YAML 파일을 활용한 단계별 설치 절차
- 설정값(Spec)의 세부 옵션과 데이터 영속성 원리
- 설치 후 데이터가 정말 잘 유지되는지 확인하는 검증법
- 실무에서 자주 발생하는 오류와 해결책
스테이트풀셋 구축 전 반드시 챙겨야 할 체크리스트
스테이트풀셋을 설치하기 전에 무턱대고 YAML 파일을 작성하면 십중팔구 파드가 Pending 상태에서 멈춰버려요. 스테이트풀셋은 디플로이먼트보다 훨씬 까다로운 조건을 요구하기 때문이에요. 가장 먼저 확인해야 할 것은 클러스터의 저장소 환경이에요.
필수 준비물 및 전제 조건
먼저 쿠버네티스 클러스터가 정상적으로 동작하고 있어야 하며, StorageClass가 설정되어 있어야 해요. 스테이트풀셋의 핵심인 VolumeClaimTemplate이 동작하려면, 요청이 들어왔을 때 자동으로 볼륨을 생성해줄 수 있는 동적 프로비저닝(Dynamic Provisioning) 기능이 필요하거든요. 만약 수동으로 볼륨을 관리해야 하는 환경이라면 작업 난이도가 몇 배로 올라가니 주의하세요.
스테이트풀셋을 사용할 때는 파드의 네트워크 식별을 위해 ‘헤드리스 서비스(Headless Service)’가 반드시 필요해요. 일반적인 서비스와 달리 클러스터 내부에서 각 파드에 직접 접근할 수 있는 DNS 이름을 만들어주는 역할을 합니다.
디플로이먼트 vs 스테이트풀셋 비교
내가 지금 만들려는 서비스가 스테이트풀셋에 적합한지 판단하기 어렵다면 아래 표를 참고해 보세요. 어떤 특성을 선택해야 할지 명확해질 거예요.
| 구분 항목 | 디플로이먼트(Deployment) | 스테이트풀셋(StatefulSet) |
|---|---|---|
| 주요 목적 | 상태가 없는 애플리케이션 운영 | 데이터 영속성이 필요한 서비스 |
| 파드 식별자 | 랜덤한 해시값 (예: web-abc12) | 순차적 번호 (예: mysql-0) |
| 저장소 할당 | 모든 파드가 동일 볼륨 공유 가능 | 파드마다 독립된 전용 볼륨 할당 |
| 네트워크 식별 | 서비스 IP를 통한 접근 | 고유한 DNS 이름 제공 (Headless) |
만약 여러분의 서비스가 단순한 API 서버라면 디플로이먼트로 충분해요. 하지만 데이터베이스처럼 파드가 재시작되어도 이름이 유지되어야 하거나, 각 파드가 각자의 데이터를 들고 있어야 한다면 망설이지 말고 스테이트풀셋을 선택해야 합니다.
스테이트풀셋 설치 및 초기 구성 단계별 가이드
이제 본격적으로 스테이트풀셋을 구축해 볼게요. 단순히 파일을 실행하는 것을 넘어, 각 설정이 어떤 마법을 부리는지 하나씩 뜯어보겠습니다. 실습을 위해 간단한 Nginx 기반의 저장소 테스트 환경을 만든다고 가정해 볼게요.
STEP 1. 헤드리스 서비스(Headless Service) 생성하기
스테이트풀셋의 첫 단추는 헤드리스 서비스를 만드는 거예요. 일반적인 서비스는 여러 파드를 하나의 IP로 묶어주지만, 헤드리스 서비스는 각 파드의 IP를 개별적으로 노출시켜 줍니다. 이를 통해 nginx-0.nginx-service 같은 고유한 도메인 주소를 가질 수 있게 돼요.
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
여기서 가장 중요한 부분은 clusterIP: None이에요. 이 한 줄이 서비스를 ‘헤드리스’로 만듭니다. 이 설정이 없으면 스테이트풀셋의 가장 큰 장점인 개별 파드 접근 기능을 제대로 쓸 수 없어요.
STEP 2. 스테이트풀셋 YAML 작성 및 옵션 이해하기
이제 핵심인 스테이트풀셋 파일을 작성할 차례예요. 스테이트풀셋 설정 방법의 핵심은 volumeClaimTemplates에 있습니다. 이 부분은 디플로이먼트에는 없는 아주 특별한 기능이에요.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-set
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx-service"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "standard"
resources:
requests:
storage: 1Gi
작성한 파일의 핵심 요소를 짚어볼까요?
serviceName: 위에서 만든 헤드리스 서비스의 이름과 반드시 일치해야 해요. 그래야 DNS가 연결됩니다.replicas: 생성할 파드의 개수예요. 스테이트풀셋은 0번부터 차례대로 생성됩니다.volumeClaimTemplates: 이게 진짜 주인공이에요. 파드가 생성될 때마다 이 템플릿을 바탕으로 새로운 PVC(PersistentVolumeClaim)를 자동으로 만들어줍니다. 즉, 파드 3개를 띄우면 3개의 독립된 볼륨이 생기는 거죠.
볼륨의
accessModes를 확인하세요. 대부분의 클라우드 환경(AWS EBS 등)에서는 ReadWriteOnce(RWO)만 지원합니다. 여러 노드에서 동시에 쓰려고 하면 볼륨 마운트 오류가 발생할 수 있어요.STEP 3. 리소스 배포 및 동작 원리 파악
작성이 완료되었다면 명령어를 통해 배포를 진행합니다. kubectl apply -f nginx-sts.yaml
명령어를 입력하면 쿠버네티스는 아주 정중하고 질서 있게 움직이기 시작해요. 먼저 0번 파드를 생성하고, 0번 파드의 볼륨이 정상적으로 준비될 때까지 기다립니다. 그 후 1번 파드를, 그다음 2번 파드를 순서대로 생성하죠. 이런 순차적 생성(Ordered Ready) 방식은 클러스터 내의 분산 시스템이 데이터 정합성을 유지하는 데 매우 중요합니다.
STEP 4. 설정 적용 후 동작 검증
설치가 끝났다고 해서 안심하기엔 이릅니다. 데이터가 정말로 각 파드에 잘 할당되었는지, 네트워크는 잘 통하는지 직접 확인해야 해요. 다음 세 가지 검증 과정을 반드시 거치세요.
- 파드와 PVC 매칭 확인:
kubectl get pvc를 입력했을 때,www-nginx-set-0,www-nginx-set-1등 파드 이름이 포함된 PVC가 각각 생성되었는지 확인하세요. - 데이터 영속성 테스트: 파드 하나에 접속해서 파일을 하나 만들어보세요.
kubectl exec nginx-set-0 -- sh
접속 후echo "hello" > /usr/share/nginx/html/test.txt를 실행합니다. 그 다음 파드를 강제로 삭제(kubectl delete pod nginx-set-0)하고, 파드가 다시 살아났을 때 해당 파일이 남아있는지 확인하세요. - 네트워크 식별 확인: 다른 파드 내부에서 스테이트풀셋 파드의 DNS 이름을 통해 통신이 가능한지 확인합니다.
nslookup nginx-set-0.nginx-service명령어가 성공해야 합니다.
이 검증 과정을 통과했다면, 여러분은 성공적으로 쿠버네티스 스테이트풀셋 환경설정을 마친 것입니다.
자주 하는 실수와 해결법 및 FAQ
스테이트풀셋은 설정이 복잡한 만큼 운영 중에 예기치 못한 문제들이 자주 발생하곤 해요. 실무자들이 가장 많이 겪는 사례들을 정리했습니다.
자주 하는 실수와 해결법
❌ 실수: 파드가 계속 Pending 상태에서 멈춰 있어요.
왜 발생할까요? 대부분 StorageClass 설정이 잘못되었거나, 요청한 용량이 클러스터의 가용 자원을 초과했기 때문이에요. 혹은 볼륨을 할당해줄 프로비저너(Provisioner)가 작동하지 않는 경우도 있습니다.
✅ 해결법: kubectl describe pod [파드이름]을 실행하여 하단의 Events 섹션을 보세요. ‘FailedScheduling’ 메시지와 함께 볼륨 문제라는 힌트가 적혀 있을 거예요.
❌ 실수: 파드를 삭제했는데 볼륨(PVC)이 그대로 남아 있어요.
왜 발생할까요? 스테이트풀셋의 설계 철학 때문입니다. 데이터 보호를 위해 스테이트풀셋을 삭제하더라도 그 파드가 사용하던 PVC는 자동으로 지워지지 않도록 되어 있어요. 의도치 않은 데이터 삭제를 막기 위한 안전장치죠.
✅ 해결법: 데이터가 더 이상 필요 없다면 kubectl delete pvc [PVC이름] 명령어로 수동으로 삭제해 주어야 합니다.
❌ 실수: 헤드리스 서비스 이름이 맞는데도 DNS 조회가 안 돼요.
왜 발생할까요? 스테이트풀셋의 serviceName 필드와 실제 생성된 서비스의 metadata.name이 일치하지 않는 경우가 많습니다.
✅ 해결법: YAML 파일의 두 이름을 정확히 대조해 보세요. 대소문자 하나라도 틀리면 DNS 레코드는 생성되지 않습니다.
❌ 실수: 파드를 스케일 아웃(Scale-out)했는데 데이터가 꼬여요.
왜 발생할까요? 스테이트풀셋의 파드들은 각각 독립적인 볼륨을 가져야 하는데, 실수로 volumeClaimTemplates가 아닌 일반 volumes 섹션에 볼륨을 정의했기 때문입니다.
✅ 해결법: 반드시 volumeClaimTemplates를 사용하여 파드마다 개별 PVC가 생성되도록 설정하세요.
❌ 실수: 업데이트 중에 파드가 죽지 않고 계속 이전 버전을 유지해요.
왜 발생할까요? updateStrategy 설정이 잘못되었거나, 파드가 이전 버전의 데이터와 호환되지 않아 종료되지 못하는 상태일 수 있습니다.
✅ 해결법: RollingUpdate 옵션을 확인하고, 파드가 정상적으로 종료(Terminating)될 수 있도록 종료 기간(TerminationGracePeriodSeconds)을 넉넉히 설정하세요.
자주 묻는 질문
Q. 디플로이먼트 대신 스테이트풀셋을 써야 하는 기준을 딱 한 줄로 요약한다면요?
데이터가 파드의 재시작과 상관없이 유지되어야 하고, 각 파드가 서로 다른 데이터를 가져야 한다면 무조건 스테이트풀셋을 써야 해요.
Q. 스테이트풀셋은 파드를 생성할 때 왜 순서대로 하나씩 만드나요?
클러스터 내의 데이터 정합성을 위해서예요. 예를 들어 데이터베이스 클러스터라면 0번 노드가 완전히 준비되어야 1번 노드가 클러스터에 합류하여 동기화를 시작할 수 있기 때문입니다.
Q. 헤드리스 서비스 없이 스테이트풀셋을 쓸 수는 없나요?
쓸 수는 있지만, 그렇게 하면 스테이트풀셋을 쓰는 의미가 거의 사라져요. 각 파드에 고유한 네트워크 주소를 부여하는 것이 스테이트풀셋의 가장 큰 강력함 중 하나거든요.
Q. 파드 개수를 늘리면 볼륨도 자동으로 늘어나나요?
네, 맞아요. volumeClaimTemplates를 사용했다면, 새로운 파드가 생길 때마다 정의된 스토리지 클래스에 따라 새 볼륨이 자동으로 생성됩니다.
Q. 운영 환경에서 스테이트풀셋을 쓸 때 가장 주의할 점은 무엇인가요?
볼륨의 백업과 복구 전략이에요. 쿠버네티스가 볼륨 관리를 도와주지만, 볼륨 자체가 손상되거나 잘못된 설정으로 삭제되는 상황에 대비해 외부 스토리지 스냅샷 등을 이용한 별도의 백업 체계를 갖춰야 합니다.
성공적인 스테이트풀셋 운영을 위한 마지막 정리
스테이트풀셋은 쿠버네티스에서 가장 강력하면서도 다루기 까다로운 오브젝트 중 하나예요. 하지만 그 원리를 이해하고 나면, 데이터베이스와 같은 핵심 서비스를 클라우드 네이티브 환경에서 안정적으로 운영할 수 있는 핵심 무기를 갖게 되는 셈입니다.
- 데이터 영속성이 필요한 서비스에는 반드시 스테이트풀셋을 사용하세요.
- 헤드리스 서비스를 통해 파드별 고유 DNS를 확보하는 것이 핵심입니다.
volumeClaimTemplates를 사용하여 파드마다 독립된 볼륨을 할당하세요.- 배포 후에는 반드시 파드-PVC 매칭과 데이터 유지 여부를 검증하세요.
- PVC는 스테이트풀셋 삭제 시 자동으로 지워지지 않으니 수동 관리가 필요합니다.
오늘 배운 내용을 바탕으로 지금 바로 실습 환경에서 테스트해 보세요. 이론으로 보는 것과 실제 YAML을 적용하며 겪는 시행착오는 완전히 다를 거예요. 실습 중에 막히는 부분이 있거나 예상치 못한 오류 메시지를 만난다면 주저하지 말고 댓글로 질문을 남겨 주세요. 함께 고민해 보겠습니다.
🚀 다음 단계로 나아가기
- 오늘 할 일: 스테이트풀셋 기본 YAML 파일 작성해 보기
- 이번 주 할 일: 실제 데이터베이스(MySQL 등)를 스테이트풀셋으로 배포해 보기
- 실행 직전 할 일: 클러스터의 StorageClass가 정상 작동하는지 확인하기
함께 읽으면 좋은 글:
– 쿠버네티스 스테이트풀셋 기본 개념 완벽 정리
– 쿠버네티스 클러스터 구축 및 초기 설정 입문 가이드