본문 바로가기
IT/Kubernetes

[Kubernetes] StatefulSet

by 물통꿀꿀이 2019. 6. 7.

이번 포스팅에서는 StatefulSet에 대해 알아보려고 한다.


ReplicaSet


그림 1. ReplicaSet


다시금 ReplicaSet을 살펴보면 그림 1 처럼 미리 정의해 놓은 Pod의 설정을 Replicas 개수에 따라 만든다.


그림 2. PVC & PV


더군다나 Pod를 설정 할 때 PVC와 같은 Volume을 포함하게 되면 그림 2와 같이 Replicas로 만들어진 모든 Pod는 동일한 PVC를 참조하게 된다. (ReplicaSet으로 만들어지는 모든 Pod는 동일한 매니페스트 파일을 사용하기 때문에)


만일, 위와 같이 사용하고 싶다면 큰 문제는 없지만 그렇지 않다면 문제가 될 수 있다. 이미 매니페스트 파일을 만들 때 PVC를 정의했기 때문에 각 Pod는 다른 PVC를 접근할 수 없다. 이러한 상황에서 다음과 같은 방법으로 접근 할 수 있다.

1) 각 Pod마다 다른 ReplicaSet를 사용

Volume을 위해 Pod와 ReplicaSet를 1:1로 묶는 방식으로 Pod에 Volume을 각기 다르게 적용할 수 있지만 관리하기가 매우 어렵다. 더욱이 ReplicaSet의 장점을 희석시키고 있다.

물론 특정 Volume을 같이 사용한다면 Replicas 개수를 늘리면 되지만 전체적으로 Scaling up & down이 어렵다.

2) 동일한 Volume에 다른 디렉토리 사용

해당 Volume에 병목 현상이 발생 할 수 도 있다. 더군다나 ReplicaSet으로 생성되는 Pod는 하나의 Application이 동작하기 때문에 내부 비즈니스 로직을 여러 상황을 가정하고 만들어야 하기 때문에 문제가 발생 할 수 있는 여지도 크다.


Volume 이외에도 발생할 수 있는 문제는 Pod의 내부 속성에 대한 것이다.

앞서 말한바와 같이 ReplicaSet이 적용되면 Pod는 생성/삭제가 자유로워진다. 그런데 기존에 동작하는 Pod가 내려가로 이를 대체하는 새로운 Pod가 올라오면 문제는 기존 Pod와 새로운 Pod가 완전히 일치하지 않는다는 것이다. 즉, 다른 것은 동일할 수 있어도 Hostname, IP 등은 달라진다. (Pod가 생성될 때마다 Hostname과 IP는 새로운 것으로 할당 받기 때문에)


이처럼 ReplicaSet을 사용해 Pod를 관리하게 되면 많은 문제점이 발생 할 수 있다. 때문에 StatefulSet을 사용한다.


StatefulSet

StatefulSet은 Pod을 Scaling Up&Down 또는 Deploy 할 때, 각 Pod의 기존 Spec(Unique 설정 값들)을 유지시켜 준다. 즉, StatefulSet은 sticky 값을 유지하고 생성할 때 기존 값을 사용하여 그대로 만들어 준다. (때문에 Pod의 속성들이 변경되지 않는다.) 


그렇다면 와닿지 않을 수 있으니 간단히 ReplicaSet과 StatefulSet을 비교해 보자.


그림 3. Pets Vs Cattle


뜬금없는 그림으로 보이지만 전혀 그렇지 않다. 흔히 많이 비교하는 개념으로 ReplicaSet은 Cattle, StatefulSet은 Pets로 비유한다.

먼저 Cattle의 경우는 전체의 관점에서 관리한다. 때문에 모든 Instance 하나 하나에 대해 면밀히 관심을 두는 것이 아닌 Instance 하나가 죽으면 새로운 것으로 교체한다.

(소 목장에서 소가 병들어 죽으면 다른 소를 가져오는 것처럼)

이와 반대로 Pets의 경우는 개별의 관점에 관리한다. Cattle과 관점이 정반대로 Instance 하나 하나에 면밀히 관심을 두고 Instacne 하나가 죽으면 완전 똑같은 것으로 다시 만드는 것이다.

(물론 고양이, 개와 같은 동물은 죽으면 대체할 수 없겠지만..)


아무튼, 다시 ReplicaSet과 StatefulSet으로 돌아와서 정리해보면 Cattle은 ReplicaSet에 해당하고 Pets는 StatefulSet에 해당한다.

몇몇 부분만 비교를 해보면..

- Pod ID (Identity)

그림 4. ReplicaSet Pods

그림 5. StatefulSet Pods


그림 4에서 볼 수 있듯이, ReplicaSet으로 생성된 Pod는 ReplicaSet Name + 임의의 숫자가 붙어있다.

반면에 StatefulSet은 StatefulSet Name + Ordering이 되어 있는 것을 확인 할 수 있다.

이렇듯 Pod ID 관해서 StaefulSet은 replicas 개수에 따라 숫자가 붙게 되어 ID를 예측하기가 쉽다. (숫자는 0 부터 N-1)


StatefuSet을 Scaling 할 때는 그림 6에서 보는 바와 같이 Ordering 단위로 이루어진다. 즉, Scale Up 할 때는 마지막 Ordering 다음 번호를 Scale Down 할 때는 마지막 Ordering 번호를 선택한다. 때문에 사용자 입장에서는 임의로 지워지는 것이 아니기 때문에 추가&삭제 Pod를 쉽게 알 수 있는 장점이 있다.


다만, StatefulSet은 Scale Down이 되었을 경우 빠른게 작업을 수행하지 않는다. 그 이유는 내부 데이터 손실을 막기 위해 저장할 곳을 찾기 때문이다. 


그림 6. Re-distributing Data on Scale Down


그러므로 분산형 스토리지의 경우에 동시에 StatefulSet Pod를 Scale Down 했을 경우 데이터 손실이 올 수 있다. (현재 해결 방법은 순차적으로)


- Service

ReplicaSet과 달리 StatefulSet의 경우는 각 Pod 마다 주소를 지정해줘야 할 때가 있다. (내부 관리를 위해)

때문에 Service를 Headless로 사용한다.


이제까지 설명한 바를 좀 더 이해할 수 있도록 StatefulSet을 사용하여 한 가지 예시를 만들어보도록 하자.

('kubernetes in action'에서 소개하는 예시를 사용한다.)


세부 핵심 로직은 다음과 같다.

1) POST 요청일 때, Body 데이터를 파일로 작성한다.

2) GET 요청일 때, 저장된 파일을 반환한다.


이를 위해 준비해야 할 사항은 Service, StatefulSet, Volume이다. (파일을 저장해야 하기 때문에)

apiVersion: v1

kind: Service

...

spec:

  clusterIP: None // Headless 

  ...


apiVersion: apps/v1beta1

    kind: StatefulSet

    ...

        spec:

          containers:

            ...

            volumeMounts:

            - name: data

              mountPath: /var/data

      volumeClaimTemplates:

      - metadata:

          name: data

        spec:

          resources:

            requests:

              storage: 1Mi

          accessModes:

          - ReadWriteOnce 


StatefulSet을 사용하기 위해서는 Headless Service를 사용해야 한다. 

그리고 StatefulSet를 생성하는 매니페스트 파일을 보면 ReplicaSet과 크게 다르지 않은 것을 알 수 있다. 다만, volumeClaimTemplates을 사용한다는 것이 큰 차이점이다.

volumeClaimTemplates은 PVC(PersistentVolumeClaim)을 만드는 템플릿이다. 


그림 7. volumeClaimTemplates


실제로 파일을 실행하고 동작하는 Pod에 yaml 파일을 보면 volumes에 PVC가 만들어진 것을 확인 할 수 있다. 그리고 이를 통해 Pod에 PV(Persistent Volume)이 자동으로 Binding 되게 한다. (즉, Pod <-> PVC가 1:1로 Mapping 되었다.)

그림 8. PV(Persistent Volume)

(PV는 미리 정의된 StorageClass에 의해 만들어진 동적 Storage이다.)


다시 돌아가서 StatefulSet이 실행된 것을 보면 아래의 그림 9과 같이 replica 개수가 2개인 Pod를 확인 할 수 있다.


그림 9. Created StatefulSet


(참고로 StatefulSet은 Pod를 생성할 때 동시에 생성하지 않고 순차적으로 생성한다. - 내부 리소스 경합 이슈 때문)


이제 앞서 언급했던 GET/POST 호출을 통해 StatefulSet으로 만들어진 Pod가 정상적으로 동작하고 있는지 확인해보자.

Service는 Headless 모드(ClusterIP= None)이기 때문에 Service를 통해 접근 할 수 없다. 대신 직접 Pod에 접근 할 수 있는데 여기서는 kubectl Proxy를 통해 접근해보자.


그림 10. Access Pod through kubectl Proxy


그림 10은 Proxy를 통해 GET -> POST -> GET 작업을 수행하였다. 해당 작업을 통해 "kubia-0" Name의 Pod에 파일이 생성되었다는 것을 알 수 있다.


그렇다면 본격적으로 Pod를 삭제 이후에 재생성하면 그대로 상태를 유지하는지 살펴보겠다.


그림 11. Delete & Create Pod


그림 11과 같이 Pod를 삭제하면 ReplicaSet과 같이 StatefulSet도 Replicas의 개수에 맞춰 생성해준다.

그림 12. Re-Access Pod through kubectl Proxy


그리고 다시 GET 요청을 보내면 이전과 같이 그대로 결과 값이 오는 것을 확인 할 수 있다.

ReplicaSet에서는 Pod를 지우면 완전히 새로운 값으로 생성되지만 StatefulSet에서는 기존 값을 그대로 유지하고 더군다나 Volume이 연결된 상태에서도 기존 Volume을 그대로 유지해 준다. (그림 13 참조)


그림 13. StatefulSet Scaling


다만, 재생성되는 Pod가 항상 기존 Pod가 존재했던 Node에 그대로 생성되지 않을 수 있다. (사용할 수 있는 Node라면 어떤 곳에서든 생성된다.)

내부적으로 스케줄링되어 기존 Pod의 값을 그대로 가져간다.


Reference

https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/

https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

https://www.slideshare.net/LiranCohen4/scaling-docker-with-kubernetes-64160039

https://cioan.ca/posts/cattlenotpets-pt3/

https://medium.com/@marko.luksa/graceful-scaledown-of-stateful-apps-in-kubernetes-2205fc556ba9

https://github.com/luksa/kubernetes-in-action/tree/master/Chapter10

'IT > Kubernetes' 카테고리의 다른 글

[Kubernetes] Headless Service  (0) 2019.06.07
Docker, Pod 인자 전달 비교  (0) 2019.05.27
[Kubernetes] ConfigMap  (0) 2019.05.27
[Kubernetes] Internals - Pause Container  (0) 2019.05.16
[Kubernetes] Internals - Init Pod  (0) 2019.05.16

댓글