K8s DaemonSet이란?
DaemonSet은 클러스터의 모든 노드(또는 선택된 노드)에 정확히 하나의 Pod를 실행하는 워크로드 리소스다. 노드가 추가되면 자동으로 Pod가 배치되고, 노드가 제거되면 Pod도 삭제된다. 로그 수집, 모니터링 에이전트, 네트워크 플러그인, 스토리지 데몬 같은 노드 레벨 인프라 서비스에 필수적이다.
| 워크로드 | Pod 배치 | 대표 용도 |
|---|---|---|
| Deployment | N개 레플리카, 스케줄러 결정 | 애플리케이션 서버 |
| StatefulSet | 순서 보장, 안정적 ID | 데이터베이스, 메시지 큐 |
| DaemonSet | 노드당 1개, 전 노드 배치 | 로그/메트릭 에이전트, CNI |
| Job/CronJob | 완료까지 실행 | 배치 처리, 스케줄 작업 |
기본 DaemonSet 구성
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentbit
namespace: logging
labels:
app: fluentbit
spec:
selector:
matchLabels:
app: fluentbit
template:
metadata:
labels:
app: fluentbit
spec:
serviceAccountName: fluentbit
tolerations:
# 컨트롤 플레인 노드에도 배치
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: fluentbit
image: fluent/fluent-bit:3.1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc/
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: config
configMap:
name: fluentbit-config
nodeSelector와 nodeAffinity: 선택적 배치
모든 노드가 아닌 특정 노드 그룹에만 DaemonSet을 배치할 수 있다. GPU 노드에만 GPU 드라이버를, SSD 노드에만 캐시 에이전트를 배치하는 식이다.
# nodeSelector: 간단한 레이블 매칭
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: gpu-monitor
namespace: monitoring
spec:
selector:
matchLabels:
app: gpu-monitor
template:
metadata:
labels:
app: gpu-monitor
spec:
nodeSelector:
hardware/gpu: "true" # GPU 노드에만 배치
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.0
ports:
- containerPort: 9400
---
# nodeAffinity: 복잡한 조건
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ssd-cache
namespace: infra
spec:
selector:
matchLabels:
app: ssd-cache
template:
metadata:
labels:
app: ssd-cache
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
# SSD가 있고 워커 노드인 경우만
- key: storage/ssd
operator: In
values: ["nvme", "sata"]
- key: node-role.kubernetes.io/worker
operator: Exists
containers:
- name: cache-agent
image: cache-agent:1.0
K8s Affinity 스케줄링 심화에서 다룬 nodeAffinity 패턴을 DaemonSet에 동일하게 적용할 수 있다.
Toleration: 특수 노드 접근
컨트롤 플레인, 전용 노드 등 Taint가 설정된 노드에 DaemonSet을 배치하려면 Toleration이 필요하다.
spec:
template:
spec:
tolerations:
# 컨트롤 플레인 노드 접근
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
# 전용 노드 접근 (예: monitoring=true:NoSchedule)
- key: dedicated
operator: Equal
value: monitoring
effect: NoSchedule
# 노드 NotReady 상태에서도 유지 (300초)
- key: node.kubernetes.io/not-ready
operator: Exists
effect: NoExecute
tolerationSeconds: 300
# 노드 unreachable 상태에서도 유지
- key: node.kubernetes.io/unreachable
operator: Exists
effect: NoExecute
tolerationSeconds: 300
업데이트 전략
DaemonSet은 RollingUpdate와 OnDelete 두 가지 업데이트 전략을 지원한다.
# RollingUpdate (기본): 자동 롤링 업데이트
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 동시에 업데이트되는 최대 노드 수
maxSurge: 0 # 추가 Pod 없이 교체 (1.22+)
minReadySeconds: 30 # Pod Ready 후 30초 대기
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true # 노드 네트워크 직접 사용
hostPID: true # 노드 프로세스 접근
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
ports:
- containerPort: 9100
hostPort: 9100
securityContext:
runAsNonRoot: true
runAsUser: 65534
---
# OnDelete: 수동 제어 (Pod 삭제 시에만 새 버전 생성)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cni-plugin
spec:
updateStrategy:
type: OnDelete # CNI 같은 민감한 컴포넌트는 수동 제어
# ...
hostPath와 hostNetwork 실전
DaemonSet은 노드의 파일시스템, 네트워크, 프로세스에 직접 접근하는 경우가 많다. 보안과 기능 사이의 균형이 중요하다.
# 노드 모니터링 DaemonSet: 시스템 메트릭 수집
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: system-monitor
namespace: monitoring
spec:
selector:
matchLabels:
app: system-monitor
template:
metadata:
labels:
app: system-monitor
spec:
hostNetwork: true # 노드 IP로 직접 리스닝
hostPID: true # /proc 접근
dnsPolicy: ClusterFirstWithHostNet # hostNetwork 시 DNS 설정
containers:
- name: monitor
image: system-monitor:2.0
securityContext:
privileged: false
capabilities:
add: ["SYS_PTRACE"] # 최소 권한만 부여
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: rootfs
mountPath: /host/root
readOnly: true
mountPropagation: HostToContainer
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: rootfs
hostPath:
path: /
실전 예시: Prometheus Node Exporter
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
labels:
app.kubernetes.io/name: node-exporter
spec:
selector:
matchLabels:
app.kubernetes.io/name: node-exporter
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: "10%" # 전체 노드의 10%씩 업데이트
template:
metadata:
labels:
app.kubernetes.io/name: node-exporter
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9100"
spec:
tolerations:
- operator: Exists # 모든 Taint 허용 → 전 노드 배치
hostNetwork: true
hostPID: true
securityContext:
runAsNonRoot: true
runAsUser: 65534
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
- --collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+)($|/)
ports:
- containerPort: 9100
hostPort: 9100
name: metrics
resources:
requests:
cpu: 50m
memory: 30Mi
limits:
cpu: 250m
memory: 100Mi
readinessProbe:
httpGet:
path: /
port: 9100
initialDelaySeconds: 5
livenessProbe:
httpGet:
path: /
port: 9100
initialDelaySeconds: 15
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: root
mountPath: /host/root
readOnly: true
mountPropagation: HostToContainer
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /
K8s Pod Probe 헬스체크 전략에서 다룬 Probe 패턴을 DaemonSet Pod에도 동일하게 적용하면 에이전트 상태를 안정적으로 관리할 수 있다.
리소스 관리와 Priority
DaemonSet은 인프라 서비스이므로 앱 Pod보다 높은 우선순위를 가져야 한다. PriorityClass를 설정하면 리소스 부족 시에도 DaemonSet Pod가 축출되지 않는다.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: infra-daemonset
value: 1000000
globalDefault: false
description: "DaemonSet 인프라 서비스용 높은 우선순위"
---
# DaemonSet에 적용
spec:
template:
spec:
priorityClassName: infra-daemonset
디버깅과 운영
# DaemonSet 상태 확인
kubectl get daemonset -n monitoring
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE
# node-exporter 5 5 5 5 5
# fluentbit 5 5 4 5 4 ← 1개 NotReady
# 배치되지 않은 노드 찾기
kubectl get nodes -o wide | while read node; do
kubectl get pod -n monitoring -l app=fluentbit
--field-selector spec.nodeName=$node 2>/dev/null
done
# 특정 노드의 DaemonSet Pod 로그
kubectl logs -n monitoring -l app=fluentbit
--field-selector spec.nodeName=worker-03
# 롤아웃 상태
kubectl rollout status daemonset/fluentbit -n logging
# 롤백
kubectl rollout undo daemonset/fluentbit -n logging
마무리
DaemonSet은 로그 수집, 모니터링, 네트워크 플러그인 등 노드 레벨 인프라 서비스의 표준 배포 방식이다. Toleration으로 전 노드 커버리지를 확보하고, nodeSelector/nodeAffinity로 선택적 배치를 제어하며, PriorityClass로 축출 방지까지 설정하면 안정적인 인프라 레이어가 완성된다.