[Database/Kubernetes/DOIK] StatefulSet & Headless Service
Database를 Kubernetes 환경에서 운영하기 위해서 반드시 필요한 것은 아래와 같다.
- 휘발되지 않은 영구 저장소
- pod의 Dynamic IP 주소가 아닌 Static한 접속 주소
- 보안 감사 만족을 위한 DB 접근 통제
- DB 상태가 정상인지 판단할 기준
💡 영구 저장소와 Static 접속 주소의 조건을 만족시키는 것이 바로 StatefulSet이다.
☝ StatefulSet & Headless Service의 개념을 정리하자면 ????
✔ StatefulSet 이란?
StatefulSet은 kubernetes 리소스 중 Deployment와 유사하게 pod를 관리하는 리소스이다.
scaling을 관리하며, pod들의 순서 및 고유성을 보장한다.
Deployment와 비교하여 말하자면
Deployment
의 경우, pod명이 난수로 생성되어 생성되는 순서나 pod명의 고유성을 보장받지 못한다.
하지만 StatefulSet
의 경우, pod명이 난수가 아닌 고유한 숫자를 가지게 되고 숫자 0부터 순서대로 생성된다. (만약 중간에 1번을 지운다고 해도 다음 생성되는 숫자는 1이다.)
아래 Deployment와 StatefulSet로 생성한 pod 명의 비교 예시를 가져 왔다.
(🐱 |DOIK-Lab:default) root@k8s-m:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
deploy-cf9b57fb9-gdd68 1/1 Running 0 8s
deploy-cf9b57fb9-p2dtq 1/1 Running 0 39s
deploy-cf9b57fb9-tzxnz 1/1 Running 0 8s
(🐱 |DOIK-Lab:default) root@k8s-m:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
stateful-0 1/1 Running 0 102s
stateful-1 1/1 Running 0 82s
stateful-2 1/1 Running 0 62s
✔ Headless Service 란?
Kubernetes 리소스에서 Service는 애플리케이션을 네트워크 서비스로 노출하는 방법이다.
ServiceTypes
은 ClusterIP, NodePort, LoadBalancer, ExternalName가 있는데 Headless Service를 사용하려면 우선 ServiceTypes
은 ClusterIP로 설정하면 된다.
ClusterIP는 서비스를 클러스터-내부IP에 노출시키는 거라 클러스터 내에서만 서비스에 도달할 수 있다.
근데 Headless Service라니.. 말 그대로 머리가 없는 서비스다(?)
(이미지 출처 : Pop art of cartoon headless businessman © studiostoks - shutterstock)
ClusterIP: None
으로 설정하여, 클러스터 네트워크 내부에서 해당 Pod에 직접 접근 가능한 고유 IP와 DNS가 생기는 것이다.
🤘 실습으로 확인하는 StatefulSet & Headless Service
✔ StatefulSet으로 pod를 배포해 보자!
-
web.yaml 파일 받아 오기
(🐱 |DOIK-Lab:default) root@k8s-m:~# curl -s -O https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/web/web.yaml (🐱 |DOIK-Lab:default) root@k8s-m:~# cat web.yaml ───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ File: web.yaml ───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ apiVersion: v1 2 │ kind: Service 3 │ metadata: 4 │ name: nginx 5 │ labels: 6 │ app: nginx 7 │ spec: 8 │ ports: 9 │ - port: 80 10 │ name: web 11 │ clusterIP: None 12 │ selector: 13 │ app: nginx 14 │ --- 15 │ apiVersion: apps/v1 16 │ kind: StatefulSet 17 │ metadata: 18 │ name: web 19 │ spec: 20 │ serviceName: "nginx" 21 │ replicas: 2 22 │ selector: 23 │ matchLabels: 24 │ app: nginx 25 │ template: 26 │ metadata: 27 │ labels: 28 │ app: nginx 29 │ spec: 30 │ containers: 31 │ - name: nginx 32 │ image: k8s.gcr.io/nginx-slim:0.8 33 │ ports: 34 │ - containerPort: 80 35 │ name: web 36 │ volumeMounts: 37 │ - name: www 38 │ mountPath: /usr/share/nginx/html 39 │ volumeClaimTemplates: 40 │ - metadata: 41 │ name: www 42 │ spec: 43 │ accessModes: [ "ReadWriteOnce" ] 44 │ resources: 45 │ requests: 46 │ storage: 1Gi 47 │ ───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
-
web.yaml으로 Headless Service와 StatefulSet 생성
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl apply -f web.yaml && kubectl get pods -w -l app=nginx service/nginx created statefulset.apps/web created NAME READY STATUS RESTARTS AGE web-0 0/1 Pending 0 0s web-0 0/1 Pending 0 9s web-0 0/1 ContainerCreating 0 9s web-0 0/1 ContainerCreating 0 9s web-0 1/1 Running 0 18s web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 10s web-1 0/1 ContainerCreating 0 10s web-1 0/1 ContainerCreating 0 11s web-1 1/1 Running 0 20s
-
web-0, web-1 pod에 접속해서 hostname 출력하기
(🍎 |DOIK-Lab:default) root@k8s-m:~# for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done web-0 web-1
-
다른 터미널로 모니터링
0️⃣ Headless Service 특징 ) pod마다 고유한 주소를 갖는다
- netshoot 이미지로 netdebug pod 생성하여 zsh로 접속!
--rm
: 쉘이 종료되면 pod를 자동으로 삭제해 주는 옵션이다. (pod를 일시적으로 생성)--restart
: Container restartPolicy 옵션을 의미하며, 사용 가능한 값은 Always, OnFailure, Never이다. (기본 값: Always)
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl run -it --rm netdebug --image=nicolaka/netshoot --restart=Never -- zsh If you don't see a command prompt, try pressing enter. dP dP dP 88 88 88 88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P 88' `88 88ooood8 88 Y8ooooo. 88' `88 88' `88 88' `88 88 88 88 88. ... 88 88 88 88 88. .88 88. .88 88 dP dP `88888P' dP `88888P' dP dP `88888P' `88888P' dP Welcome to Netshoot! (github.com/nicolaka/netshoot) netdebug ~
-
nginx service 이름으로 도메인 질의했더니 headless service에 묶인 pod의 ip 리스트를 보여 준다.
netdebug ~ nslookup nginx Server: 10.200.1.10 Address: 10.200.1.10#53 Name: nginx.default.svc.cluster.local Address: 172.16.24.2 Name: nginx.default.svc.cluster.local Address: 172.16.158.2
-
각 pod의 레코드 값을 직접 알기 위해
-type=srv
라는 태그 값을 입력해 준다.netdebug ~ nslookup -type=srv nginx Server: 10.200.1.10 Address: 10.200.1.10#53 nginx.default.svc.cluster.local service = 0 50 80 web-0.nginx.default.svc.cluster.local. nginx.default.svc.cluster.local service = 0 50 80 web-1.nginx.default.svc.cluster.local.
-
각 pod의 고유한 주소로 특정 pod를 찾아갈 수 있다.
netdebug ~ nslookup web-0.nginx Server: 10.200.1.10 Address: 10.200.1.10#53 Name: web-0.nginx.default.svc.cluster.local Address: 172.16.158.2 netdebug ~ nslookup web-1.nginx Server: 10.200.1.10 Address: 10.200.1.10#53 Name: web-1.nginx.default.svc.cluster.local Address: 172.16.24.2
-
쉘 종료하니 pod가 자동 삭제됐다!
netdebug ~ exit pod "netdebug" deleted ```
1️⃣ StatefulSet 특징1) pod를 삭제하면 동일한 이름을 가진 pod로 재생성된다
-
pod 삭제 실행 후 재실행 시 pod명 확인
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl delete pod -l app=nginx && kubectl get pods -w -l app=nginx pod "web-0" deleted pod "web-1" deleted NAME READY STATUS RESTARTS AGE web-0 0/1 ContainerCreating 0 1s web-0 1/1 Running 0 2s web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 0s web-1 0/1 ContainerCreating 0 0s web-1 0/1 ContainerCreating 0 1s web-1 1/1 Running 0 2s
-
각 pod로 접속하여 hostname 확인
(🍎 |DOIK-Lab:default) root@k8s-m:~# for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done web-0 web-1
2️⃣ StatefulSet 특징2) pod는 영속적인 저장소를 가질 수 있다
-
pvc 확인
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl get pvc -l app=nginx NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound pvc-7cce446e-e68a-4783-99f9-fbb891538669 1Gi RWO local-path 11m www-web-1 Bound pvc-7defb95e-8b02-4886-851b-e339e234fd71 1Gi RWO local-path 11m
-
웹 서버 index.html 에 hostname 추가 후 웹 접속 해서 확인
(🍎 |DOIK-Lab:default) root@k8s-m:~# for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)-pv-test" > /usr/share/nginx/html/index.html'; done (🍎 |DOIK-Lab:default) root@k8s-m:~# for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done web-0-pv-test web-1-pv-test
-
app=nginx
label 값을 가진 pod 삭제 실행 후 scaling 확인(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl delete pod -l app=nginx && kubectl get pods -w -l app=nginx pod "web-0" deleted pod "web-1" deleted NAME READY STATUS RESTARTS AGE web-0 0/1 ContainerCreating 0 0s web-0 0/1 ContainerCreating 0 1s web-0 1/1 Running 0 2s web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 0s web-1 0/1 ContainerCreating 0 0s web-1 0/1 ContainerCreating 0 1s web-1 1/1 Running 0 2s
-
웹 접속 해서 확인 : PV 저장소 확인
✅ 고유 이름을 가진 pod가 삭제되고 다시 생성되더라도 해당 pod는 기존 사용하던 pv만 사용한다
(ex.web-0
라는 pod는web-0-pv-test
라는 pv만 계속 물고 온다)
✅ 이게 바로 영속적인 저장소를 보장받는 StatefulSet의 특징이다(🍎 |DOIK-Lab:default) root@k8s-m:~# for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done web-0-pv-test web-1-pv-test
3️⃣ StatefulSet 특징3) pod는 고유한 이름을 가지며, 0부터 순차적으로 생성된다
-
pod scale out
✅ pod가 증가 또는 감소할 때 순차적으로 생성되는 것을 확인할 수 있다.
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl scale sts web --replicas=5 && kubectl get pods -w -l app=nginx statefulset.apps/web scaled NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 88s web-1 1/1 Running 0 86s web-2 0/1 Pending 0 0s web-2 0/1 Pending 0 10s web-2 0/1 ContainerCreating 0 10s web-2 0/1 ContainerCreating 0 11s web-2 1/1 Running 0 18s //...생략...
-
pod scale in
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl patch sts web -p '{"spec":{"replicas":3}}' && kubectl get pods -w -l app=nginx statefulset.apps/web patched NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 2m54s web-1 1/1 Running 0 2m52s web-2 1/1 Running 0 86s web-3 1/1 Running 0 68s web-4 1/1 Terminating 0 61s web-4 1/1 Terminating 0 62s web-4 0/1 Terminating 0 62s web-4 0/1 Terminating 0 62s web-4 0/1 Terminating 0 62s //...생략...
✔ 실습을 마치고 리소스 삭제!
(🍎 |DOIK-Lab:default) root@k8s-m:~# kubectl delete -f web.yaml && kubectl delete pvc --all
service "nginx" deleted
statefulset.apps "web" deleted
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
📚 참고 자료
- 🚀가시다님 노션🚀
Leave a comment