Kubernetes DaemonSet 实战指南:原理、调度与避坑
2026/9/21 22:50:59 网站建设 项目流程

做 Kubernetes 的人,基本都绕不开 DaemonSet 这个控制器。它不像 Deployment 那样天天出现在业务发布里,也不像 StatefulSet 老被拿来讨论数据库和中间件,但只要涉及集群的基础能力建设——网络、日志、监控、安全、存储,十有八九都有它的份。这篇文章我打算把 DaemonSet 从原理到落地完整聊一遍,包含调度机制、清单写法、更新策略、资源控制、故障排查,也会结合我在企业集群里踩过的几个坑,希望帮你把它真正用顺。

先说清楚一件事:这里说的“控制器”不是硬件控制器,也不是 PID 控制器、电机驱动电路那类东西,而是 Kubernetes 控制面里的控制器(Controller)。它负责持续观察集群状态,把实际状态往期望状态拉。Deployment、StatefulSet、DaemonSet 都属于控制器,只是管理逻辑不同。DaemonSet 的核心承诺就一句话:在集群中满足条件的所有节点上,保证恰好运行一个 Pod 副本。节点新增,它会自动补 Pod;节点删除,对应 Pod 也随之清理。这个能力听起来简单,但企业里很多基础组件离开了它,运维成本会高到让你怀疑人生。

1. 先从调度说起:DaemonSet 到底凭什么做到“每个节点一个 Pod”

1.1 Deployment、StatefulSet 与 DaemonSet 的本质区别

很多初学者会困惑:Deployment 也能通过调度把 Pod 打散到不同节点,为什么还要单独搞一个 DaemonSet?

区别在于调度维度。Deployment 调度的是“副本数量”,它只保证 N 个副本分布在集群里,具体落在哪些节点由调度器决定。节点不够时 Pod 会 Pending,节点多了副本也不会因此增加。StatefulSet 则是为有状态应用设计的,强调每个 Pod 有稳定的网络标识和存储,但也不绑定“一个节点一个副本”的语义。

DaemonSet 绑定的是节点维度。它遍历集群里的节点,把每个匹配条件的节点当成一个“必须服务的位置”,然后在这个位置上放一个 Pod。这种拓扑约束天然适合三类工作负载:

  • 节点级基础设施:kube-proxy、CNI 网络插件(Calico、Cilium 的 agent)、容器运行时辅助组件,没有它们节点就是“裸奔”的。
  • 数据采集器:日志采集、监控指标采集、安全审计 Agent,需要覆盖每一个节点才能不漏数据。
  • 本地存储/运维工具:需要在每台机器上挂载磁盘、清理镜像、执行巡检任务的组件。

我见过有人用 Deployment 强行配合反亲和性来模拟 DaemonSet,比如把副本数手动设置成节点数,再给每个 Pod 加 nodeName 或者 nodeAffinity。这样看起来“也能用”,但代价很大:每次集群扩容都要手动改副本数,节点故障后 Pod 会被调度到别的地方,完全破坏了“节点本机数据必须本机处理”的假设。后来这个团队还是老老实实换成了 DaemonSet。

1.2 一次“偷懒”让我意识到它的价值

讲一个我自己的经历。之前负责一套生产集群,为了节省资源,我一开始把日志采集器部署成了 Deployment,副本数按当时的节点数填。后面集群扩容,新节点进来自动纳管,但我忘了同步更新 Deployment 的副本数,结果新节点跑了大半天都没有日志采集。等到排查问题的时候才发现,那段时间的日志全丢了。

这种问题本质上不是操作失误,而是选型错了。采集器必须跟随节点生命周期自动伸缩,这就是 DaemonSet 存在的意义。后来我把采集器改成 DaemonSet,集群加节点后 Pod 自动创建,删节点后 Pod 自动回收,再也没有出现过“节点在但 Agent 不在”的盲区。

再补充一个判断标准:如果你的应用“每台机器都要有一份,且节点不存在时这个副本也没必要存在”,那它大概率应该用 DaemonSet。如果只是“总共要 N 份,具体放哪无所谓”,那就用 Deployment。这个区分想清楚,后面很多方案就顺了。

2. 核心机制拆解:DaemonSet 控制器是怎么把 Pod 送到每个节点上的

2.1 节点筛选与调度逻辑的演进

DaemonSet 控制器本身是 kube-controller-manager 里的一个控制器。它的工作流程大致是:不断监听 Node 变化、DaemonSet 变化和 Pod 变化,然后计算“每个节点上应该有的 DaemonSet Pod”和“当前实际存在的 Pod”之间的差异,有差异就补齐或者清理。

关键点在于它怎么决定“哪些节点需要 Pod”。早期版本做法比较粗暴,DaemonSet 控制器直接把 Pod 的 spec.nodeName 写死为某个节点,然后交给 kubelet 去启动。这个方式的问题在于绕过了调度器,很多调度策略、资源过滤、亲和性规则都用不上。后来 Kubernetes 改了实现:DaemonSet 控制器会为每个匹配的节点生成一个 Pod 模板,并且在这个 Pod 上自动注入一条 nodeAffinity,要求它必须调度到指定节点。调度器仍然参与资源判断、污点容忍、优先级抢占这些流程,只是最终结果被约束在那个节点上。

对我们使用者来说,这意味着 DaemonSet 的 Pod 同样受调度器管理,所以节点资源不足、存在无法容忍的污点、标签不匹配等都会导致 Pod 无法运行。它不是“硬塞给节点”,而是“定向调度到节点”。

2.2 节点标签、污点和容忍是如何参与决策的

DaemonSet 控制器在生成 Pod 时,会先判断节点是否满足条件。默认情况下,如果你的 DaemonSet 清单里没有写 nodeSelector、nodeAffinity、tolerations,那它会在所有节点上创建 Pod,包括工作节点,也会尝试在控制平面节点上创建——前提是控制平面节点没有不可容忍的污点。

实际企业集群里,控制平面节点通常会打上污点,比如node-role.kubernetes.io/control-plane:NoSchedule。如果你想让某个 DaemonSet 也覆盖控制平面节点,必须显式加 toleration。如果不加,控制器会跳过这些节点。这里容易踩坑:你以为写了一个“所有节点”的 DaemonSet,结果主节点上没有;等检查的时候才发现控制面组件少了采集器。

节点筛选的核心参数包括:

  • nodeSelector:按节点 label 精确匹配,最简单直接。
  • tolerations:允许 Pod 调度到带污点的节点。
  • nodeAffinity:支持更灵活的标签匹配,比如InNotInExists

举个例子,如果只想让 DaemonSet 跑在带有disktype=ssd标签的节点上,可以直接写 nodeSelector。如果想跑在除 GPU 节点外的所有节点,可以用 nodeAffinity 的NotIn排除node-role.kubernetes.io/gpu=true的节点。

2.3 controller revision 与滚动更新时的版本管理

另一个容易被忽略的机制是 ControllerRevision。Kubernetes 会为 DaemonSet 的每次模板变更生成一个版本记录。你修改 DaemonSet 的 Pod 模板并执行 apply 之后,控制器会创建一个新的 ControllerRevision,然后按更新策略逐步替换 Pod。这也是为什么你可以用kubectl rollout history daemonset/<name>查看历史版本,再通过kubectl rollout undo回滚。

很多时候更新卡住,不一定是 Pod 启动失败,也可能是 ControllerRevision 数量过多、历史版本堆积导致 etcd 压力增大。企业里跑了一两年的集群,如果频繁更新 DaemonSet,建议定期清理旧的 ControllerRevision。虽然默认有revisionHistoryLimit限制(默认 10),但遇到特殊情况可以检查一下。

3. 从零写一个可落地的 DaemonSet

3.1 最小清单逐行拆解

先看一个最基础的 DaemonSet 清单,我用 node-exporter 当例子,因为它结构简单、但包含了很多企业落地必须的字段:

apiVersion: apps/v1 kind: DaemonSet metadata: name: node-exporter namespace: monitoring labels: app: node-exporter spec: selector: matchLabels: app: node-exporter template: metadata: labels: app: node-exporter spec: hostNetwork: true hostPID: true tolerations: - operator: Exists containers: - name: node-exporter image: prom/node-exporter:v1.7.0 args: - --path.procfs=/host/proc - --path.sysfs=/host/sys - --path.rootfs=/host/root ports: - name: metrics containerPort: 9100 hostPort: 9100 resources: requests: cpu: 50m memory: 50Mi limits: cpu: 250m memory: 256Mi securityContext: privileged: true volumeMounts: - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: root mountPath: /host/root readOnly: true volumes: - name: proc hostPath: path: /proc - name: sys hostPath: path: /sys - name: root hostPath: path: /

逐个说下关键字段为什么存在。

spec.selector.matchLabels必须跟template.metadata.labels一致,而且 DaemonSet 创建后 selector 是不允许修改的,要改只能删除重建。这一点比 Deployment 更严格,因为 DaemonSet 的 Pod 直接绑定节点,改 selector 会让控制器无法正确识别新旧 Pod。

hostNetwork: true表示 Pod 直接使用宿主机网络。node-exporter、kube-proxy、CNI 插件这类组件通常都需要它,因为它们要么要监听宿主机端口,要么要访问宿主机网络命名空间。

hostPID: true允许 Pod 看到宿主机上的进程。采集指标、清理进程时少不它。

tolerations: - operator: Exists表示容忍所有污点。对于采集器这类需要覆盖所有节点的 DaemonSet,我一般直接这么写。但如果你对安全要求高,不建议全家桶式容忍,最好只容忍确定的那几个污点。注意“容忍所有污点”也意味着即使节点被加了 NoExecute 污点,Pod 也不会被驱逐,这对某些运维场景是好事,但对某些安全场景需要谨慎。

hostPath挂载宿主机目录时,需要确认运行时路径。比如容器运行时是 containerd 的集群,日志目录通常在/var/log/pods,而不是/var/lib/docker/containers。不同 K8s 版本、不同容器运行时,路径会差很多。写死目录前,先上节点ls看一眼。

创建命令:

kubectl apply -f daemonset-node-exporter.yaml kubectl get ds -n monitoring kubectl get pods -n monitoring -o wide | grep node-exporter

正常情况下,每个节点都会有一个 Running 的 Pod。

3.2 控制调度范围:标签、污点与容忍、亲和性

企业里不可能所有 DaemonSet 都跑满“所有节点”。常见的受限场景有三种:

场景做法示例
只跑在指定节点池nodeSelectordisktype: ssd
跑在所有节点,但不跑 GPU 节点nodeAffinityNotIn排除gpu=true
容忍主节点污点,覆盖控制面节点tolerationskey: node-role.kubernetes.io/control-plane

nodeSelector 最简单,但无法表达“排除”和“范围”的概念。比如你想让 DaemonSet 跑在除 Windows 节点外的所有 Linux 节点,nodeSelector 做不到,你必须给所有 Linux 节点打一个标签,或者用 nodeAffinity:

affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/os operator: In values: - linux

注意填 affinity 时,DaemonSet 控制器还是会加一层自己的节点亲和性,所以实际 Pod 上会同时有控制器注入的亲和性和你写的亲和性,两者叠加,最终调度到指定节点。

容忍度的典型例子是让采集器也跑在主节点上:

tolerations: - key: node-role.kubernetes.io/control-plane effect: NoSchedule

如果你的集群有多个污点,比如node-role.kubernetes.io/master,老版本集群要单独加。新版本统一用control-plane

3.3 企业实战:日志采集与节点监控两个典型场景

日志采集是 DaemonSet 用得最多的场景之一。我常用的日志采集方案是 Filebeat 或者 Fluent Bit,它们的 DaemonSet 需要把宿主机日志目录挂载进去。核心模板大概长这样:

spec: template: spec: serviceAccountName: filebeat tolerations: - operator: Exists containers: - name: filebeat image: docker.elastic.co/beats/filebeat:8.13.4 volumeMounts: - name: varlog mountPath: /var/log - name: dockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: podlogs mountPath: /var/log/pods readOnly: true volumes: - name: varlog hostPath: path: /var/log - name: dockercontainers hostPath: path: /var/lib/docker/containers - name: podlogs hostPath: path: /var/log/pods

这里我把 docker 和 pods 两个目录都挂进去了,是因为很多集群是部分节点用 docker、部分节点用 containerd 的过渡状态。卷名如果冲突,可以分两个容器实现,但一般挂载不同路径没关系。真正需要关心的是磁盘占用:日志量大时 hostPath 目录会暴涨,建议给采集器设置日志轮转,同时在宿主机上配置 docker/containerd 的 log driver 限制。

节点监控的典型代表是 kube-prometheus 里的 node-exporter。它会把宿主机/proc/sys/挂载进容器,用 hostPID、hostNetwork 读取节点指标。这类组件的核心要求是权限和端口。使用 hostNetwork 时,9100 端口直接落在宿主机上,要注意端口冲突。如果同一节点另一个进程占用了 9100,node-exporter 会启动失败。

另外还有网络插件。Calico 的 calico-node、Cilium 的 cilium-agent,本质上都是 DaemonSet。它们需要写入宿主机网络配置、创建虚拟网卡,所以通常要特权模式,还要挂载/lib/modules/run/xtables.lock这些路径。这些组件不是普通业务 Pod,不建议随意修改它们的调度配置,否则整个节点网络都可能出问题。

4. 更新、灰度与回滚:企业里最常用的操作路径

4.1 更新策略选型:OnDelete 还是 RollingUpdate

DaemonSet 支持两种更新策略:OnDeleteRollingUpdate

OnDelete的含义是:更新模板后,控制器不会主动动任何现有 Pod,只有你手动删除某个节点的 Pod,控制器才会按新模板创建新 Pod。这种模式适合环境敏感的组件,比如你想先在几个节点上验证新版本,确认没问题后再手动滚动其他节点。

RollingUpdate是默认策略,控制器会自动滚动更新。它有两个关键参数:

updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 minReadySeconds: 30

maxUnavailable控制更新过程中最多可以有多少个 Pod 处于不可用状态。默认值是 1,表示控制器逐节点滚动,最多一个节点的采集器暂时不可用。minReadySeconds表示新 Pod 至少保持 Ready 多少秒后才继续更新下一个。对于日志采集器、监控采集器这类组件,我建议必须设置minReadySeconds,最好 30 秒以上,否则新版本启动慢、还没 Ready 就被判定失败,会引发连锁滚动。

从 Kubernetes 1.21 开始,DaemonSet 的 RollingUpdate 还支持maxSurge,但这个参数只能设置为 0 或 1。当maxSurge=1时,控制器会先在目标节点上启动一个新 Pod,等新 Pod Ready 后再删除旧 Pod,实现“先起后删”,减少节点上空窗期。代价是更新的瞬间节点上会同时存在两个 DaemonSet Pod,需要节点留有足够资源。基础组件建议保持maxSurge=0,减少资源峰值。

4.2 灰度发布实操:先让一小撮节点跑新版本

DaemonSet 不像 Deployment 支持按比例灰度。它天然是“所有匹配节点一个 Pod”,所以常见的灰度做法是利用节点标签划分批次。

我常用的流程分三步:

  1. 给灰度节点打标签,比如agent-gray=true
  2. 创建一个新的 DaemonSet,模板和正式版一致,只是镜像指向新版本,并且 nodeSelector 指向agent-gray=true
  3. 验证新 DaemonSet 在这些节点上运行正常后,直接把正式 DaemonSet 的镜像也更新为新版本,让它滚动到全量节点。

这套“双 DaemonSet 并行”的方法在企业里很实用。灰度 DaemonSet 只服务带特殊标签的节点,不会影响其他节点;正式 DaemonSet 可以等到灰度验证通过再更新。

如果节点数量大,还可以继续用标签控制批次。比如第一批打batch=1,第二批打batch=2。每验证一个批次,再把下一批节点打上标签。灰度 DaemonSet 会自动在新增节点上创建 Pod,不需要手动调整副本数。

4.3 回滚与版本查看

回滚 DaemonSet 的姿势和 Deployment 基本一致:

kubectl rollout history daemonset/node-exporter -n monitoring kubectl rollout undo daemonset/node-exporter -n monitoring kubectl rollout undo daemonset/node-exporter -n monitoring --to-revision=2

回滚后,控制器会创建新的 ControllerRevision,然后按 updateStrategy 滚动到旧版本。注意回滚不会修改节点的“期望状态”,而是把 DaemonSet 模板切回旧版本。如果你的集群已经升级到新版本,旧模板可能不兼容新节点,回滚前最好先看历史版本的镜像和参数是否还支持当前节点。另一个常见的坑是:回滚时 maxUnavailable 太小,旧版本镜像比较大,节点上同时跑着新旧两个版本的 Pod,磁盘吃紧导致滚动卡住。这种情况下可以临时调大 maxUnavailable 到 2~3,加速回滚。

5. 企业落地绕不开的细节:资源、权限、安全与网络

5.1 资源预留与优先级:别让基础组件成为压垮节点的最后一根稻草

DaemonSet 默认会跑在每个节点上,意味着它的资源请求会乘以节点数。假如你给日志采集器设置了requests: cpu: 500m,那 100 个节点就是 50 核的预留资源,这个数字相当可观。所以企业里给 DaemonSet 设置资源时一定要克制。

我通常按组件类型给参考值:

组件requests.cpurequests.memory说明
日志采集器(Filebeat/Fluent Bit)100m100Mi日志量大时再上调
节点监控(node-exporter)50m50Mi指标采集,资源消耗低
网络插件(Calico/Cilium agent)250m256Mi视节点规模调整
kube-proxy100m100Mi连接数高时需上调

limits 不要比 requests 大太多,防止某个节点日志量爆增把整机 CPU 打满。limits 设置过小也会出问题:比如 node-exporter 在/proc很大时,内存不够会被 OOM Kill。建议先观察几天的实际使用量,再按 p95 值设置。

资源紧张时,优先级类(PriorityClass)非常关键。Kubernetes 默认有两个系统级优先级:system-cluster-criticalsystem-node-critical。基础组件建议设置为system-node-critical,这样节点资源不足时,调度器会优先保证它们的运行,甚至可能驱逐低优先级 Pod。

priorityClassName: system-node-critical

5.2 网络与存储:hostNetwork、hostPort 与 hostPath

DaemonSet 的网络模式要特别小心。很多基础组件必须使用hostNetwork: true,此时 Pod 的端口直接绑定在宿主机上。优点是性能好、配置简单;缺点是端口冲突不可由 K8s 自动规避,你需要人工保证端口不重复。

如果你不想使用 hostNetwork,只想让每个节点的 DaemonSet Pod 都暴露一个宿主机端口,可以用hostPort。但这里有个坑:hostPort 与 hostNetwork 不同,前者创建的是 iptables 规则,调度时 K8s 不能完全避免同节点端口冲突,而且性能略差。一般情况下,DaemonSet 暴露端口优先考虑 hostNetwork,或者干脆通过 Service 访问指标。

存储方面,DaemonSet 最常见的卷类型是hostPath。它直接使用宿主机路径,适合读日志、挂载运行时 socket、访问设备文件等场景。但 hostPath 不适合做有状态数据持久化,比如每个节点的本地数据库,因为 Pod 被删除再重建后,数据还在原路径,但你没有自动清理机制。如果确实需要“每节点一个本地存储”,建议用 local PV + StorageClass 的延迟绑定模式,而不是直接 hostPath。

5.3 权限与多集群交付:最小化权限和模板化

DaemonSet 里的采集器、监控组件、网络插件,很多都需要访问 Kubernetes API。比如日志采集器要关联 Pod 元数据,就需要读 Pod、Node、Namespace 的权限。正确的做法是给每个 DaemonSet 创建一个独立的 ServiceAccount,绑定最小权限的 Role/ClusterRole。

一个最小 RBAC 示例:

apiVersion: v1 kind: ServiceAccount metadata: name: filebeat namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: filebeat rules: - apiGroups: [""] resources: ["pods", "namespaces", "nodes"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: filebeat roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: filebeat subjects: - kind: ServiceAccount name: filebeat namespace: kube-system

然后在 DaemonSet 的 template.spec 里写serviceAccountName: filebeat。别图省事直接给 default ServiceAccount 绑 ClusterRole,那是在给整个集群开后门。

多集群交付时,我建议把所有 DaemonSet 清单用 Kustomize 或者 Helm 管理。不同环境的差异通常只有镜像版本、资源大小、污点容忍、节点标签这几处,用模板参数化比复制粘贴 YAML 靠谱得多。特别是集群节点标签不一致时,同一套清单通过 values 覆盖 nodeSelector,能少走很多弯路。

6. 常见问题与排查技巧实录

6.1 Pod 一直 Pending,事件里报调度失败

DaemonSet Pod 如果是 Pending,第一步看事件:

kubectl describe pod <daemonset-pod> -n <namespace>

最常见的三类原因:

  • 节点标签不匹配:你写了 nodeSelector,但目标节点没有对应的 label。检查节点标签:
    kubectl get nodes --show-labels
  • 存在不可容忍的污点:比如主节点的 control-plane 污点,或者运维人员临时加的排空污点。看事件里有没有didn't match pod anti-affinity rules或者didn't tolerate taint。解决方式是加 toleration。
  • 节点资源不足:DaemonSet 在每个节点上都请求一份资源,如果某些节点资源被业务 Pod 占满,调度器会跳过。看事件里的Insufficient cpuInsufficient memory。这种情况可以调小 requests,或者给 DaemonSet 设置更高优先级,必要时让调度器驱逐低优先级 Pod。

还有一个隐藏陷阱:DaemonSet 控制器只会在“匹配条件”的节点上创建 Pod,如果节点标签在 DaemonSet 创建后才变化,新增节点不会自动创建。此时 DaemonSet 控制器会持续尝试,但状态可能表现为“期望 5,当前 4,但没报错”。用kubectl get ds -A看 DESIRED 和 CURRENT 是否一致,不一致就是控制器认为某些节点还没建 Pod。

6.2 更新卡住,滚动状态不前进

滚动更新时,kubectl rollout status daemonset/<name>一直卡住,常见原因有三个:

  • maxUnavailable 设置太小。比如只有一台节点,maxUnavailable=1 时新 Pod 还没 Ready 就把旧 Pod 删了,节点上暂时没有任何可用副本。但节点资源不够启动新 Pod,于是卡死。建议基础组件 maxUnavailable 保持 1,但 minReadySeconds 不宜太长;极端场景下可以临时调大 maxUnavailable。
  • 新镜像拉取失败。更新后的 Pod 一直 ImagePullBackOff,导致 Ready 永远等不到。需要看具体事件,确认镜像地址、tag 是否存在、私有仓库认证是否配置。
  • 旧 Pod 一直卡在 Terminating。节点上的容器被阻塞,删除不掉。可以强制删除:
    kubectl delete pod <pod-name> -n <namespace> --force --grace-period=0
    但这只是权宜之计,根本原因要上节点看 containerd/dockerd 日志。

遇到滚动卡住,先查 ControllerRevision:

kubectl get controllerrevisions -n <namespace> -l apps.kubernetes.io/controller-revision-hash=

再对比新旧 Pod 模板差异,有时候是资源字段写错了导致新 Pod 无法被调度,但表面看起来只是“没有滚动”。所以我建议更新前先用kubectl diff预览:

kubectl diff -f daemonset.yaml

6.3 镜像拉取失败与批量节点拉取性能问题

镜像问题在 DaemonSet 里会被放大。业务应用只有几十个副本,DaemonSet 是全节点跑,拉一次大镜像就是几十上百个节点同时拉。新版本发布时,镜像仓库很容易被打爆,特别是在自建 Harbor 且网络带宽有限的情况下。

几个缓解措施:

  • 更新前先在部分节点上手动拉取镜像,预热缓存:
    ctr -n k8s.io images pull registry.example.com/agent:new
  • 给镜像仓库配置限流或使用 P2P 分发方案。开源社区已经有成熟的镜像分发加速工具,企业内部部署一套非常值得。
  • 避免新老镜像体积差距过大。很多 DaemonSet 镜像体积膨胀是因为把调试工具、各种依赖都打进去了。基础组件镜像尽量精简,能显著降低滚动更新的失败率。

另外,私有镜像仓库一定要在 DaemonSet 里配置imagePullSecrets,否则新节点加入集群后,DaemonSet Pod 会因为拉不到镜像而反复 CrashLoopBackOff。而且如果你把 DaemonSet 的管理权限交给了多个团队,建议将 imagePullSecret 统一放到 kube-system 等公共命名空间,避免每个团队各自维护一遍。

6.4 一个值得养成的排查习惯

最后分享一个我自己的习惯:每次新建或修改 DaemonSet,我都不会直接在集群上改。我会先写一个临时的最小规模版本,把 nodeSelector 指向一个临时标签,确认新 Pod 在目标节点上运行正常、日志正常、端口正常,再把标签扩展到全量节点,或者直接把正式 DaemonSet 的模板更新过去。

这样做看起来多了一步,但对基础组件这种“出问题影响面极大”的负载来说,值得。DaemonSet 一上线就是全局生效,不像业务应用可以只灰度一批。你永远不希望“新版本采集器把整个集群的日志采集搞挂”这种事情发生。宁愿多花十分钟验证,也别拿生产节点当实验田。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询