DaemonSet 滚动更新:节点一台接一台宕机,根因在更新策略 场景: DaemonSet 滚动更新 CNI 配置 → DaemonSet Pod 逐节点终止重建 → 节点逐台 NotReady路径: Pod → CRI → Node → 集群 逐层排查版本: K8s v1.25上篇讲了 Pod 调度不均衡问题——nodeAffinity 配置写反导致 Pod 挤在同一台节点资源利用率一边倒。这篇我们来看另一个因配置引发的连锁反应DaemonSet 滚动更新导致节点逐个宕机。更新一个 DaemonSet 的配置发布后节点一台接一台变成 NotReady——不是硬件故障不是 kubelet 挂了是 DaemonSet controller 的 RollingUpdate 策略在每个节点上执行 terminate → 等待 Ready → 再继续下一节点。当被终止的是 CNI 网络插件 Pod如 Calico节点网络瞬间断连kubelet 的 NodeStatus 上报超时node controller 直接判 NotReady。K8s 的 RollingUpdate 只保证你的 Pod 被替换——不保证你的节点还在线上。【坐标】→ 节点逐台失联现象很直接发布一个 DaemonSet 配置更新后监控面板上节点开始一台一台变红。用kubectl get nodes -w能看到 Node 状态变化的时间线——间隔大约 1-2 分钟一台完全跟 DaemonSet 滚动更新的节奏吻合。kubectl describe node node-name查看 NotReady 节点的 ConditionConditions: Type Status LastHeartbeatTime Reason Message ---- ------ ----------------- ------ ------- Ready Unknown... NodeStatusUnknown Kubelet stopped postingnodestatus.... MemoryPressure False... DiskPressure False...关键信息Kubelet stopped posting node status——kubelet 不再上报心跳。不是 kubelet 进程挂了是它无法跟 API server 通信了。这里立即引发直觉kubelet 的网络依赖什么答案是 CNI 插件Container Network Interface它通常以 DaemonSet 形式运行在每个节点上。如果 CNI Pod 被终止了节点就失去了网络能力。— 去查 DaemonSet 状态。Pod 层kubectl get pods -n kube-system -l k8s-appcalico-node -w。DaemonSetDaemonSet——在每个节点上运行一个 Pod 的控制器常用于网络插件、监控代理等基础设施组件正在逐节点更新新 Pod 全部卡在 Init 阶段。【分层】→ 逐层排查排查路径从 Pod 内日志/Events出发逐层往下到 CRI 运行时再到 Node 层面最后看集群范围。不跳过任何一层。Pod 层DaemonSet 更新序列kubectl describe daemonset calico-node -n kube-system确认更新策略。C2 — DaemonSet controller 的 RollingUpdate 逻辑DaemonSet controller 在 RollingUpdate 模式下每次选择一个节点终止其上的旧 Pod等待新 Pod 变为 Ready然后才继续下一个节点。maxUnavailable: 1意味着同一时间最多只有一个节点的 Pod 处于不可用状态。这不是并发的——是逐节点的串行操作。当新 Pod 永远无法 Ready 时controller 卡在当前节点不再继续更新其他节点。但这里的现象是节点逐个宕机——说明新 Pod 虽然没 Ready但旧 Pod 已经被终止了节点网络已经断了。kubectl logs calico-node-def34 -n kube-system看不到日志Pod 在 Init 阶段需要用crictlCRI 命令行工具用于直接与容器运行时交互比 kubectl logs 能看到已退出容器的历史日志查已退出的 Init Container。CRI 层CNI 容器启动失败先找到节点上的容器 ID# 在 NotReady 节点上执行crictlps-a|grepcalicocrictl ps -a | grep calico输出如下CONTAINER IMAGE CREATED STATE NAME a1b2c3d4e calico-node:v3.262minutes ago Exited install-cni e5f6g7h8i calico-node:v3.261minute ago Exited calico-node两个容器都 Exited。crictl logs a1b2c3d4e看 Init Container 输出2026/07/0310:22:15[INFO]Installing CNI binaries...2026/07/0310:22:16[ERROR]Failed to create CNI config: configfile/host/etc/cni/net.d/10-calico.conflist not found根因明确新版本 Calico 的 CNI 配置文件路径变了旧配置挂载没有覆盖新路径导致 CNI 安装失败。节点上没有 CNI 配置 → kubelet 无法设置 Pod 网络 → 节点失去网络能力。Node 层kubelet 心跳断连journalctl -u kubelet -f --since 10 minutes ago查看 kubelet 日志Jul 0310:22:20 node-1 kubelet[1234]: E Failed to updatenodestatus: Posthttps://api-server:6443/api/v1/nodes/node-1/status:dial tcp10.0.0.1:6443: connect: no route tohostJul 0310:22:30 node-1 kubelet[1234]: E Failed to updatenodestatus:...C2 解释kubelet 每隔 10s--node-status-update-frequency向 API server 报告一次 Node 状态。当 CNI 不可用kubelet 无法通过网络连接 API serverNodeStatus 上报连续超时。node controllercontroller-manager 内的节点管理组件负责监控节点心跳状态在--node-monitor-grace-period默认 40s内没收到上报将 Node 标记为Unknown再过--node-monitor-period默认 5s标记为NotReady。时间线时间事件T0sDaemonSet controller 终止 node-1 的 calico-node PodT5sCNI 配置丢失节点网络断连T10skubelet 首次上报失败T40snode controller 标记 node-1 为 UnknownT50snode controller 标记 node-1 为 NotReadyT60sDaemonSet controller 继续下一个节点用maxUnavailable: 1的串行滚动集群层DaemonSet controller 的 RollingUpdate回到集群视角。DaemonSet controller 的行为是保证每个节点上有一个 Pod 运行它在滚动更新时并不知道节点的网络能力依赖它的 Pod。controller 的职责边界止于 Pod 生命周期——它检查新 Pod 是否 Ready不是检查节点是否 Ready。# DaemonSet controller 的逻辑简化for each node:terminate old Pod on node create new Pod on node wait for new Pod to become Readyif timeout:stop,dont continueelse:next node这个循环里新 Pod 的 ready 检查依赖于 kubelet 的健康检查。但 kubelet 的健康检查依赖于 CNI。CNI 依赖于 DaemonSet 自己。这是一个循环依赖——DaemonSet controller 不知道也无法知道。【路径】→ 下次先查 DaemonSet 状态遇到节点逐台宕机第一反应不是查节点——是查 DaemonSet。# 1. 看哪个 DaemonSet 在滚动更新kubectl rollout status daemonset--all-nkube-system# 2. 看更新策略kubectl describe daemonsetname-nns|grep-A5UpdateStrategy# 3. 看 DaemonSet Pod 更新序列kubectl get pods-nkube-system-lk8s-appapp-w# 4. 看 Node NotReady 时间线kubectl get nodes-owide|grepNotReady# 5. 定位节点失联时间点kubectl describenodenode-name|grep-A10Conditions判断标准如果 NotReady 节点的数量和时间跟某个 DaemonSet 的 Pod 终止节奏吻合——问题在 DaemonSet不在节点。【定位】→ 最常误判❌ 大多数人会这么查“节点 NotReady → 登录节点查 kubelet 状态 → systemctl restart kubelet → 短暂恢复 → 又挂了 → 再重启 → 重复。”浪费大量时间在节点层打转重启 kubelet 只是暂时恢复了网络连接但 CNI 问题没解决网络会再次断连。✅ 正确的排查思路遇到节点逐个宕机先做两件事检查 DaemonSet 状态kubectl rollout status daemonset --all -n kube-system——看是否某个 DaemonSet 正在更新对比时间线DaemonSet Pod 终止时间 vs Node NotReady 时间如果吻合直接定位到 DaemonSet。然后如果是 CNI 插件 DaemonSet → 节点网络依赖它更新前必须评估对节点网络的影响如果是其他基础设施 DaemonSetlog agent / monitoring agent→ 影响范围较小但同样可能引发级联问题两者的差异前者从节点现象出发Node NotReady后者从集群视角出发DaemonSet 更新 时间线交叉。不出节点层永远找不到根因。【标点】→ 修复 Check-list紧急止损直接回滚 DaemonSet 到上一个版本kubectl rollout undo daemonset/calico-node-nkube-system回滚后DaemonSet controller 重新在每个节点上创建旧版本的 Pod。CNI 恢复节点网络恢复kubelet 重新上报node controller 将 Node 状态恢复为 Ready。回滚效果对比# before: 新版本 CNI 配置路径不对volumes:-name:cni-configconfigMap:name:calico-config-v2# ❌ 新路径新版本# after: 旧版本volumes:-name:cni-configconfigMap:name:calico-config# ✅ 旧路径正常工作预防配置# 方案 AmaxUnavailable: 25%先建后删比例updateStrategy:rollingUpdate:maxUnavailable:25%# 方案 BOnDelete手动控制每台节点更新updateStrategy:type:OnDelete# 方案 C加 PodDisruptionBudgetPDB - Pod 中断预算保证最少可用的 Pod 数量apiVersion:policy/v1kind:PodDisruptionBudgetmetadata:name:calico-node-pdbnamespace:kube-systemspec:minAvailable:90%selector:matchLabels:k8s-app:calico-nodemaxUnavailable: 25%限制同时不可用的 Pod 数不会整节点断网OnDelete策略要求手动删除 Pod 才触发更新适合 CNI 等关键基础设施PDB 保证至少 90% 的节点有 CNI 可用排查 Check-list故障排查的终点不是修好了——是把排查路径写成 check-list。□ 发现节点逐台 NotReady → 先查 DaemonSet kubectl rollout status daemonset--all-nkube-system □ 确认滚动更新 kubectl describe daemonsetname-nkube-system|grep-A5UpdateStrategy □ 确认 DaemonSet Pod 终止时间线 kubectl get pods-nkube-system-lk8s-appapp-w□ 确认节点 NotReady 时间线 kubectl get nodes-owide|grepNotReady kubectl describenodenode-name|grep-A10Conditions □ 回滚止损 kubectl rollout undo daemonset/name-nkube-system □ 排查新版本配置差异 kubectl describe daemonsetname-nkube-systembefore.yaml# 对比版本间 ConfigMap / 环境变量 / 卷挂载变化□ 验证回滚后节点恢复 kubectl get nodes-wkubectl get pods-nkube-system-lk8s-appapp-w附完整命令清单# 查看 DaemonSet 滚动更新状态kubectl rollout status daemonset/calico-node-nkube-system# 查看 DaemonSet 更新策略kubectl describe daemonset/calico-node-nkube-system|grep-A5UpdateStrategy# 查看 DaemonSet Pod 状态持续监听kubectl get pods-nkube-system-lk8s-appcalico-node-w# 查看节点状态kubectl get nodes-owide kubectl describenodenode-name|grep-A10Conditions# 回滚 DaemonSetkubectl rollout undo daemonset/calico-node-nkube-system# 查看 DaemonSet 历史版本kubectl rollouthistorydaemonset/calico-node-nkube-system# 回滚到指定版本kubectl rollout undo daemonset/calico-node-nkube-system --to-revisionN# 节点上查看已退出容器CRI 层crictlps-a|grepcalico crictl logscontainer-id# 查看 kubelet 日志journalctl-ukubelet-f--since30 minutes ago下篇我们聊 DaemonSet OnDelete vs RollingUpdate 选择陷阱——什么场景该用哪种策略以及为什么你的基础设施 DaemonSet 应该用 OnDelete。