스테이트풀셋을 삭제한 경우에는(kubectl delete -f 든, kubectl delete sts 든) PV, PVC 는 삭제되지 않고 남아있게 되며, PVC 와 PV 의 바인딩 관계도 그대로 유지되게 된다. 때문에 다시 스테이트풀셋을 생성한 경우에 남아있던 영구 볼륨 데이터 그대로 파드가 기동되어 재사용하게 된다. 레플리카수를 scale in 하였다가 다시 scale out 한 경우도 마찬가지로 동작한다.

$ kubectl apply -f sample-statefulset.yaml
$ kubectl get pv
$ kubectl get pvc

# sample-statefulset 을 수동으로 삭제한 후에도 PV, PVC 가 유지되는지 확인

$ kubectl delete statefulset sample-statefulset
$ kubectl get pv
$ kubectl get pvc

 

직접 PVC 를 삭제하면, PV 와 바인딩이 풀리면서 PV 도 자동으로 삭제되게 된다.(영구볼륨의 ClaimPolicy 설정 기본값일 때)

$ kubectl delete pvc www-statefulset-{0..2}

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

데몬셋과 동일하게 OnDelete, RollingUpdate 를 지정할 수 있지만 몇가지 차이점이 있다.

 

1. RollingUpdate 시 데몬셋과 다른 점

OnDelete 는 데몬셋의 그것과 동일하게 동작하지만, RollingUpdate 의 경우에는 좀 다르다. 스테이트풀셋은 영속성 데이터를 다루는 리소스 컨트롤러 이므로, 추가 파드를 생성해서 롤링 업데이트를 할 수 없으며, 마찬가지로 maxUnavailable 를 지정하여 업데이트 중 동시에 여러 파드를 중지시킬 수도 없다. 파드마다 ready 상태 여부를 확인하고 순차적으로 업데이트하게 된다.(spec.podManagementPolicy: Parallel 이어도 마찬가지)

2. RollingUpdate 시 파티션을 나누어 업데이트

스테이트풀셋의 경우 spec.updateStrategy.rollingUpdate.partition 필드에 RollingUpdate 를 적용할 파티션을 지정할 수 있다.(아래 참고) partition 을 설정하면, 해당 값 이상의 인덱스 값을 가진 파드들만 업데이트 대상이 된다. 수동으로 재기동한 경우에도 partition 의 영향을 받는다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-rollingupdate
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 3
  serviceName: statefulset-rollingupdate
  replicas: 5
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.16

 

 

위 매니페스트를 사용하여 스테이트풀셋을 만들고, 파드를 업데이트하면서 관찰해보면, 실제로 partition 에 해당하는 인덱스 이상의 파드만 업데이트 되는 것을 볼 수 있다.

$ kubectl apply -f statefulset-rollingupdate.yaml
# statefulset-rollingupdate-0 ~ 4 까지 5개의 파드가 순차적으로 생성됨

# nginx:1.16 -> nginx:1.17 로 이미지 수정 후 다시 apply 를 진행
# watch 를 이용하여 파드 상태를 관찰하면 아래와 같이 출력됨
$ kubectl get pods -l app=sample-app -o wide --watch
NAME                          READY   STATUS              RESTARTS   AGE   IP            NODE              NOMINATED NODE   READINESS GATES
statefulset-rollingupdate-4   1/1     Terminating         0          83s   10.244.2.11   desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   0/1     Completed           0          83s   10.244.2.11   desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   0/1     Completed           0          84s   10.244.2.11   desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   0/1     Completed           0          84s   10.244.2.11   desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   0/1     Pending             0          0s    <none>        <none>            <none>           <none>
statefulset-rollingupdate-4   0/1     Pending             0          0s    <none>        desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   0/1     ContainerCreating   0          0s    <none>        desktop-worker2   <none>           <none>
statefulset-rollingupdate-4   1/1     Running             0          1s    10.244.2.12   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   1/1     Terminating         0          86s   10.244.2.10   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   1/1     Terminating         0          86s   10.244.2.10   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   0/1     Completed           0          86s   10.244.2.10   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   0/1     Completed           0          87s   10.244.2.10   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   0/1     Completed           0          87s   10.244.2.10   desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   0/1     Pending             0          0s    <none>        <none>            <none>           <none>
statefulset-rollingupdate-3   0/1     Pending             0          0s    <none>        desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   0/1     ContainerCreating   0          0s    <none>        desktop-worker2   <none>           <none>
statefulset-rollingupdate-3   1/1     Running             0          1s    10.244.2.13   desktop-worker2   <none>           <none>

 

즉, 0번~2번 인덱스의 파드는 영향을 받지 않고, 3~4번 인덱스의 파드가 하나씩 순차적으로 Termination -> Completed 로 종료된 후, Pending -> ContainerCreating -> Running 으로 새로 실행된 것을 알 수 있다. 여기서 이번에는 partition 을 1로 변경 후 apply 하게 되면 1~2번 파드만 추가로 업데이트를 하게 된다.(기존 업데이트 완료되었던 3~4번 파드는 업데이트되지 않음)

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

데이터베이스와 같이 stateful 한 워크로드를 위한 리소스이다. 데이터를 영구적으로 저장하기 위해 사용한다.

 

1. 스테이트풀셋 생성

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-statefulset
spec:
  serviceName: sample-statefulset
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.16
          ports:
            - containerPort: 80
          volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: www
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1G

 

위 내용으로 스테이트풀셋을 생성하고, 아래 커맨드로 파드명 규칙을 확인해보면, 레플리카셋이나 데몬셋과는 다르게 파드명 suffix 로 incremental index 가 붙는것을 알 수 있다.

$ kubectl get pods -o wide -l app=sample-app

 

매니페스트에서 주요하게 보아야 할 내용은 아래와 같다.

  • spec.template.spec.containers[].volumeMounts[]: 컨테이너에서 사용할 볼륨명과 마운트 경로
  • spec.volumeClaimTemplates[]: 스테이트풀셋이 생성할 영구 볼륨 요청 템플릿

즉, 스테이트풀셋이 volumeClaimTemplates 에 정의된 볼륨 리소스를 생성하여 영구 볼륨을 확보하고, 이를 컨테이너에서 요청하는 것이다. 생성된 persistentvolumes 과 persistentvolumeclaims 는 다음 커맨드로 확인할 수 있다.

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-66206fda-ac99-4923-9b7c-eff0e3a3f677   1G         RWO            Delete           Bound    default/www-sample-statefulset-0   standard       <unset>                          22m
pvc-922ab66a-e5cc-447c-99d6-99d2f476345e   1G         RWO            Delete           Bound    default/www-sample-statefulset-1   standard       <unset>                          22m
pvc-9ecd3989-6435-4cbc-9e94-146866a59ddf   1G         RWO            Delete           Bound    default/www-sample-statefulset-2   standard       <unset>                          22m

$ kubectl get pvc
NAME                       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
www-sample-statefulset-0   Bound    pvc-66206fda-ac99-4923-9b7c-eff0e3a3f677   1G         RWO            standard       <unset>                 25m
www-sample-statefulset-1   Bound    pvc-922ab66a-e5cc-447c-99d6-99d2f476345e   1G         RWO            standard       <unset>                 22m
www-sample-statefulset-2   Bound    pvc-9ecd3989-6435-4cbc-9e94-146866a59ddf   1G         RWO            standard       <unset>                 22m

 

PV(persistentvolumes)와 PVC(persistentvolumeclaims) 에 대한 내용은 추후에 영구 볼륨을 따로 다룰 때 자세히 설명하고, 여기서는 간단히 정리하면 다음과 같다.

  • PV: 물리적인 스토리지. namespaced: false. 즉, cluster 전역 스토리지
  • PVC: 물리적인 스토리지를 namespace 에서 사용하기 위한 객체. namespaced: true
  • PVC 에서 PV 의 사용을 요청하기 위해서는 PVC 와 PV 의 아래 조건이 맞아야 한다.(바인딩 조건)
    • PVC 에 selector 가 설정되어 있으면 PV 중에서 selector 에 일치하는 key=value 를 찾는다.(optional)
    • spec.accessModes 가 일치하여야 한다.
    • PV 의 spec.capacity.storage 용량이 PVC spec.resources.request.storage 용량 이상이어야 한다.
    • PV 와 PVC 둘 다 storageClassName 이 명시되어 있으면 그 값이 일치하여야 한다.(둘 다 명시되어있지 않으면 빈문자열로 취급되어 일치함. 한쪽만 명시되면 불일치함)
  • PVC 는 파드에 설정되고, 파드는 PVC 를 통해서 PV 를 인식하고 사용한다.
  • PVC 한개가 여러개의 PV 에 바인딩 될 수는 없다.
  • 한번 바인딩이 되었다가 풀리면 PV 가 released 상태가 되며, 바인딩 되었던 PVC 에서는 다시 바인딩이 불가능하기 때문에, PV 를 삭제 후 다시 생성해야 한다. PV 를 조건에 맞게 생성하면 자동으로 바인딩이 이루어진다.

 

2. 스테이트풀셋 스케일링의 특이점

레플리카셋이나 데몬셋은 scale out 또는 scale in 시에 생성/삭제되는 파드가 무작위이지만, 스테이트풀셋은 아래와 같은 규칙이 있다.

  • scale out: pod 명 suffix index 가 1씩 증분하면서 생성되며, 하나의 파드가 생성 후 ready 상태가 되면 다음 파드가 생성된다.
  • scale in: pod 명의 suffix 가 큰 것부터 하나씩 삭제된다. 삭제될 때도 하나씩 순차적으로 삭제된다.

이러한 특징 때문에 0번째 파드가 항상 가장 오래 남아있게 되는데, 그 특징으로 0번째 파드를 마스터로 사용하는 이중화 애플리케이션에 적합하다.

 

3. 파드 관리 정책

위에서 스테이트풀셋의 파드는 생성/삭제시 순차적으로 진행된다고 하였는데, 이를 변경하는 파라미터가 있다. spec.podManagementPolicy 필드로, 기본값이 OrderedReady 이고, 이를 Parallel 로 설정하면 레플리카셋과 동일하게 파드 생성/삭제가 병렬로 이루어진다.

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,