[Kubernetes] 데몬셋
안녕하세요? 정리하는 개발자 워니즈 입니다. 이번시간에는 쿠버네티스 데몬셋
에 대해서 알아보는 시간을 갖겠습니다.
지난 글들은 아래를 참고 해주시면 됩니다.
- 쿠버네티스 1편 : 설치 가이드
- 쿠버네티스 2편 : pod
- 쿠버네티스 3편 : service
- 쿠버네티스 4편 : deployment
- 쿠버네티스 5편 : pod 설정
- 쿠버네티스 6편 : 배포 전략
- 쿠버네티스 7편 : volume
- 쿠버네티스 8편 : daemonset
- 쿠버네티스 9편 : 테라폼을 통한 클러스터 구성
- 쿠버네티스 10편 : eks에서 volume 사용하기
- 쿠버네티스 11편 : helm
- 쿠버네티스 12편 : helm chart template
- 쿠버네티스 13편 : helm deploy
- 쿠버네티스 14편 : fluentd를 통한 log수집
- 쿠버네티스 15편 : chartmuseum
- 쿠버네티스 16편 : 배포툴(ArgoCD 설치방법/사용법)
- 쿠버네티스 17편 : 배포툴(ArgoCD 구성/알람)
- 쿠버네티스 18편 : 쿠버네티스 Autoscailing
- 쿠버네티스 19편 : 쿠버네티스 로깅 아키텍처
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가지 개념에 대해서 정리해보도록 하겠습니다.
- node selector
Selector로 지정된 label 과 Node에 지정된 label이 매칭되는 Node에만 Pod를 배포 - node affinity
affinity로 지정된 Node 명과 일치된 Node명에만 Pod가 배포 - taint & toleration
Taints가 지정된 Node에는 tolerations이 매칭되는 Pod만 배포할 수 있음.
위의 config를 지정하지 않으면 모든 Node에 대해서 데몬셋 컨트롤러가 동작하여 Pod를 전체 Node에 배포합니다.
nodeSelector는 nodeSelector로 지정된 Lavel로 Node에 정의된 Lavel이 매칭되는 Node를 찾아서 스케쥴링 하는 기능으로 위의 예제에서 nodeSelector를 추가하고 node1에 Lavel을 추가하여 Pod가 node1 노드에만 배포되는 과정을 실습해보겠습니다.
- 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 <--추가
- 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 <-- 노드 셀렉터 지정
- 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
에 대해서 알아보는 시간을 갖도록 하겠습니다.