K8s DaemonSet 운영 전략

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은 RollingUpdateOnDelete 두 가지 업데이트 전략을 지원한다.

# 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로 축출 방지까지 설정하면 안정적인 인프라 레이어가 완성된다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux