Kubernetes Controllers : StatefulSet

1. Overview

이번 문서에서는 Kubernetes(k8s)의 Controller중, StatefulSet에 대해서 알아보겠습니다.

2. Prerequisites

본문에서 사용한 spec :
OS : CentOS v7.6
Arch : x86

k8s클러스터는 1마스터 2노드로 구성했습니다.
Master : 4cpu, ram16G
Node : 2cpu, ram4G

3. StatefulSet

이전 포스팅에서 k8s의 여러 컨트롤러에 대해 알아봤었습니다. 이 컨트롤러들은 주로 상태가 없는(stateless) pod을 관리하는 용도로 사용됩니다.

참고 : 호롤리한 하루/Kubernetes Controllers : Replication, Deployment, DaemonSet

오늘 소개할 StatefulSet Controller는 이름에서 느껴지듯이 상태를 가지고 있는 pod들을 관리하는 용도로 사용됩니다.

pod에 순서를 지정해서 지정한 순서대로 실행되게 할 수도 있으며, 볼륨을 지정해줘서 pod을 내렸다가 올려도 데이터가 유지되게 할 수 있습니다.

실습

Static Provisioning의 경우 :
미리 1G짜리 pv를 두개 만들어줍니다.

kind: PersistentVolume
metadata:
 name: pv
spec:
 capacity:
   storage: 1Gi
 accessModes:
   - ReadWriteMany
 nfs:
   server: x.x.x.x
   path: /.../mount/dir

image

Dynamic Provisioning의 경우 :
호롤리한 하루/Kubernetes Volumes : Static & Dynamic Provisioning을 참고하여 storage class, provisioner까지 만든 후 부터 아래 실습을 진행해주세요.

StatefulSet Controller를 생성해줍니다.

$ vim statefulset-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    name: web
  clusterIP: 10.96.10.10
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx          # ==spec.template.metadata.labels
  template:
    metadata:
      labels:
        app: nginx         # ==spec.selector.matchLabels
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: web-pvc
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: web-pvc
    spec:
      storageClassName: nfs-storageclass    # Static Provisioning의 경우에는 주석처리!
      accessModes: [ "ReadWriteMany" ]      # pv의 accessmode와 일치할것
      resources:
        requests:
          storage: 1Gi

배포!

$ kubectl apply -f statefulset-nginx.yaml

image

$ kubectl get pod

image
pod이 생성될 때 0번부터 순차적으로 생성됩니다.

볼륨이 제대로 바운드되었는지 확인 :

$ kubectl get pv,pvc

image


그럼 이번엔 서비스에 접속해봅시다.

$ kubectl get svc

image

ip:port로 접속!
image
했는데 403에러가 발생합니다.

이유는 바로

$ for i in 0 1; do kubectl exec web-$i -- ls -a /usr/share/nginx/html; done

image

루트 html폴더가 비어있기 때문입니다.


각 pod에 html파일을 하나씩 구성해줍시다.

pod들의 hostname이 다르다는것을 이용해 0번노드로 접속하면 0번노드의 hostname이 뜨는 페이지가, 1번 노드로 접속하면 1번노드의 hostname이 뜨도록 페이지를 구성해보겠습니다.

$ for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
   web-0
   web-1

$ for i in 0 1; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done

이제 각 pod이 어떤 노드에 올라갔는지 확인해봅시다.

$ kubectl describe pod web

image
image

Node2:port :
image

Node1:port :
image

403에러가 나지 않고, 각 pod의 hostname이 출력되는 것을 확인할 수 있습니다.


이번엔 pod을 삭제해보고 다시 올라왔을 때, 그대로 hostname이 출력되는지(volume이 제대로 바인딩되었는지)확인해봅시다.

$ kubectl delete pod -l app=nginx

image

replication 옵션이라 조금만 기다리면 새로운 pod이 생성될 것입니다.
image

다 running상태가 되면 웹으로 접속해봐서 hostname이 그대로 나오는지 확인해보시면 됩니다.

나올수있는 에러

웹으로 접속해봤는데 전부 같은 hostname이 나오는 경우 :

Dynamic Provisioning이 아니라 Static Provisioning을 사용할 때 할수있는 실수입니다.

Static Provisioning은 provisioning을 수동으로 해줘야하기때문에 파일시스템의 마운트포인트를 다르게 해서 pv를 생성해줘야합니다.

동일한 마운트 포인트를 사용할 경우, 같은 볼륨을 공유하는것과 마찬가지입니다. 때문에 file이 overwriting되어서 같은 hostname이 보여지는 것입니다.

–>
수동으로 nfs에 폴더 두개를 만들어주고
image

pv생성할때 각각의 폴더로 마운트하게 설정을 해줍니다.

pv.yaml :
image

pv2.yaml :
image

그럼 file overwriting같은 문제가 발생하지 않게됩니다.


댓글남기기