데몬셋은 쿠버네티스 클러스터 내의 모든 "노드"에 1개씩의 파드 실행을 보장하는 컨트롤러 리소스이다. 노드를 추가했을 때에도 자동으로 해당 노드에 파드가 기동된다. 아래의 용도로 주로 사용한다.
- 파드가 출력하는 로그를 노드 단위로 수집하는 로그 프로세스를 띄울 때
- 파드 리소스 사용 현황 및 노드 상태를 모니터링하는 프로세스를 띄울 때
즉, 모든 노드에서 반드시 동작해야 하는 프로세스를 데몬셋으로 올려 사용한다.
1. 데몬셋 생성
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: sample-ds
spec:
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx-container
image: nginx:1.16
ports:
- containerPort: 80
위 내용으로 데몬셋을 생성한 후, 파드를 확인해보면 각 노드에 1개씩 파드가 생성된 것을 알 수 있다.
현재 클러스터에 desktop-worker, desktop-worker2 노드가 있고, 각각의 노드에 하나씩 파드가 생성되었다.
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sample-ds-b9l7s 1/1 Running 0 3m37s 10.244.2.101 desktop-worker2 <none> <none>
sample-ds-gjbfk 1/1 Running 0 3m37s 10.244.1.93 desktop-worker <none> <none>
2. 데몬셋 업데이트 전략
Deployment 와 마찬가지로 spec.updateStrategy 필드에 지정하며, 아래 표의 전략에 해당하는 값을 지정한다.
전략 |
설명 |
비고 |
OnDelete |
매니페스트 변경 후 apply 할 때 변경사항이 적용된 파드로 교체되는것이 아니라, 파드가 삭제되고 다시 생성될 때 변경사항이 적용된다. |
- 업데이트를 적용하려면 파드를 수동으로 삭제해주어야 함 |
RollingUpdate |
매니페스트 변경 후 apply 할 때 변경사항이 적용된다. |
- 노드 단위의 rolling update 는 아니고, Deployment 와 동일하게 클러스터 단위의 rolling update 임 |
2.1. OnDelete
어떤 이유에서든, 파드가 정지되고 다시 생성되기 전까지는 업데이트되지 않는다. 따라서 임의의 시점에 파드를 업데이트 하려면, 데몬셋이 관리하는 파드를 kubectl delete pod 커맨드로 직접 삭제해야 한다.(데몬셋이 파드를 자동으로 새로 생성해줌)
위에서 생성한 데몬셋을 삭제하고, spec.updateStrategy: OnDelete 로 변경하여 다시 생성한 후, 파드 템플릿에서 nginx 버전을 1.16 -> 1.17 로 변경하여 apply 하면서 상태를 관찰해보자.
$ kubectl get daemonsets.apps -o wide --watch
# nginx:1.16 버전 확인
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
ds-ondelete 2 2 2 2 2 <none> 9m42s nginx-container nginx:1.16 app=sample-app
# 데몬셋 매니페스트 파일의 파드 템플릿에서 컨테이너 이미지를 nginx:1.17 버전으로 수정 후
# 다른 쉘 세션을 열고 변경된 파드 템플릿을 apply 한다.
# 아래처럼 UP-TO-DATE 항목이 2 -> 0으로 바뀐 것을 볼 수 있다.
# 즉, image 는 nginx:1.17 로 변경되었지만, 파드가 최신 버전이 아니라는 뜻이다.
ds-ondelete 2 2 2 2 2 <none> 10m nginx-container nginx:1.17 app=sample-app
ds-ondelete 2 2 2 0 2 <none> 10m nginx-container nginx:1.17 app=sample-app
$ kubectl get pods -l app=sample-app -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}'
# 또는 jq가 있다면
$ kubectl get pods -l app=sample-app -o json | jq '.items[].spec.containers[].image'
# nginx:1.16 버전이 유지되는지 확인
이제 nginx:1.17 버전을 적용하려면 아래 커맨드를 이용해 파드를 삭제하면, 데몬셋이 업데이트된 버전의 파드를 자동으로 생성해준다.
$ kubectl delete pod -l app=sample-app
# 파드 2개가 모두 삭제된 후, 다시 생성되어 올라온다.
# UP-TO-DATE 가 2로 변경된 것을 알 수 있다.
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
ds-ondelete 2 1 0 1 0 <none> 29m nginx-container nginx:1.17 app=sample-app
ds-ondelete 2 2 0 2 0 <none> 29m nginx-container nginx:1.17 app=sample-app
ds-ondelete 2 2 2 2 2 <none> 29m nginx-container nginx:1.17 app=sample-app
$ kubectl get pods -l app=sample-app -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}'
# nignx:1.17 확인
2.2. RollingUpdate
매니페스트 변경 후 apply 할 때 변경사항이 적용된다. rolling update 형태로 무중단 업데이트 배포가 이루어진다. 주의할 점은, 데몬셋에서는 하나의 노드에 동일한 파드를 2개 이상 생성할 수 없으므로, 동시에 추가 생성될 수 있는 최대 파드 수인 maxSurge 필드를 설정할 수 없다.
위에서 생성한 데몬셋을 삭제하고, spec.updateStrategy: RollingUpdate 로 변경하여 다시 생성한 후, 파드 템플릿에서 nginx 버전을 1.16 -> 1.17 로 변경하여 apply 하면서 상태를 관찰해보자.
$ kubectl get daemonsets.apps -o wide --watch
# nginx:1.16 버전 확인
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
ds-rollingupdate 2 2 2 2 2 <none> 1s nginx-container nginx:1.16 app=sample-app
# 데몬셋 매니페스트 파일의 파드 템플릿에서 컨테이너 이미지를 nginx:1.17 버전으로 수정 후
# 다른 쉘 세션을 열고 변경된 파드 템플릿을 apply 한다.
# 아래처럼 UP-TO-DATE 항목이 2 -> 0 -> 2으로 바뀐 것을 볼 수 있다.
# 파드 2개가 동시에 내려가지 않고, 한개씩 삭제된 후 재생성된 것을 알 수 있다.
ds-rollingupdate 2 2 2 2 2 <none> 28s nginx-container nginx:1.17 app=sample-app
ds-rollingupdate 2 2 2 0 2 <none> 28s nginx-container nginx:1.17 app=sample-app
ds-rollingupdate 2 2 1 1 1 <none> 28s nginx-container nginx:1.17 app=sample-app
ds-rollingupdate 2 2 2 1 2 <none> 29s nginx-container nginx:1.17 app=sample-app
ds-rollingupdate 2 2 1 2 1 <none> 29s nginx-container nginx:1.17 app=sample-app
ds-rollingupdate 2 2 2 2 2 <none> 30s nginx-container nginx:1.17 app=sample-app
# kubectl get pods -o wide --watch
# pod 도 watch 해보면, desktop-worker2 의 파드가 먼저 종료 -> 생성된 후, desktop-worker 의 파드가 종료 -> 생성된 것을 알 수 있다.
# AGE 필드를 보면 알겠지만, Terminating Completed 는 순서에 맞지 않게 이벤트가 발생했으므로 비동기적으로 수행되었음을 짐작할 수 있다.
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ds-rollingupdate-ql2lw 1/1 Terminating 0 28s 10.244.2.105 desktop-worker2 <none> <none>
ds-rollingupdate-ql2lw 0/1 Completed 0 28s 10.244.2.105 desktop-worker2 <none> <none>
ds-rollingupdate-kblv8 0/1 Pending 0 0s <none> <none> <none> <none>
ds-rollingupdate-kblv8 0/1 Pending 0 0s <none> desktop-worker2 <none> <none>
ds-rollingupdate-kblv8 0/1 ContainerCreating 0 0s <none> desktop-worker2 <none> <none>
ds-rollingupdate-ql2lw 0/1 Completed 0 28s 10.244.2.105 desktop-worker2 <none> <none>
ds-rollingupdate-ql2lw 0/1 Completed 0 28s 10.244.2.105 desktop-worker2 <none> <none>
ds-rollingupdate-kblv8 1/1 Running 0 1s 10.244.2.106 desktop-worker2 <none> <none>
ds-rollingupdate-9zvrh 1/1 Terminating 0 29s 10.244.1.97 desktop-worker <none> <none>
ds-rollingupdate-9zvrh 0/1 Completed 0 29s 10.244.1.97 desktop-worker <none> <none>
ds-rollingupdate-rd65j 0/1 Pending 0 0s <none> <none> <none> <none>
ds-rollingupdate-rd65j 0/1 Pending 0 0s <none> desktop-worker <none> <none>
ds-rollingupdate-rd65j 0/1 ContainerCreating 0 0s <none> desktop-worker <none> <none>
ds-rollingupdate-rd65j 1/1 Running 0 1s 10.244.1.98 desktop-worker <none> <none>
ds-rollingupdate-9zvrh 0/1 Completed 0 30s 10.244.1.97 desktop-worker <none> <none>
ds-rollingupdate-9zvrh 0/1 Completed 0 30s 10.244.1.97 desktop-worker <none> <none>
추가로 공부해야 할 점: 데몬셋이 노드당 1개의 파드를 보장해주는 것이라면, rolling update 시에도 노드 내에서 1개의 추가 파드를 생성해서 교체해야 하는 것이 맞아보이는데, 실제로 그렇게 동작하지 않는다. 노드 단위로 항시 서비스를 유지해야 하는 프로세스라면, 무중단 배포라고 볼 수 없다는 점이 의아하다.