[Kubernetes] 데몬셋

안녕하세요? 정리하는 개발자 워니즈 입니다. 이번시간에는 쿠버네티스 데몬셋에 대해서 알아보는 시간을 갖겠습니다.

지난 글들은 아래를 참고 해주시면 됩니다.

DaemonSet은 모든(혹은 몇몇의 지정된) Node들에 특정 Pod 한개를 유지시켜주는 Controller입니다.

동작방식은 Node들이 Cluster내에 추가될 때, DaemonSet으로 정의된 Pod가 자동으로 생성됩니다. 반대로 Node가 Cluster내에서 삭제 가 된다면, DaemonSet에 대한 Pod가 자동적으로 제거가 됩니다.

보통 이런 특성 때문에 DaemonSet은 Cluster내의 logging 및 자원 모니터링과 같은 곳에 효과적으로 사용될 수 있습니다.

쉽게 이야기해서, Node가 신규로 Cluster내에 들어올때 Monitoring과 같은 Container를 띄워서 내부 Pod들을 감시하도록 설계하는 것입니다. 모든 노드의 Pod들을 모니터링하는 역할은 반드시 필요하겠지요?

그래서 데몬셋은 Node에 무조건적으로 있어야하고 특수한 역할을 하는 Container에 대해서 적합합니다.

1. 데몬셋 스펙 작성

DaemonSet의 Spec은 앞서서의 Deployment, Pod의 스펙과 크게 다르지 않습니다.

  • ## 데몬셋 생성
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch # DaemonSet의 Name
  namespace: kube-system      # DaemonSet이 배포되는 namespace
  labels:
    k8s-app: fluentd-logging
spec:
  # DaemonSet이 관리해야할 Pod를 지정하는 Label selector입니다.
  # 아래의 Label과 Pod의 Lavel이 매칭되는 Pod가 관리대상입니다.
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      # Pod의 Lavel(위의 Lavel selector에 매칭됩니다.)
      labels:
        name: fluentd-elasticsearch
    # DaemonSet은 Pod를 관리하는 Object이므로 하위는 Pod의 Spec과 동일합니다.
    spec:
      tolerations: # tolerations에 해당되는 Node를 포함하여 스케쥴링합니다.
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: k8s.gcr.io/fluentd-elasticsearch:1.20
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

작성한 daemonset yaml파일을 쿠버네티스 클러스터에 배포합니다.

확인 결과

root@master:/lab/daemonset# kubectl get daemonset -n kube-system
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   3         3         3       3            3                     85m
root@master:/lab/daemonset# kubectl get pod -n kube-system -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP          NODE     NOMINATED NODE
fluentd-elasticsearch-b7pw7      1/1     Running   0          13m   10.38.0.1   master   
fluentd-elasticsearch-kt5cm      1/1     Running   0          13m   10.32.0.2   node2    
fluentd-elasticsearch-qfmbt      1/1     Running   0          13m   10.40.0.3   node1    

2. 특정 노트에서의 Pods 실행

데몬셋 자체는 Pod를 관리한다는 점에서 Deployment와 유사하지만, 모든 노드 혹은 특정노드에만 스케쥴링이 가능하다는 장점이 있습니다.

  • node selector
  • node affinity
  • taint & toleration

위의 3가지 개념에 대해서 정리해보도록 하겠습니다.

  1. node selector
    Selector로 지정된 label 과 Node에 지정된 label이 매칭되는 Node에만 Pod를 배포
  2. node affinity
    affinity로 지정된 Node 명과 일치된 Node명에만 Pod가 배포
  3. taint & toleration
    Taints가 지정된 Node에는 tolerations이 매칭되는 Pod만 배포할 수 있음.

위의 config를 지정하지 않으면 모든 Node에 대해서 데몬셋 컨트롤러가 동작하여 Pod를 전체 Node에 배포합니다.

nodeSelector는 nodeSelector로 지정된 Lavel로 Node에 정의된 Lavel이 매칭되는 Node를 찾아서 스케쥴링 하는 기능으로 위의 예제에서 nodeSelector를 추가하고 node1에 Lavel을 추가하여 Pod가 node1 노드에만 배포되는 과정을 실습해보겠습니다.

  1. Node에 app: dev라는 레이블을 추가합니다.
root@master:/lab/daemonset# kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   78d   v1.12.2
node1    Ready       78d   v1.12.2
node2    Ready       78d   v1.12.2


root@master:/lab/daemonset# kubectl edit node node1

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Node
metadata:
  annotations:
    kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: 2018-12-16T23:50:44Z
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/hostname: node1
    app: dev
  name: node1  <--추가
  1. DaemonSet Controller에 app:dev을 추가합니다.
root@master:/lab/daemonset# kubectl get daemonset -n kube-system
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   3         3         3       3            3                     94m
kube-proxy              3         3         3       3            3                     78d
weave-net               3         3         3       3            3                     78d

root@master:/lab/daemonset# kubectl edit daemonset fluentd-elasticsearch -n kube-system

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"DaemonSet","metadata":{"annotations":{},"labels":{"k8s-app":"fluentd-logging"},"name":"fluentd-elasticsearch","namespace":"kube-system"},"spec":{"selector":{"matchLabels":{"name":"fluentd-elasticsearch"}},"template":{"metadata":{"labels":{"name":"fluentd-elasticsearch"}},"spec":{"containers":[{"image":"k8s.gcr.io/fluentd-elasticsearch:1.20","name":"fluentd-elasticsearch","resources":{"limits":{"memory":"200Mi"},"requests":{"cpu":"100m","memory":"200Mi"}},"volumeMounts":[{"mountPath":"/var/log","name":"varlog"},{"mountPath":"/var/lib/docker/containers","name":"varlibdockercontainers","readOnly":true}]}],"terminationGracePeriodSeconds":30,"tolerations":[{"effect":"NoSchedule","key":"node-role.kubernetes.io/master"}],"volumes":[{"hostPath":{"path":"/var/log"},"name":"varlog"},{"hostPath":{"path":"/var/lib/docker/containers"},"name":"varlibdockercontainers"}]}}}}
  creationTimestamp: 2019-03-05T00:53:14Z
  generation: 1
  labels:
    k8s-app: fluentd-logging
  name: fluentd-elasticsearch
  namespace: kube-system
  resourceVersion: "3057"
  selfLink: /apis/extensions/v1beta1/namespaces/kube-system/daemonsets/fluentd-elasticsearch
  uid: 0eafcd79-3ee1-11e9-9e3c-080027b0aab8
spec:
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      creationTimestamp: null
      labels:
        name: fluentd-elasticsearch
    spec:
      nodeSelector:
        app: dev  <-- 노드 셀렉터 지정
  1. daemonset, pod를 조회해 결과를 확인해보면 Pod의 갯수가 1개로 줄어들었음을 확인 할 수 있습니다.
root@master:/lab/daemonset# kubectl get daemonset -n kube-system
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   1         1         1       0            1           app=dev         97m
kube-proxy              3         3         3       3            3                     78d
weave-net               3         3         3       3            3                     78d
root@master:/lab/daemonset# kubectl get pod -n kube-system
NAME                             READY   STATUS    RESTARTS   AGE
coredns-576cbf47c7-rdxfv         1/1     Running   0          78d
coredns-576cbf47c7-wp2x6         1/1     Running   0          78d
etcd-master                      1/1     Running   0          78d
fluentd-elasticsearch-fhnbf      1/1     Running   0          43s
kube-apiserver-master            1/1     Running   0          78d
kube-controller-manager-master   1/1     Running   0          78d
kube-proxy-8q8qn                 1/1     Running   0          78d
kube-proxy-9qpxw                 1/1     Running   0          78d
kube-proxy-ck4xq                 1/1     Running   0          78d
kube-scheduler-master            1/1     Running   0          78d
weave-net-j6p5r                  2/2     Running   0          78d
weave-net-k7b4k                  2/2     Running   0          78d
weave-net-l5wgs                  2/2     Running   0          78d
  • ## .spec.template.spec.affinity

affinity또한 nodeSelector와 마찬가지로 affinity로 선언된 Node를 매칭하는 매커니즘으로 동작하지만 nodeSelector와는 다르게 표현식을 사용해 Node의 Lavel과 매칭시킬 수 있는 특징이 있습니다.

예를 들어 아래와 같이 affinity를 정의하면 Node에 정의된 Lavel 중 kubernetes.io/hostname=node1에 매칭되는 Node에 스케쥴링이 이루어지게 됩니다. 자세한 내용은 node affinity를 참고바랍니다.

      affinity: # nodeAffinity를 추가합니다. kubernetes.io/hostname=node1
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - node1 <-- 노드명 지정
  • ## taint & toleration

toleration을 가진 Pod들만 taint를 가진 Node를 포함한 모든 노드에 스케쥴링되는 개념으로 taint & toleration로 스케쥴되는 과정은 설명하기 까다로운 부분이 있어, 예시로 대신하겠습니다.

우선 최초 위의 예제에서 Pod를 조회해보면 모든 노드에 배포되는 것을 확인할 수 있습니다.

보통 컨테이너를 배포하면 replica숫자에 맞춰서 모든 노드들에 배포가 진행이 됩니다. 마스터노드는 통상 배포가 되지 않는데, 기본적으로 컨테이너는 master node에 배포되지 않도록 설정되어 있습니다. 그 이유는 master node에 taint 가 설정되어 있기 때문입니다.

# kubectl describe node master
...
Taints:             node-role.kubernetes.io/master:NoSchedule
...

만약 master node에도 컨테이너를 배포하기 위해서는 pod에 master node에 설정된 taint에 해당되는 toleration을 추가해 주어야합니다.

즉 toleration을 가진 Pod들만 taint를 가진 Node를 포함한 모든 노드에 스케쥴링이 가능합니다.

      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule

3. 마치며...

이번시간에는 데몬셋 에 대해서 알아보는 시간을 갖었습니다. 데몬셋을 활용하면, Node가 추가 되더라도 기본적으로 수행되어야 하는 업무(로깅, 모니터링 등)에 대해서 수행하는 Pod를 띄울 수 있습니다.

또한, 그 Pod가 어떠한 특정 노드에서만 실행되게도 제어가 가능합니다. 이렇게 기본 Pod를 셋팅하고 특정 노드까지 제어해줌으로써 자동적으로 worker 노드가 Join이 되어도 기본적인 업무 수행이 가능해집니다.

다음 이시간에는 Helm에 대해서 알아보는 시간을 갖도록 하겠습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다