bigywater.tech
Kubernetes · 48

DaemonSet:每个节点一个 Pod 的调度和更新

DaemonSet 是怎么做到“每个节点一个”的?它的 Pod 为什么能在节点有压力时还留着?给节点打标签、打污点会发生什么?更新是一次换几个?这篇在 5 节点集群里把这些问题逐个测了一遍。

13.9 秒 vs 27.2 秒
4 个节点的 DaemonSet 滚动更新,maxUnavailable=2 与 maxUnavailable=1 的总耗时
desired 4 → 3
worker 被打上 NoExecute 污点后 DaemonSet 状态里 desiredNumberScheduled 的变化
0.1 秒
给节点打上 / 去掉 nodeSelector 要求的标签后 DaemonSet Pod 出现 / 消失的延迟

每个节点一个

实测 · 本机执行输出
  DaemonSet ds 创建后 Pod 所在节点:['worker', 'worker2', 'worker3', 'worker4'](控制面有污点,没有被选中)
  status:desiredNumberScheduled=4 currentNumberScheduled=4 numberReady=4 updatedNumberScheduled=4
  某个 Pod 的 spec.nodeName=worker2;控制器加的 affinity:{"nodeAffinity": {"requiredDuringSchedulingIgnoredDuringExecution": {"nodeSelectorTerms": [{"matchFields": [{"key": "metadata.name", "operator": "In", "values": ["kx-sched-worker2"]}]}]}}}
  控制器加的 tolerations: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

创建 DaemonSet 后 4 个 worker 各有 1 个 Pod,控制面因为有污点没被选中。每个 Pod 其实由 DaemonSet 控制器“指派”:spec.affinity 里被加上 nodeAffinity required、matchFields: metadata.name In [kx-sched-worker2],再交给默认调度器放置;同时自动带上 6 个容忍——not-ready、unreachable(NoExecute)与 disk-pressure、memory-pressure、pid-pressure、unschedulable(NoSchedule),所以节点有压力或被 cordon 时它仍然留着。

跟着标签走

实测 · 本机执行输出
  DaemonSet dsel(nodeSelector k42=yes),还没有节点有这个标签:Pod 在 []
  给 worker2 打标签后 0.1 秒,Pod 出现在 ['worker2']
  再给 worker3 打标签,然后去掉 worker2 的标签:0.1 秒后 Pod 在 ['worker3'](不匹配的节点上的 Pod 被删除)

DaemonSet 带 nodeSelector: {k42: "yes"}:没有节点有标签时 0 个 Pod;给 worker2 打标签 0.1 秒后 Pod 出现;再给 worker3 打标签并去掉 worker2 的,0.1 秒后只剩 worker3,不再匹配的节点上的 Pod 被删掉。控制器一直在监听节点变化。(第一次写这个实验时 YAML 里的 yes 被解析成布尔值,DaemonSet 没创建成功,别踩同样的坑。)

污点

实测 · 本机执行输出
  worker 打上 k42=x:NoSchedule:已有的 Pod 还在:['worker', 'worker2', 'worker3', 'worker4']
  换成 NoExecute 污点:0.1 秒后 worker 上的 Pod 被驱逐,DaemonSet 没有把它重建(新建的 Pod 同样容忍不了):Pod 在 ['worker2', 'worker3', 'worker4']
    DaemonSet 状态:desired=3 current=3(控制器知道 worker 不再符合条件)
  去掉污点后 0.5 秒,worker 上又有了 Pod:['worker', 'worker2', 'worker3', 'worker4']

给 worker 打 NoSchedule 污点,已有的 Pod 不动;换成 NoExecute,0.1 秒后 worker 上的 Pod 被驱逐,DaemonSet 不再重建它,status 变成 desired=3——控制器认为 worker 已经不符合条件。去掉污点后 0.5 秒 Pod 又回来了。自带的容忍里没有自定义污点,要让 DaemonSet 上有污点的节点必须自己写容忍。

更新策略

实测 · 本机执行输出
  ru1(maxUnavailable=1):改了环境变量触发更新,(updatedNumberScheduled, numberReady, numberUnavailable) 的变化:(0, 4, 0)@0.1s → (1, 3, 1)@3.2s → (1, 4, 0)@6.3s → (2, 3, 1)@9.5s → (2, 4, 0)@12.9s → (3, 3, 1)@16.3s → (3, 4, 0)@19.9s → (4, 3, 1)@23.1s → (4, 4, 0)@27.2s;共 27.2 秒
  ru2(maxUnavailable=2):改了环境变量触发更新,(updatedNumberScheduled, numberReady, numberUnavailable) 的变化:(0, 4, 0)@0.1s → (2, 2, 2)@3.6s → (2, 3, 1)@7.1s → (2, 4, 0)@7.7s → (3, 3, 1)@10.3s → (4, 2, 2)@10.8s → (4, 3, 1)@13.6s → (4, 4, 0)@13.9s;共 13.9 秒
  OnDelete:改了模板 6 秒后 updatedNumberScheduled=0/4,Pod 没变(旧 Pod 全在:True)
  手动删掉其中一个 Pod 之后:updatedNumberScheduled=1/4(只有被删掉重建的那个换成了新模板)

RollingUpdate 一次换 maxUnavailable 个节点上的 Pod,换完且就绪才继续:maxUnavailable: 1 的 4 个节点花了 27.2 秒(updated/ready/unavailable 依次 1/3/1 → 1/4/0 → 2/3/1 …),maxUnavailable: 2 只要 13.9 秒。几百个节点的集群如果还是默认的 1,更新要很久。OnDelete 完全不自动换:改了模板 6 秒后 updated=0/4,手动删掉一个 Pod,重建出来的那个才用上新模板。

分步演示

DaemonSet
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。独立的 5 节点 kind 集群 kx-sched(1 控制面 + 4 个 worker,Kubernetes v1.37.0);DaemonSet 的 Pod 是 busybox:1.36 sleep,更新实验里就绪探针 initialDelaySeconds=3、terminationGracePeriodSeconds=3 让每批替换持续几秒;状态变化取自 0.2 秒间隔轮询 DaemonSet status;Pod 出现与消失用 0.1 秒间隔轮询;每种情形各一次(时间每次运行不同,例如 maxUnavailable=1 的另一次运行是 28.4 秒)
    • 没有验证:新增节点(节点加入集群)时 DaemonSet 的行为(kind 里不方便动态加节点);minReadySeconds 的作用;maxSurge(新版本较新的特性,先起新 Pod 再停旧 Pod);DaemonSet Pod 的 hostNetwork / hostPort 冲突;节点资源不足时 DaemonSet Pod 的抢占(系统关键优先级);revisionHistoryLimit 与回滚
    京ICP备2026067502号-1京公网安备11010502063487号