Learning Platform
Глоссарий Troubleshooting
Урок 06.03 · 20 мин
Средний
DaemonSetNode-level workloadsTolerationshostPathhostNetworkUpdate strategy

DaemonSet: один Pod на каждом узле

ReplicaSet и Deployment отвечают на вопрос “сколько Pod-ов мне нужно?”. DaemonSet отвечает на другой вопрос: “сколько узлов в кластере?”. Каждому Node — по своему Pod-у. Это базовый паттерн для node-level workloads: логирование, мониторинг, CNI plugins, CSI drivers — всё, что должно жить на каждом узле кластера.

DaemonSet — старый и стабильный API, но в нём есть несколько важных нюансов: tolerations, scheduler integration, и взаимодействие с node lifecycle.


systemd: units, targets и система инициализации Linux

Что такое DaemonSet

DaemonSet — это API-объект apps/v1, который гарантирует, что на каждом узле, удовлетворяющем spec.template.spec.nodeSelector / spec.template.spec.affinity, работает ровно один Pod из этого 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:
      tolerations:
        - operator: Exists
      containers:
        - name: fluentbit
          image: fluent/fluent-bit:3.0
          volumeMounts:
            - name: varlog
              mountPath: /var/log
              readOnly: true
            - name: containerlogs
              mountPath: /var/lib/docker/containers
              readOnly: true
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: containerlogs
          hostPath:
            path: /var/lib/docker/containers

Поля очень похожи на ReplicaSet/Deployment, но НЕТ поля replicas. Число Pod-ов = число подходящих узлов в кластере.

NOTE

DaemonSet существует в namespace, но Pod-ы он создаёт по одному на узел. Имена Pod-ов: <ds-name>-<random>. У каждого Pod-а в .spec.nodeName заданна нода, на которой он живёт — это назначение делается DaemonSetController-ом (или scheduler-ом — см. ниже).


Use cases

DaemonSet — фундаментальный паттерн для системных сервисов кластера.

Типичные DaemonSets в production кластере
Логированиеfluentbit, fluentd, filebeat, vector. Читают /var/log на ноде, контейнерные логи из /var/lib/docker/containers или /var/log/pods, отправляют в централизованное хранилище.
Мониторингnode-exporter экспортирует node-level metrics (CPU, memory, disk, network). datadog-agent, newrelic-infra собирают свою телеметрию. kube-state-metrics — наоборот, обычный Deployment (один на кластер).
CNI pluginscalico-node, cilium-agent, weave-net. Конфигурируют сеть Pod-ов на каждой ноде, применяют NetworkPolicy, программируют eBPF. Должны стартовать на ноде до того, как другие Pods могут получить сеть.
CSI driversnode-plugin часть CSI драйверов (ebs-csi-node, rook-ceph-csi, openebs-iscsi). Монтирует volumes на ноде. Controller-plugin — отдельный Deployment.
kube-proxyЧасто запускается как DaemonSet — поддерживает iptables/IPVS/nftables правила для Services на каждой ноде. В managed кластерах может быть alternative — kube-proxy замещён eBPF в Cilium-режиме.
GPU/Device pluginsnvidia-device-plugin, intel-gpu-device-plugin. Регистрируют ресурсы (nvidia.com/gpu) в kubelet, что позволяет другим Pods запрашивать их через resources.limits.

Общая черта всех use cases: workload физически привязан к ноде — либо потому что использует hostPath, либо hostNetwork, либо потому что должен видеть процессы/девайсы конкретного узла.


Reconcile loop: как DaemonSetController работает

1. watch DaemonSets и Nodes
2. для каждого DaemonSet:
   - найти все Nodes, удовлетворяющие nodeSelector/affinity
   - для каждой такой Node:
     - есть ли уже Pod от этого DS на этой Node?
     - если нет — создать Pod с .spec.nodeName=<node>
   - для Pods на нодах, которые перестали удовлетворять selector:
     - удалить

Scheduler integration с v1.17

Раньше DaemonSetController создавал Pod-ы с уже выставленным .spec.nodeName — обходя scheduler. Это работало, но игнорировало pod affinity, preemption, и другие scheduling features.

С Kubernetes v1.17 (feature ScheduleDaemonSetPods, GA) DaemonSet идёт через обычный scheduler:

  • Controller создаёт Pod БЕЗ nodeName, но с nodeAffinity на конкретную ноду
  • Scheduler видит Pod, прогоняет его через filtering/scoring, биндит на ноду
  • Это даёт DS преимущества: respect preemption, taint priority, resource quotas
TIP

Практическое следствие: DS-Pod может получить статус Pending если на ноде нет ресурсов. Controller не “силой” его запустит. Поэтому DS должен иметь умеренные resource requests, иначе застрянет на узлах с насыщенной нагрузкой.


Tolerations: запуск на control plane и tainted nodes

Большинство DaemonSets должны работать на всех нодах, включая control plane и любые tainted ноды.

Control plane ноды по умолчанию имеют taint:

node-role.kubernetes.io/control-plane:NoSchedule

Без подходящего toleration DaemonSet НЕ создаст Pod на этих нодах — а это критично для мониторинга и CNI.

spec:
  template:
    spec:
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule

ToleratesAllTaints: критические DaemonSets

Некоторые DaemonSets (особенно CNI plugins) должны стартовать до того как нода станет ready — иначе нода никогда не получит сеть. Для этого используют максимально широкий toleration:

tolerations:
  - operator: Exists

Это значит “толерирую любой taint с любым key, effect, value”. Такие Pods запускаются на нодах с таинтами вроде:

  • node.kubernetes.io/not-ready:NoSchedule — нода ещё не ready
  • node.kubernetes.io/unreachable:NoExecute — нода недоступна, обычные Pods выселяются
  • node.kubernetes.io/disk-pressure:NoSchedule — давление на диск
Toleration patterns для DaemonSets
МинимальныйDaemonSet, который НЕ должен работать на control plane (например, какая-то прикладная задача только для worker nodes). Tolerations не указаны — control plane taint его отгонит.
Plus control planeМониторинг, логирование — должны работать и на control plane. Toleration на node-role.kubernetes.io/control-plane:NoSchedule.
ToleratesAllTaintsCNI, CSI, критические агенты. Tolerate Exists на любой taint. Стартуют на not-ready нодах, на нодах с disk-pressure, на любой шторм.

hostPath и hostNetwork

DaemonSets часто используют host-level ресурсы: файлы и сокеты на ноде, network namespace ноды.

hostPath

volumes:
  - name: varlog
    hostPath:
      path: /var/log
      type: Directory

Это монтирует /var/log НОДЫ внутрь контейнера. Используется для:

  • Логирование: читать /var/log/pods, /var/log/containers
  • Мониторинг: читать /proc, /sys хоста для node metrics
  • Storage drivers: писать в /var/lib/kubelet/plugins/<driver> для регистрации
WARNING

hostPath — это огромный security risk: контейнер может читать/писать в файловую систему хоста. PodSecurityStandard baseline и restricted запрещают большинство hostPath. В production hostPath разрешён только для системных DaemonSets, которые ему действительно нужны.

hostNetwork

spec:
  template:
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet

Pod использует network namespace ноды напрямую. Что это даёт:

  • Видит все интерфейсы ноды (eth0, weave, calico, …)
  • Биндится на порты хоста напрямую (если контейнер слушает :9100 — это :9100 хоста)
  • Может слушать pod traffic — полезно для CNI и observability

dnsPolicy: ClusterFirstWithHostNet нужен потому что иначе Pod использовал бы resolv.conf хоста и не видел бы cluster DNS.

Примеры: kube-proxy (нужен доступ к iptables хоста), node-exporter (слушает :9100 хоста), calico-node (программирует routing хоста).


Update strategy: RollingUpdate и OnDelete

DaemonSet поддерживает две стратегии обновления.

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 0

RollingUpdate (default)

Controller обновляет Pods по одному (по умолчанию maxUnavailable: 1). Удаляет старый Pod на ноде, создаёт новый, ждёт ready, переходит к следующей ноде.

Параметры:

  • maxUnavailable (default 1) — сколько Pods могут быть не ready одновременно. Можно процент.
  • maxSurge (с v1.21 GA) — сколько ДОПОЛНИТЕЛЬНЫХ Pods создать на ноде во время обновления. Если 1 — на короткое время на ноде будет два Pod-а (старый и новый). Дефолт 0 — для DaemonSets это редкий выбор.

OnDelete

Controller НЕ обновляет Pods автоматически после изменения template. Чтобы применить обновление, нужно вручную удалить старый Pod на ноде — controller создаст новый с новым template.

Use case: critical DaemonSets, где обновление должно быть строго контролируемым. Например, CNI: автоматическое обновление CNI на ноде может убить сеть всех Pods на этой ноде — лучше делать вручную в окно maintenance.

# Изменили DS, но Pods не обновились — стратегия OnDelete
kubectl rollout status ds/calico-node
# DaemonSet "calico-node" is not currently rolling out — using OnDelete strategy

# Применить обновление на одной ноде
kubectl delete pod -n kube-system <calico-pod-on-node-1>
# Controller создаст новый с новым template

Trimming: nodeSelector и nodeAffinity

DaemonSet может запускаться не на ВСЕХ нодах, а только на подмножестве — через nodeSelector или affinity.

spec:
  template:
    spec:
      nodeSelector:
        workload-type: gpu

Это запускает Pods только на нодах с label workload-type=gpu. Когда:

  • Нода с этим label добавляется в кластер — controller создаёт Pod на ней
  • Label убирается с ноды — controller удаляет Pod с неё

Более сложная фильтрация — через nodeAffinity:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: node-role.kubernetes.io/control-plane
              operator: DoesNotExist

kubectl: команды

# Создать
kubectl apply -f ds.yaml

# Список
kubectl get ds
# NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
# fluentbit    5         5         5       5            5           <none>          1h

# Список Pods с распределением по нодам
kubectl get pods -l app=fluentbit -o wide

# Описать
kubectl describe ds/fluentbit

# Rollout (только при RollingUpdate strategy)
kubectl rollout status ds/fluentbit
kubectl rollout history ds/fluentbit
kubectl rollout undo ds/fluentbit

# Удалить (cascade — все Pods на всех нодах)
kubectl delete ds/fluentbit

В kubectl get ds колонки:

  • DESIRED — сколько нод подходят под selector (если nodeSelector пустой — это все нода)
  • CURRENT — сколько Pods создано controller-ом
  • READY — сколько из них Ready
  • UP-TO-DATE — сколько Pods с текущим template (важно во время rolling update)
  • AVAILABLE — сколько Ready + старше minReadySeconds

Проверка знанийKnowledge check
Почему CNI DaemonSets (calico-node, cilium) обычно используют максимально широкий toleration `operator: Exists`, а не просто toleration на control-plane taint?
ОтветAnswer
Потому что CNI должен стартовать ДО того, как нода станет Ready — иначе нода никогда не получит работающую сеть Pod-ов, и kubelet не сможет запускать никакие другие workloads на ней. Когда нода добавляется в кластер, у неё временно есть taints вроде `node.kubernetes.io/not-ready:NoExecute` и `node.kubernetes.io/unreachable:NoExecute`. Обычные Pods НЕ запускаются на такой ноде из-за этих taints. Если CNI Pod не толерирует эти taints — он не стартует, нода не получит сеть, не станет ready, taints не снимутся — deadlock. Поэтому CNI и подобные critical DaemonSets толерируют ВСЕ taints через `operator: Exists` — это разрешает им стартовать на не-ready нодах и инициализировать сеть.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Почему в DaemonSet нет поля spec.replicas, в отличие от Deployment и ReplicaSet?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5