Pod 이름이 매번 바뀌는데, 데이터베이스는 어떻게 클러스터를 이루나
MySQL 3대로 Galera 클러스터를 세우려 한다. 각 노드는 "내가 노드 1번이고, 노드 2번은 저기, 노드 3번은 저기"라고 알아야 한다 — 서로를 안정적인 이름으로 부를 수 있어야 복제 토폴로지가 잡힌다. 그런데 04장에서 본 대로 일반 Pod는 죽을 때마다 이름이 바뀐다. "mysql-1"이 죽고 새 Pod가 "mysql-x7y9"로 살아나면, 다른 노드는 "내가 알던 노드가 사라졌네?"라고 혼란에 빠진다.
이 글이 푸는 것은: Kubernetes가 상태 있는(stateful) 워크로드를 위해 StatefulSet으로 "안정된 정체(stable identity)"를 어떻게 보장하는가다. 핵심은 숫자다 — 0, 1, 2라는 순서가 있는 이름이, Pod가 죽었다 살아나도 변하지 않는다는 것.
StatefulSet이 보장하는 세 가지 안정성
Deployment가 만드는 Pod는 이름이 무작위 접미사(mysql-a1b2)를 단다. StatefulSet이 만드는 Pod는 순차 번호를 단다: mysql-0, mysql-1, mysql-2. 그리고 이 이름이 안정적이다. StatefulSet이 보장하는 것 세 가지:
- 안정된 네트워크 정체성: Pod 이름(
mysql-0)과 그에 대응하는 DNS 이름(mysql-0.mysql.default.svc.cluster.local)이 죽었다 살아나도 같다. - 안정된 스토리지: 각 Pod는 자기 전용 영구 볼륨(PV)을 갖고, 재생성돼도 같은 PV에 다시 연결된다.
- 순차성: 0번이 떠야 1번이 뜨고(생성), 역순으로 종료된다.
flowchart TD
SS["StatefulSet<br/>replicas: 3"] --> P0["mysql-0<br/>→ PV-A (전용)"]
SS --> P1["mysql-1<br/>→ PV-B (전용)"]
SS --> P2["mysql-2<br/>→ PV-C (전용)"]
HS["headless Service: mysql"] -. DNS .-> P0
HS -. DNS .-> P1
HS -. DNS .-> P2
이름이 번호라는 게 왜 중요한가? 상태ful 앱(MySQL Galera, ZooKeeper, Elasticsearch)은 보통 토폴로지를 설정으로 갖는다. "노드 0번은 이 역할, 1번은 저 역할"처럼. 이름이 안정적이면 이 설정이 깨지지 않는다.
headless Service — Pod 하나를 이름으로 찌르는 법
일반 Service(ClusterIP)는 여러 Pod를 묶어 하나의 가상 IP로 부른다. 로드밸런싱이 목적이다. 그런데 상태ful 앱은 "아무 Pod"가 아니라 *"정확히 mysql-0"*를 찔러야 한다 — 리더를 찌르거나, 특정 노드에만 쓰기를 보내거나.
이걸 가능하게 하는 게 headless Service(clusterIP: None)다. headless Service는 가상 IP를 주지 않는 대신, DNS가 각 Pod의 IP를 직접 반환하게 한다.
# Kubernetes 1.36
apiVersion: v1
kind: Service
metadata:
name: mysql # headless
spec:
clusterIP: None # 핵심: 가상 IP 없음
selector:
app: mysql
ports: [{port: 3306}]
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql # 이 headless Service와 짝
replicas: 3
selector:
matchLabels: {app: mysql}
template:
metadata:
labels: {app: mysql}
spec:
containers:
- name: mysql
image: mysql:8.4
이제 DNS가 두 종류의 질의를 응답한다:
mysql.default.svc.cluster.local→ Pod 3개의 IP 전부(클라이언트가 고름).mysql-0.mysql.default.svc.cluster.local→ 정확히 mysql-0의 IP.
# Kubernetes 1.36 — 특정 Pod를 이름으로 찾기
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- \
nslookup mysql-0.mysql.default.svc.cluster.local
확인할 것: mysql-0이라는 안정된 이름이 그 Pod의 IP를 반환한다. Pod가 재생성돼 IP가 바뀌어도 이름은 같은 Pod를 가리킨다.
headless Service의 이 DNS 동작은 StatefulSet이
serviceName으로 그 Service를 가리킬 때만 완전히 성립한다. 잊지 말 것.
volumeClaimTemplates — 각 Pod마다 전용 영구 볼륨
StatefulSet의 또 다른 핵심: volumeClaimTemplates. 각 Pod마다 자기 전용 PVC/PV를 자동으로 만들어 준다.
spec:
serviceName: mysql
replicas: 3
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests: {storage: 10Gi}
template:
spec:
containers:
- name: mysql
image: mysql:8.4
volumeMounts:
- {name: data, mountPath: /var/lib/mysql}
이렇게 하면 Pod마다 data-mysql-0, data-mysql-1, data-mysql-2라는 PVC가 생기고, 각 Pod는 자기 번호에 맞는 PVC에 연결된다. 핵심 보장: mysql-0이 죽어서 새 mysql-0이 떠도, 그것은 다시 data-mysql-0에 연결된다 — 이전 데이터가 그대로 남아 있다.
이것이 Deployment와 결정적으로 다른 점이다. Deployment는 Pod가 죽으면 새 Pod가 아무 노드에 새 볼륨으로 뜰 수 있다. 상태ful 앱엔 치명적이다 — "내 데이터"가 사라지거나 섞이니까. StatefulSet은 어느 데이터가 어느 Pod에를 영속적으로 묶는다.
순차적 생성과 종료 — 왜 순서가 보장되는가
StatefulSet은 Pod를 0번부터 순서대로 만든다. mysql-0이 ready(준비됨)돼야 mysql-1을 만들고, 그 다음 mysql-2. 종료는 역순 — 2번부터 죽는다. 이 순서 보장이 왜 필요한가?
상태ful 클러스터엔 보통 초기화 순서가 있다. 예를 들어 "첫 번째 노드가 데이터를 부트스트랩하고, 그다음 노드들이 첫 번째에 합류한다"는 패턴(ZooKeeper, etcd). 순서가 없으면 누가 부트스트랩할지 모호해진다.
# Kubernetes 1.36 — StatefulSet 생성 순서 관찰
kubectl get pods -l app=mysql -w
확인할 것: mysql-0이 먼저 Running+Ready가 되고, 그 후 mysql-1이 생긴다. 동시에 안 생김.
NAME READY STATUS RESTARTS AGE
mysql-0 0/1 ContainerCreating 0 1s
mysql-0 1/1 Running 0 3s
mysql-1 0/1 Pending 0 0s
mysql-1 0/1 ContainerCreating 0 1s
mysql-1 1/1 Running 0 4s
...
순서를 끄는 옵션도 있다 —
podManagementPolicy: Parallel. 이땐 동시 생성된다. 단 정체성 보장(이름/PV)은 여전히 순차 번호로. 병렬은 보통 동질적 replica(예: 캐시 노드)에만.
StatefulSet과 RollingUpdate — 한 번에 하나씩, 역순으로
StatefulSet도 롤아웃을 한다. 이미지를 바꾸면 역순으로(2번→1번→0번) 한 번에 하나씩 교체한다. 각 Pod가 ready+lready가 돼야 다음으로 넘어간다. Deployment의 maxSurge 같은 "초과 생성"은 없다 — 한 번에 정확히 하나만.
이 점진성이 느리게 느껴질 수 있다(replicas 100이면 한 번에 하나씩). 그래서 큰 StatefulSet엔 OnDelete 전략(컨트롤러가 자동 안 바꾸고, Pod가 수동 삭제될 때만 새 버전으로)을 쓰기도 한다. 단 관리 부담이 커진다.
직접 확인하기
# Kubernetes 1.36 — StatefulSet + headless Service 한 번에 만들기
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata: {name: web}
spec: {clusterIP: None, selector: {app: web}, ports: [{port: 80}]}
---
apiVersion: apps/v1
kind: StatefulSet
metadata: {name: web}
spec:
serviceName: web
replicas: 3
selector: {matchLabels: {app: web}}
template:
metadata: {labels: {app: web}}
spec:
containers: [{name: nginx, image: nginx:1.27}]
EOF
kubectl get pods,pvc -l app=web
확인할 것: Pod 이름이 web-0,1,2(순차), PVC가 data-web-0,1,2로 각 Pod마다 하나씩.
# 특정 Pod DNS 해석
kubectl run t --image=busybox:1.36 --rm -it --restart=Never -- nslookup web-0.web
Name: web-0.web.default.svc.cluster.local
Address: 10.244.x.x
# Pod 하나를 지우면 — 같은 이름으로 같은 PVC로 재생성
kubectl delete pod web-1
kubectl get pod web-1
확인할 것: web-1이 같은 이름으로 다시 생긴다. PVC data-web-1은 그대로.
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "StatefulSet은 단순히 디스크가 있는 Deployment다" | 아니다. 안정된 이름·DNS·순서·고정 PV 바인딩이 핵심. 디스크는 그중 하나 |
| "Pod 이름이 안정적이라 IP도 안정적이다" | IP는 바뀐다(재생성 시). 안정적인 건 이름과 그에 대한 DNS 매핑 |
| "headless Service는 로드밸런싱을 한다" | 안 한다. 각 Pod IP를 DNS로 노출. 클라이언트가 고름 |
| "PVC는 Pod마다 자동 생긴다" | StatefulSet의 volumeClaimTemplates로만. Deployment는 안 됨 |
| "StatefulSet은 항상 순차 기동한다" | 기본은 그렇지만 podManagementPolicy: Parallel이면 동시 |
| "StatefulSet이면 무조건 상태ful 앱에 맞다" | 상태ful 앱이 StatefulSet 기능(이름/순서)을 필요로 할 때만. 단순 캐시는 Deployment도 충분 |
요약 — 이 글의 결론
- StatefulSet의 본질은 "안정된 정체성"이다. 이름(
mysql-0)·DNS·전용 PV가 Pod가 죽었다 살아나도 변하지 않는다 — 상태ful 클러스터의 토폴로지 설정이 깨지지 않는 토대. - headless Service(
clusterIP: None)가 각 Pod를 이름으로 직접 찌르게 한다. "아무 Pod"가 아니라 "정확히 mysql-0"로. - volumeClaimTemplates가 Pod마다 전용 PVC/PV를 묶어 준다. 같은 번호의 Pod는 항상 같은 PV — 데이터 영속성의 핵심.
- 순차 생성/역순 종료가 상태ful 클러스터의 부트스트랩 순서를 보장한다.
podManagementPolicy: Parallel로 끌 수도 있다. - Deployment와 결정적 차이: Deployment는 Pod 정체성이 무작위·휘발성. StatefulSet은 번호가 붙고 영속적. "상태"가 필요하면 StatefulSet, 무상태면 Deployment가 정답.
- 롤아웃은 역순, 한 번에 하나. Deployment의 maxSurge 같은 초과 생성이 없다. 그래서 큰 StatefulSet은 느리다 — OnDelete로 수동 제어하기도.
생각해 볼 문제
mysql-0의 IP가 바뀌어도 DNS 이름이 같은 Pod를 가리키는 메커니즘은? (headless Service와 kube-DNS의 관계)- StatefulSet에서
replicas를 3→1로 줄이면 어떤 Pod가 먼저 죽나? 그때 그 Pod의 PVC는 어떻게 되나? - headless Service 없이 StatefulSet을 만들면 DNS 이름이 동작하는가?
serviceName의 역할은? - Deployment에 PVC를 달면 왜 상태ful 앱에 부적합한가? 두 가지 이상 말하라.
- StatefulSet의
OrderedReady가 느린 이유를, replicas 100 클러스터 관점에서 설명하라. - 일시적 네트워크 단절로
mysql-2가 잠시 안 보였다가 돌아왔다. StatefulSet은 새 Pod를 만들까, 기존 것을 둘까?
참고
- Kubernetes 공식 문서 - StatefulSets - 접근 2026-07-12 (안정된 정체성, 순차성)
- Kubernetes 공식 문서 - Headless Service와 StatefulSet DNS - 접근 2026-07-12
- Kubernetes 공식 문서 - volumeClaimTemplates - 접근 2026-07-12
- Kubernetes 공식 문서 - Pod Management Policy - 접근 2026-07-12 (OrderedReady vs Parallel)
- Kubernetes 1.36 API - StatefulSetSpec - 접근 2026-07-12
'Tech Artifacts > Kubernetes' 카테고리의 다른 글
| Kubernetes - 08. scheduling (0) | 2026.07.13 |
|---|---|
| Kubernetes - 07. daemonset job cronjob (0) | 2026.07.13 |
| Kubernetes - 05. deployment (0) | 2026.07.13 |
| Kubernetes - 04. pod (0) | 2026.07.13 |
| Kubernetes - 03. api machinery (0) | 2026.07.13 |
