포스트

Kubernetes (15) - StatefulSet으로 고정된 Pod 식별자와 스토리지 관리하기

Kubernetes (15) - StatefulSet으로 고정된 Pod 식별자와 스토리지 관리하기

1. StatefulSet이 필요한 이유

Deployment와 ReplicaSet은 같은 사양의 Pod를 여러 개 실행하고 개수를 유지하는 데 적합하다. 하지만 데이터베이스나 메시지 브로커처럼 각 복제본이 고유한 이름, 네트워크 주소, 디스크 데이터를 가져야 하는 애플리케이션에는 Pod를 서로 바꿔도 되는 방식이 맞지 않는다.

StatefulSet은 각 Pod에 고정된 순번과 이름, 안정적인 네트워크 식별자, Pod별 영속 스토리지를 제공하는 워크로드 리소스다. Pod가 삭제되어 새로 만들어져도 sf-nginx-0처럼 같은 순번의 이름을 유지하고, 설정한 PVC를 다시 연결할 수 있다.


2. StatefulSet Pod의 고유한 정체성

replicas: 3으로 sf-nginx StatefulSet을 만들면 Pod는 다음처럼 생성된다.

1
2
3
sf-nginx-0
sf-nginx-1
sf-nginx-2

끝의 숫자를 ordinal(순번)이라고 한다. 기본적으로 0부터 replicas - 1까지 부여되며, StatefulSet은 이를 기준으로 Pod를 식별한다.

구분DeploymentStatefulSet
Pod 이름임의의 접미사가 붙어 교체될 때 달라짐<StatefulSet 이름>-<순번> 형태로 유지
Pod 간 관계대체로 서로 교환 가능각 Pod가 고유한 정체성을 가짐
네트워크일반적으로 Service 뒤의 공통 접근점Pod별 고정 DNS 이름 사용 가능
스토리지별도 설정 필요volumeClaimTemplates로 Pod별 PVC 생성 가능
적합한 대상상태가 없는 웹 애플리케이션DB, 메시지 브로커, 분산 저장소

실습에서 kubectl describe pod sf-nginx-0를 실행하면 다음과 같은 라벨과 소유 관계를 확인할 수 있다.

1
2
3
4
5
6
Labels:
  app=webui
  apps.kubernetes.io/pod-index=0
  controller-revision-hash=sf-nginx-85cd44ddd9
  statefulset.kubernetes.io/pod-name=sf-nginx-0
Controlled By:  StatefulSet/sf-nginx

apps.kubernetes.io/pod-index=0statefulset.kubernetes.io/pod-name=sf-nginx-0는 이 Pod가 0번 복제본임을 보여 준다. Pod를 삭제해도 StatefulSet은 sf-nginx-0이라는 이름의 대체 Pod를 생성한다. 다만 Pod UID와 IP는 새로 바뀔 수 있다.


3. Headless Service로 Pod별 DNS 만들기

StatefulSet의 serviceName은 Pod의 네트워크 도메인을 결정한다. 이 이름과 같은 Headless Service를 사용자가 먼저 만들어야 Pod별 DNS 이름을 안정적으로 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# sf-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sf-service
spec:
  clusterIP: None
  selector:
    app: webui
  ports:
    - name: http
      port: 80
      targetPort: 80

clusterIP: None은 이 Service가 단일 가상 IP로 로드 밸런싱하지 않는 Headless Service임을 뜻한다. 기본 네임스페이스와 기본 클러스터 도메인에서는 각 Pod를 다음 형식으로 조회할 수 있다.

1
2
3
sf-nginx-0.sf-service.default.svc.cluster.local
sf-nginx-1.sf-service.default.svc.cluster.local
sf-nginx-2.sf-service.default.svc.cluster.local

sf-service는 모든 Pod를 하나로 숨기는 일반 Service가 아니라, StatefulSet Pod가 각자의 DNS 이름을 갖도록 도와주는 역할을 한다.


4. NGINX StatefulSet 매니페스트

다음 예제는 sf-service를 네트워크 도메인으로 사용하고 NGINX Pod 세 개를 생성한다. 제공된 실습 예제의 podManagementPolicy: Parallel을 그대로 사용했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# statefulset-exam.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sf-nginx
spec:
  serviceName: sf-service
  replicas: 3
  podManagementPolicy: Parallel
  selector:
    matchLabels:
      app: webui
  template:
    metadata:
      labels:
        app: webui
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.14
          ports:
            - containerPort: 80

selector.matchLabelstemplate.metadata.labels는 일치해야 한다. StatefulSet의 selector는 생성 후 변경할 수 없으므로, 다른 컨트롤러와 겹치지 않는 라벨을 정해야 한다.

위 매니페스트는 Pod 이름과 DNS 식별자는 제공하지만 영속 스토리지는 만들지 않는다. 실제 상태 저장 애플리케이션에는 volumeClaimTemplates를 추가해 Pod마다 별도의 PVC를 만들어야 한다.

1
2
3
4
5
6
7
8
volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi

컨테이너에는 이 PVC를 volumeMounts로 연결한다. 예를 들어 sf-nginx-0에는 data-sf-nginx-0, sf-nginx-1에는 data-sf-nginx-1처럼 Pod별 PVC가 생성된다. StatefulSet을 축소하거나 삭제해도 이 PVC와 연결된 볼륨은 기본적으로 자동 삭제되지 않으므로, 데이터 보존과 정리 정책을 별도로 결정해야 한다.


5. OrderedReady와 Parallel의 차이

StatefulSet은 Pod의 고유한 정체성은 유지하면서, 생성·종료 순서를 podManagementPolicy로 선택할 수 있다.

정책동작적합한 경우
OrderedReady0부터 순서대로 만들고, 이전 Pod가 Ready가 된 뒤 다음 Pod를 생성한다. 축소 시에는 큰 순번부터 종료한다. 기본값이다.순서 있는 초기화와 기동이 필요한 클러스터
ParallelPod 생성·종료 시 다른 Pod의 Ready·종료를 기다리지 않고 병렬로 처리한다.순서는 중요하지 않지만 Pod별 이름·스토리지는 필요한 경우

실습 매니페스트는 Parallel을 사용하므로 sf-nginx-0, sf-nginx-1, sf-nginx-2가 동시에 생성될 수 있다. 병렬 정책도 Pod 이름, DNS, PVC의 고유성은 유지한다. 다만 분산 데이터베이스처럼 시작 순서가 중요한 애플리케이션에는 기본값인 OrderedReady가 더 적합할 수 있다.


6. 생성, Pod 복구, 스케일 조정

Headless Service와 StatefulSet을 순서대로 적용한다.

1
2
3
4
kubectl apply -f sf-service.yaml
kubectl apply -f statefulset-exam.yaml
kubectl get sts
kubectl get pods -l app=webui

관리 중인 0번 Pod를 삭제하면 StatefulSet은 같은 순번을 가진 Pod를 다시 만든다.

1
2
kubectl delete pod sf-nginx-0
kubectl get pods -l app=webui --watch

새 Pod는 sf-nginx-0이라는 이름을 유지하지만, 재생성된 Pod이므로 이전 Pod와 IP는 다를 수 있다. volumeClaimTemplates를 사용했다면 0번 Pod에 연결된 기존 PVC를 다시 사용한다.

복제본 수는 scale 명령 또는 매니페스트의 spec.replicas로 변경한다.

1
2
3
kubectl scale sts sf-nginx --replicas=4
kubectl scale sts sf-nginx --replicas=2
kubectl scale sts sf-nginx --replicas=3

4개로 확장하면 sf-nginx-3이 추가되고, 2개로 축소하면 높은 순번의 Pod가 제거되어 sf-nginx-0, sf-nginx-1만 남는다. Parallel 정책에서는 이 과정이 병렬로 진행될 수 있다.


7. 업데이트와 롤백

컨테이너 이미지처럼 spec.template을 변경하면 StatefulSet의 기본 전략인 RollingUpdate가 동작한다. 기본 정책에서는 가장 큰 ordinal부터 하나씩 Pod를 교체하고, 새 Pod가 Ready가 된 뒤 다음 Pod를 업데이트한다.

1
2
kubectl set image sts/sf-nginx nginx-container=nginx:1.15
kubectl rollout status sts/sf-nginx

실습에서도 이미지 변경 뒤 sf-nginx-0을 조회하면 nginx:1.15가 실행 중인 것을 확인할 수 있다.

배포 이력을 보고 이전 리비전으로 되돌릴 수 있다.

1
2
3
kubectl rollout history sts/sf-nginx
kubectl rollout undo sts/sf-nginx
kubectl rollout status sts/sf-nginx

특정 리비전으로 되돌릴 때는 --to-revision을 지정한다.

1
kubectl rollout undo sts/sf-nginx --to-revision=2

잘못된 이미지처럼 새 Pod가 Ready 상태가 될 수 없는 설정으로 업데이트하면, OrderedReady StatefulSet의 롤아웃은 해당 Pod에서 멈출 수 있다. 템플릿을 이전 정상 설정으로 되돌린 뒤에도 문제가 된 Pod를 직접 삭제해야 복구가 진행될 수 있으므로, 운영 환경에서는 kubectl rollout status로 진행 상태를 반드시 확인한다.


8. 정리

StatefulSet은 sf-nginx-0처럼 고정된 Pod 이름과 순번을 제공하며, Headless Service를 통해 Pod별 DNS 이름을 구성한다. volumeClaimTemplates를 사용하면 Pod마다 분리된 영속 볼륨을 연결할 수 있어, DB·메시지 브로커·분산 저장소 같은 상태 저장 애플리케이션을 관리하는 데 적합하다.

이번 실습처럼 scale, Pod 삭제, rollout undo를 실행해 보면 Pod 이름은 유지하면서도 컨트롤러가 복구·확장·업데이트를 수행하는 방식을 확인할 수 있다. 다만 StatefulSet을 삭제하거나 축소해도 PVC가 기본적으로 남는다는 점을 기억하고, 데이터 보존 정책을 명확히 세워야 한다.


참고 자료

  • [StatefulSetsKubernetes 공식 문서](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.