4.5 DaemonSet
지금까지의 controller에게 배치의 질문은 “몇 개를 어디든"이었다. DaemonSet은 반대로 “모든 노드에 하나씩"을 묻는다. replicas 필드 자체가 없고, 대상 노드 수가 곧 Pod 수가 된다. 노드에 상주하며 그 노드 자체를 상대하는 에이전트, 즉 로그 수집기, 모니터링 에이전트, CNI 데몬, CSI 노드 플러그인이 이 모델의 사용자다. kube-proxy도 kubeadm 계열 클러스터에서는 DaemonSet으로 동작한다.
노드마다 정확히 하나
DaemonSet controller는 대상 노드마다 Pod를 하나씩 만들고, 각 Pod에 해당 노드를 가리키는 node affinity를 지정해 스케줄러에 넘긴다. 노드가 클러스터에 추가되면 그 노드 몫의 Pod가 자동으로 만들어지고, 노드가 제거되면 함께 정리된다. 노드를 100대로 늘리는 날에도 로그 수집 구성을 따로 수정할 일이 없는 이유다. 대상을 전체 노드가 아니라 부분집합으로 좁히려면 Pod template에 nodeSelector나 node affinity를 지정한다. GPU 노드에만 배치하는 전용 모니터링 에이전트가 전형적인 예다.
노드의 파일을 다루는 용도가 많다 보니 hostPath volume과 자주 결합된다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/logcontrol plane 노드는 보통 node-role.kubernetes.io/control-plane:NoSchedule taint로 일반 워크로드를 받지 않는데, 로그는 거기서도 수집해야 하므로 위처럼 toleration을 명시해 대상에 포함시킨다.
DaemonSet은 웬만한 taint를 무시한다
taint와 toleration의 일반 규칙은 Part VIII. 스케줄링과 자원 관리에서 다루지만, DaemonSet에는 특례가 있다. controller가 모든 DaemonSet Pod에 toleration 한 묶음을 자동으로 추가한다.
| 자동 toleration | effect |
|---|---|
| node.kubernetes.io/not-ready | NoExecute |
| node.kubernetes.io/unreachable | NoExecute |
| node.kubernetes.io/disk-pressure | NoSchedule |
| node.kubernetes.io/memory-pressure | NoSchedule |
| node.kubernetes.io/pid-pressure | NoSchedule |
| node.kubernetes.io/unschedulable | NoSchedule |
| node.kubernetes.io/network-unavailable (hostNetwork 시) | NoSchedule |
풀어 읽으면 이렇다. 노드가 NotReady여도 데몬은 축출되지 않고, cordon된(unschedulable) 노드에도 배치되며, 네트워크가 아직 준비되지 않은 노드에도 hostNetwork 데몬은 실행된다. 마지막 항목이 CNI 에이전트의 존재 조건이다. CNI 데몬이 기동해야 그 노드에 Pod 네트워크가 생기는데, 그 데몬 자신이 “네트워크 없음” taint에 막히면 순환에 빠진다. 일반 워크로드를 대피시키는 신호들을 데몬은 무시하도록 설계된 것이고, kubectl drain이 DaemonSet Pod를 축출 대상에서 제외하는(--ignore-daemonsets) 것도 같은 맥락이다. 노드가 비워지는 동안에도 로그와 메트릭은 수집되어야 한다.
업데이트 전략
기본은 RollingUpdate이고 보폭은 maxUnavailable(기본 1)이다. 노드가 300대라면 한 번에 한 노드씩 교체되어 완료까지 오래 걸리므로, 수용 가능한 범위에서 10% 같은 비율로 조정한다. 반대 방향의 maxSurge도 지원되는데(1.25 GA), 같은 노드에 새 Pod를 먼저 기동하고 옛것을 종료하는 방식이라 hostPort를 쓰는 데몬에는 적용하지 못한다.
OnDelete는 자동 교체를 완전히 끈다. template를 바꿔도 기존 Pod는 그대로 있고, 노드에서 Pod를 직접 삭제한 것만 새 버전으로 재생성된다. 잘못 배포되면 노드 전체의 네트워크나 스토리지가 마비되는 CNI, CSI 계열 데몬을 노드 몇 대에서 먼저 검증하고 점진적으로 확대 적용하는 통제 수단으로 쓰인다.