ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes DaemonSet原理与生产实践:从调度逻辑到滚动更新排错

Kubernetes DaemonSet原理与生产实践:从调度逻辑到滚动更新排错 1. 一次排查引发的思考节点上缺了几个Pod问题出在哪先讲一件我前几天遇到的事。生产集群里有一个DaemonSet管理着所有节点上的日志采集组件。某个深夜监控告警提示某个节点上的日志采集停止工作我登上去一看Pod没了。再一查发现这个节点被打了污点DaemonSet默认不会调度到带污点的节点上而当初创建DaemonSet时没有配置容忍度。整个过程排查下来不到五分钟但暴露出来一个问题很多人用DaemonSet只停留在每个节点跑一个Pod这句话上真正遇到节点异常、滚动更新、调度冲突的时候还是一头雾水。这个控制器在Kubernetes里太常用了。kube-proxy、网络插件、日志采集、监控探针、GPU驱动初始化几乎全走DaemonSet部署。但它和Deployment、StatefulSet的工作方式差异很大很多从业务应用转过来的运维会对它的调度逻辑、更新策略感到陌生。这篇内容打算把DaemonSet拆开讲透它解决什么问题、控制器内部怎么工作、YAML字段该怎么写、生产环境常见故障怎么排查。不管你是刚搭完集群还在看各类初始化报错的初学者还是已经维护生产环境、遇到节点组件异常的老手都能在里头找到有用的东西。2. 为什么需要DaemonSet节点级守护进程的定位2.1 它和Deployment的本质区别先把最核心的问题说清楚Deployment部署的是无状态业务应用关注的是副本数达到期望值。你声明replicas: 3调度器去挑三个合适的节点把Pod放上去至于具体放哪三个节点取决于资源、亲和性、反亲和性你不用管。DaemonSet则完全不同。它的语义不是我要多少个副本而是我要在每一个符合条件的节点上都跑一个实例。这个符合条件的节点可以是全部节点也可以是打了某个标签的节点子集。无论是新增节点还是删除节点DaemonSet控制器都会持续监听保证节点集合和Pod集合一一对应。节点加入集群它自动补齐Pod节点被移除它把那个节点上的Pod一并回收。用生活化的类比就行Deployment像是按门店数量配店员人不够了就从总部调人具体哪个店分几个看生意量DaemonSet像是给每个门店装一个消防报警器不管这个店是总店还是加盟店只要开店就必须装一台店关了报警器跟着拆走。这个区别带来一个实际操作上的习惯变化业务应用挂了你是扩缩容、重新调度DaemonSet管理的组件挂了优先级是看它有没有覆盖到该覆盖的节点而不是副本数量稳不稳定。很多监控平台默认按Deployment的方式去检查Deployment的副本数放在DaemonSet上就会产生误判。2.2 典型使用场景从kube-proxy到GPU节点DaemonSet最常见的应用就是集群的基础设施组件。列几个我实际维护过的网络插件比如Calico的calico-node、Flannel的flanneld它们必须跑在每个节点上负责转发Pod流量、维护路由规则。没有这个组件节点上任何Pod都无法对外通信。kube-proxy负责节点上Service的负载均衡规则也是每个节点一个。日志采集Filebeat、Fluentd、Loki的promtail通过挂载宿主机目录采集/var/log和/var/lib/docker/containers下的日志。监控探针node-exporter采集节点指标配合Prometheus使用。存储客户端如iscsi、ceph-common的初始化容器确保节点具备挂载对应存储类型的能力Pod调度到节点前先装好依赖。GPU节点专用组件GPU环境下的驱动初始化、显存监控组件配合nodeSelector只调度到带有gpu标签的节点上。可能有人会问部署在DaemonSet里的组件和直接用systemd在节点上裸跑服务有什么区别区别在于DaemonSet把版本管理、健康检查、滚动更新、故障重启都纳入了Kubernetes的声明式体系。节点上的systemd服务出了问题你得逐台登录排查DaemonSet版本的组件升级直接改镜像tag滚动更新由控制器自动完成至少理论上如此。生产环境中凡是每个节点必须有、且只能有一份的东西优先考虑DaemonSet这基本是共识。3. 控制器核心机制拆解从期望状态到Pod调度3.1 节点集合的枚举逻辑DaemonSet控制器的工作循环可以概括为三步枚举节点、比对Pod、滚动补齐。控制器通过API Server watch节点列表和DaemonSet对象拿到这个DaemonSet对应的全部Pod列表。然后它过滤节点过滤规则包括节点的污点、DaemonSet的nodeSelector、节点亲和性等。过滤后的节点集合就是Pod的期望分布集合。接着把期望集合和现有Pod逐一比对发现哪个节点少了就创建Pod哪个节点多了就删除多余的Pod。注意这里有个非常关键的细节对于一个节点如果它本来不应该有对应的Pod但实际有一个Pod在那里跑控制器不会把它迁走而是把它删除。这个场景最常见于滚动更新旧版本Pod还没被清理新版本Pod已经创建同一节点上短暂出现了两个Pod或者某某节点标签被改掉了DaemonSet认为它不该再待在这里于是删除原Pod。删除是预期的但偶尔会有误删的争议下文排错部分专门展开。3.2 调度策略抢占还是等待DaemonSet创建的Pod在默认情况下会被调度器正常调度但它的调度逻辑与普通Deployment的Pod有个重要区别DaemonSet Pod创建的早期阶段会先由DaemonSetController直接指定NodeName然后再送交给kubelet创建。普通Pod则是先经过调度器选出节点再绑到节点上。这个差异需要理解一下背景。在旧版本里DaemonSet Pod甚至可以不经过调度器由控制器直接在目标节点上创建。后来为了支持节点亲和性、资源预留等高级调度特性默认行为调整为受调度器约束但控制器仍会预选候选节点。有一个著名的坑DaemonSet的Pod如果数量少于节点数检查时看到Status的Conditions或者Events提示Unschedulable别急着怀疑控制器坏了。比如Pod处于Pending查看describe输出发现FailedScheduling里写着节点资源不足这其实是调度器拒绝了它下一步要看的是节点资源而不是DaemonSet的配置。另一个实测中容易踩的点DaemonSet Pod默认不启用PriorityClass抢占。也就是说集群资源紧张时DaemonSet的Pod不会挤掉低优先级的业务Pod它只会等资源腾出来。这个设计是刻意的——基础设施组件不应该在资源紧张时暴力抢占业务但反过来说如果你的监控探针因为节点上业务Pod太多而长期Pending可能需要考虑为DaemonSet设置单独的PriorityClass或者从架构上调整资源预留。3.3 更新策略如何影响存量PodDaemonSet有两种更新策略OnDelete和RollingUpdate。默认是滚动更新控制器的行为可以这样描述它不会一次性把所有节点的Pod都换掉而是分批推进每批数量由maxUnavailable和maxSurge控制。由于DaemonSet在每个节点上只有一个Pod它的滚动更新粒度是节点维度而不是整个集群维度。maxUnavailable的默认值是1意思是最多允许一个节点上的DaemonSet Pod不可用。如果集群有100个节点默认情况下一次大概只更新一台等它ready了再继续。比Deployment的默认更新节奏慢很多这是故意的基础设施组件全挂的代价比业务应用全挂要严重得多。maxSurge默认值为0表示不允许节点上同时存在新旧两个Pod。如果把它调成1控制器会在更新某个节点时先创建新版Pod等它Running且Ready后再删除旧版Pod。好处是滚动更新期间服务不中断代价是瞬间会有一个节点上同时跑两份实例要注意资源占用能否承受。把这两个参数用好滚动更新的体验会舒服很多。但注意maxSurge生效有一个前提Pod调度器的节点资源绑定逻辑要适配且新Pod必须能被调度到同一节点。如果新版镜像启动失败或资源请求超过节点容量maxSurge会一直卡住。很多生产事故原因就是maxSurge配了1但新版Pod因为资源不足无法调度更新流程锁死旧版又删不掉。3.4 为什么滚动更新要先看ReadyDaemonSet的滚动更新有个隐性的校验环节控制器不会仅根据Pod容器创建完成就确认更新成功而必须等到Pod的Ready状态变为true并且满足minReadySeconds如果设置了后才认为这一批更新完成推进下一批。这个机制被很多人忽略。比如你更新了一个镜像tag但新版镜像在启动时崩溃Pod不断重启Ready迟迟无法变为true那么滚动更新会一直停在第一个节点。看起来Pod是Running但容器其实在CrashLoopBackOff。排查时别只盯着版本号要查Pod状态。这里补充一个实战经验DaemonSet更新卡住时先看两个东西——kubectl rollout status daemonset/xxx --timeout60s的输出以及当前Pod的Ready状态。如果Ready是false再Describe一下对应Pod看Liveness或者Readiness探针返回什么。大多数卡住的原因都是探针失败而不是控制器逻辑问题。4. YAML实战一个可上线的DaemonSet清单逐字段解析4.1 基础骨架与必填字段先给一个生产可用的文件采集DaemonSet示例后续逐行说明。apiVersion: apps/v1 kind: DaemonSet metadata: name: log-collector namespace: observability labels: app.kubernetes.io/name: log-collector spec: selector: matchLabels: app.kubernetes.io/name: log-collector template: metadata: labels: app.kubernetes.io/name: log-collector spec: tolerations: - operator: Exists hostPID: true containers: - name: collector image: fluent/fluent-bit:2.2.2 imagePullPolicy: IfNotPresent resources: requests: cpu: 100m memory: 128Mi limits: memory: 512Mi volumeMounts: - name: varlog mountPath: /var/log - name: containerlogs mountPath: /var/lib/docker/containers readOnly: true - name: config mountPath: /fluent-bit/etc/fluent-bit.conf subPath: fluent-bit.conf volumes: - name: varlog hostPath: path: /var/log - name: containerlogs hostPath: path: /var/lib/docker/containers - name: config configMap: name: fluent-bit-config updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 25% maxSurge: 0几个必填字段的含义说清楚apiVersion必须是apps/v1。早期版本里的extensions/v1beta1早被废弃了不要再写否则创建直接报错。spec.selector是必填的且一旦创建不可修改。它用来匹配Pod标签控制器靠它找这个DaemonSet管理的Pod。写错selector最直接的后果是要么Pod全成孤儿要么控制器把别的DaemonSet的Pod误删。spec.template里的labels必须和selector的matchLabels一致。很多新手在这里犯错两个地方标钯不一致创建时会被API Server直接拒绝。4.2 节点选择三件套nodeSelector、亲和性、容忍度DaemonSet的节点选择集群里常用的有几种手段往往需要组合使用。nodeSelector最简单只调度到带指定标签的节点上。例如GPU节点spec: template: spec: nodeSelector: gpu-type: nvidia-a100缺点也很明显它只能做精确匹配不支持或、非这类复杂逻辑。比如你想调度到除了Windows之外的所有Linux节点nodeSelector就做不到得用节点亲和性。节点亲和性提供更丰富的规则requiredDuringSchedulingIgnoredDuringExecution表示硬性要求preferredDuringSchedulingIgnoredDuringExecution是软性偏好。生产环境我给GPU的DaemonSet一般这样配spec: template: spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-role.kubernetes.io/gpu operator: Exists注意我只写了调度期的规则运行期不限制。意思是节点只要带这个标签就行后续标签删了Pod不会被主动驱逐。要理解忽略执行期这八个字的含义它能帮你规避不少认知偏差。容忍度解决的是污点节点的调度问题。集群master节点默认有node-role.kubernetes.io/control-plane污点如果要让DaemonSet也跑到master上采集控制面日志必须加对应容忍度。最简单的写法是operator: Exists匹配所有污点但这样风险太大更推荐明确匹配spec: template: spec: tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule这样只有这个污点的effect是NoSchedule时才容忍其他污点照样不调度。4.3 hostPath与hostNetwork什么时候必须用DaemonSet部署的大多是节点级组件访问宿主机资源是刚需。最常用的两种方式hostPath挂载宿主机目录、hostNetwork复用宿主机网络栈。hostPath适用于日志采集、配置文件管理等场景。比如上面那个采集容器/var/lib/docker/containers目录里存着所有容器的标准输出和标准错误日志不挂载的话根本拿不到。挂载时注意readOnly: true日志采集场景只需要读不需要写。只读挂载能降低误动宿主机文件的风险这个习惯建议从一开始就养成。hostNetwork则用于网络插件、kube-proxy这类需要操作宿主机网络栈的组件。一旦设置了hostNetwork: truePod不会分配独立的Pod IP直接使用节点IP和端口。注意这种情况下如果容器监听端口和节点上已有服务冲突会直接失败且Pod的ports字段更多是文档性质实际监听由容器内程序决定。我个人的建议是能用hostPath解决的就别开hostNetworkhostNetwork引入的网络冲突问题排查成本很高。如果非要用先把节点端口占用情况理清至少搞清楚这个端口是否被集群内的其他服务占用。4.4 资源预留与terminationGracePeriodSeconds节点级组件有一个共同的风险它和应用Pod共享节点资源。如果DaemonSet容器不设requests和limits它可能把节点CPU或内存打到极高水平把业务Pod挤出。反过来如果limits设得太小在高负载节点上采集组件自己先被OOM杀掉日志采集中断。这类基础设施组件的资源配比一般遵循requests保守一点limits略高于requests但要设定上限。以日志采集为例requests给100m CPU和128Mi内存limits给512Mi内存就是一个比较稳妥的起点。磁盘IO类采集组件还要注意内存limit不要设太低否则大日志模式下容易OOM。terminationGracePeriodSeconds默认30秒对业务容器通常够用但日志采集组件在终止时需要时间刷盘缓冲数据我建议调成60秒。很多采集组件在滚动更新时丢数据追根究底就是这个字段太短旧Pod还没来得及把缓冲写出去就被强制杀掉了。5. 滚动更新与回滚升级节点组件时最容易被坑的细节5.1 用一场真实的滚动更新串起全流程假设要把日志采集的fluent-bit从2.1.10升级到2.2.2。我通常不会直接改镜像tag而是先看当前状态kubectl get ds -n observability log-collector -o wide确认当前版本、节点数量、期望数量都正常后执行kubectl set image ds/log-collector collectorfluent/fluent-bit:2.2.2 -n observability然后立即观察kubectl rollout status ds/log-collector -n observability正常情况下输出会逐个节点推进每个节点从旧版本变为新版本直到全部完成。如果卡住输出会一直停在某个节点上并提示Pod未就绪。实测下来滚动更新最容易出问题的场景有三种镜像拉取失败新镜像tag在私有仓库里不存在kubelet每轮重试都会失败Pod状态一直ImagePullBackOff。这种问题卡住时rollout status完全查不出原因必须describe Pod看事件。探针失败新版本镜像启动后Readiness探针始终不通过Pod显示Running但Ready一直是0/1。原因可能是配置路径变化、端口变化或探针本身写死了旧路径。节点资源不足新版资源requests比老版高而节点剩余资源不够调度器无法把新Pod放上去maxUnavailable又卡着旧Pod不删整个更新流程锁死。对于第三种情况有一个专门的处理顺序先临时把DaemonSet的更新策略改成OnDelete手动删除有问题的节点上的旧Pod让控制器按新模板重建确认新Pod能起再把策略改回RollingUpdate。注意改策略本身不分版本但操作时务必保证当前版本与期望版本一致否则策略改回去之后可能立刻触发一轮新的更新。5.2 maxUnavailable与maxSurge的组合取舍给一个参数组合建议表方便直接对照场景maxUnavailablemaxSurge说明保守慢速更新01每个节点先起新版Podready后再删旧版资源压力最大但对服务可用性要求极高时用默认推荐25%0每个节点先删旧版再起新版一次最多25%节点不可用整体平稳资源占用低快速推进50%0允许半数节点同时不可用适合日志采集类非关键链路的组件快速更新资源极紧张10严格一次只动一个节点更新慢但最安全需要提醒的是maxUnavailable和maxSurge都是百分比时百分比的基数不是节点总数而是期望Pod总数。比如50个节点maxUnavailable: 25%算出来是12.5向下取整实际同时最多只能有12个节点Pod不可用。很多人在大规模集群上看到更新速度比预期慢就是因为对这个取整规则不清楚。5.3 回滚的正确姿势滚动更新失败后回滚操作同样基于版本记录。DaemonSet和Deployment类似每次修改pod template都会生成新的ControllerRevision。查看历史版本kubectl rollout history ds/log-collector -n observability回滚到上一个版本kubectl rollout undo ds/log-collector -n observability也可以指定要回滚的版本号用--to-revision2这种写法。要注意rollout undo本质上是把pod template改回历史版本它不保留上一次更新的临时状态。也就是说回滚后会触发一次新的滚动更新而不是瞬间切回旧Pod。如果旧版本镜像还在节点上缓存着回滚会快一点如果不在还是要重新拉取。我踩过的一个坑回滚之后忘记观察最终状态以为回到了旧版本就万事大吉。实际上旧版本如果也存在探针问题回滚一样会卡住。所以无论更新还是回滚最终都以kubectl rollout status看到全部ready为准不能只看spec里的镜像tag。6. 生产环境排错DaemonSet故障现场复盘6.1 排查链路从Pod事件到控制器日志故障排查讲究链路清晰。碰到DaemonSet相关的Pod异常我通常按这个顺序查看Pod状态kubectl get pods -n xxx -o wide确认是Pending、CrashLoopBackOff还是ImagePullBackOff。看Pod事件kubectl describe pod xxx -n xxx重点看Events部分FailedScheduling、FailedToPullImage、Liveness probe failed会直接体现。看控制器状态kubectl get ds xxx -n xxx -o yaml看status里的numberAvailable、numberUnavailable、desiredNumberScheduled是否异常。看kubelet日志如果事件里没有明确结论去对应节点上查kubelet日志通常能发现容器创建时的一些底层错误。大部分问题在前两步就能定位。但如果Pod已经创建成功但行为异常比如日志没采集到就要进容器看实际运行情况了。kubectl logs加--previous可以看到崩溃前一个实例的日志这个参数在CrashLoopBackOff场景下非常实用。6.2 常见故障为什么某个节点没有对应Pod这是使用DaemonSet时最常见的疑问之一。明明节点在线但kubectl get pods -o wide显示某个节点没有这个DaemonSet的Pod。排查思路分三步先看调度约束用kubectl get node nodeName --show-labels确认节点标签是否满足nodeSelector或节点亲和性要求用kubectl describe node nodeName看Taints确认是否有未容忍的污点。再看DaemonSet期望kubectl get ds xxx -o yaml里看status的desiredNumberScheduled和currentNumberScheduled。如果期望数量小于节点总数说明控制器认为某些节点不该调度继续回到第一步核对约束。最后看调度记录如果约束都没问题但Pod就是没创建看controller-manager组件的日志。DaemonSet控制器在controller-manager里运行它为什么跳过某个节点会在日志里体现日志位置一般是/var/log/kubernetes/kube-controller-manager.log容器化部署时则要查对应Pod日志。一个容易误解的点节点NotReady。当节点NotReady时DaemonSet控制器不会立刻在该节点上创建Pod而是等待节点恢复。这是合理的但如果节点长期NotReady有的团队会直接删掉节点对象强制重建此时DaemonSet会在新节点上创建Pod原节点上的Pod则随节点对象删除而从集群中消失。很多节点不见了但Pod还在的疑虑其实是集群操作习惯导致的并非DaemonSet控制器行为异常。6.3 一个误删场景复盘节点标签变更引发的连锁反应真实发生过这么一件事某节点因为要调整角色被加了node-role.kubernetes.io/worker标签。集群里恰好有个DaemonSet专门给带有该标签的节点初始化存储客户端。标签加上后DaemonSet控制器立刻在该节点上创建了一个Pod。后来运维觉得这个标签不需要了直接去掉没有先删除对应的DaemonSet Pod。结果控制器发现该节点不再匹配nodeSelector主动终止了那台上的Pod。节点上的存储工具链因此断掉几分钟后有任务触发了挂载失败。这件事的教训有三层。第一节点标签是调度决策的一部分改动前要考虑同集群内的所有DaemonSet是否会受影响不能只看业务应用。第二DaemonSet的Pod被删掉是控制器按预期执行不是误操作业务依赖该节点组件的必须提前确认可以容忍摘除。第三如果确实需要临时踢掉一个节点上的DaemonSet正确做法不是删Pod——控制器会立刻重建——而是先把该节点从DaemonSet的覆盖范围摘除方式包括删除标签、加污点后更新容忍度配置或者直接用kubectl edit调整selector谨慎selector修改可能需要重建控制器。6.4 更新卡住的常见原因与处理预案滚动更新卡住前面提到过三类这里把处理预案整理成一个快速对照表现象根因处理方式新Pod一直Pending事件显示FailedScheduling节点资源不足或调度约束不满足检查节点剩余资源确认是否要调大maxUnavailable或降低资源requests新Pod Running但Ready 0/1探针失败或应用初始化失败看日志确认配置路径和端口修正YAML后重新更新新Pod ImagePullBackOff镜像不存在或仓库凭据错误检查镜像tag是否拼写正确确认pullSecret是否配置新Pod反复CrashLoopBackOff应用运行期崩溃用kubectl logs --previous确认崩溃原因一般和配置有关rollout status一直卡住但Pod全部Ready控制器监听异常或API Server负载高检查controller-manager健康状态必要时重启controller-manager Pod处理预案的核心原则是先看清是调度问题、镜像问题还是应用问题再动手改。不要一上来就删Pod删掉之后Pod被控制器立刻重建问题不会消失只会制造更多Event刷屏。7. 进阶操作把DaemonSet用在更复杂的场景7.1 和Operator的配合节点组件也能声明式管理很多团队现在用Operator管理有状态应用比如数据库集群但对DaemonSet的关注相对少。实际上Operator模式完全适用于节点级组件自定义资源定义CRD描述集群每个节点需要初始化什么驱动、需要部署什么探针Operator内部再创建相应的DaemonSet对象。好处是节点的加入和退出可以由Operator统一协调不需要运维手工改污点、改标签。我见过一个比较典型的实现GPU集群的驱动初始化Operator。节点加入集群后Operator watch到节点事件判断GPU型号然后在CRD里记录该节点需要的驱动版本接着创建带nodeSelector和相应容忍度的DaemonSetPod内的初始化容器完成驱动安装。节点被移除时Operator自动清理对应的DaemonSet Pod。这样的实现比每个团队各自维护一套Shell脚本租约可观测性和异常自愈能力都强得多。如果你手上已经有Operator开发能力用一个单独的DaemonSet来承载每节点预热镜像、缓存模型文件这类定时任务型组件是很顺手的思路。Operator提供逻辑DaemonSet保证覆盖面。7.2 GPU节点上的DaemonSet调度容器的顺序问题继续谈GPU场景。现在深度学习工作负载经常需要宿主机GPU驱动准备就绪如果驱动由DaemonSet管理就要注意调度顺序。GPU节点先被打上标签然后DaemonSet开始在该节点上创建Pod。如果驱动初始化容器需要访问宿主机GPU设备Pod的resources里要声明nvidia.com/gpu这类扩展资源并且DaemonSet pod template中要包含对应的limits字段这样调度器才知道该Pod需要GPU。如果没有声明扩展资源调度器可能把驱动初始化Pod调度到一个没有GPU的节点或者验证失败。另一个实测中的点驱动初始化Pod和业务GPU Pod不能应用同一个命名空间但不建议让驱动Pod抢占GPU资源。驱动初始化容器更倾向于使用初始化容器模式先在DaemonSet Pod中跑initContainer完成驱动探测、加载随后终止主容器保持sleep这样Pod生命周期和覆盖范围都能被控制器管理节点驱动状态也具备自愈能力。7.3 规模集群下的DaemonSet运维建议集群节点数超过几百台后DaemonSet的滚动更新慢就会变得很突出。比如500个节点maxUnavailable默认1一次更新一台假设每台Pod启动到Ready需要30秒全量更新要4个多小时。这个速度在生产变更是不可接受的。处理办法有几个维度。一是适当调大maxUnavailable日志采集这类链路对中断不敏感直接给到50%甚至75%都可以。二是先灰度再全量用节点标签把集群划分出金丝雀组先把DaemonSet的nodeSelector指向金丝雀组确认稳定后再把标签扩展到全集群。三是利用minReadySeconds做细致的健康检查给足应用启动时间避免每次变更都因为启动慢导致回滚。还要提一下DaemonSet Pod数量多的时候kubectl get pods输出会非常长建议习惯性地配合标签过滤kubectl get pods -n observability -l app.kubernetes.io/namelog-collector -o wide7.4 DaemonSet与externalIPs、集群搭建场景的交集有些人在搜索k8s externalips时遇到的问题是网络插件下发的路由规则不生效流量走不到Service后端。排查到最后往往发现是DaemonSet部署的网络插件在某个节点上没正常启动导致该节点的转发规则缺失。这类问题最终的修复手段就是移除节点污点或重新触发DaemonSet Pod重建。所以这里想强调当网络层面出现单点异常时先把网络插件DaemonSet当第一嫌疑人进行排查确认它的Pod在每个节点上都已经Running且Ready再去看Service、Endpoint等上层配置。同样集群搭建时遇到master节点初始化报错或者某节点加入后一直NotReady——这类问题和DaemonSet也有强关联。新节点加入集群后kubelet尝试启动但网络插件、kube-proxy这些组件如果无法调度到新节点节点状态就会一直不健康。很多节点加入失败的排查路径最终都会落到该节点上的daemon组件是否有Pod在跑这个环节上。从这个意义上说理解DaemonSet的调度逻辑不只是会部署组件更是排查集群各类初始化问题的底层能力。建议把节点状态异常→先看节点上的daemon组件Pod状态养成默认反应能省不少脑细胞。8. 写在最后的经验DaemonSet这个控制器平时存在感不如Deployment强但它是整个集群稳定性的地基。网络、存储、监控、日志几乎每个节点级组件都和它有关。把这篇文章里的内容消化掉至少能保证你在节点异常、滚动更新卡住、Pod调度不符合预期时不再一头雾水。我个人实操中的几条经验挑重点分享一是永远不要忽视污点配置master节点、专用GPU节点、存储节点往往都有污点标签没配容忍度的DaemonSet会在这些节点上静默缺失二是更新之前先在测试集群完整走一遍滚动更新实测的坑最后基本都出在探针和资源上和控制器本身关系不大三是所有节点级组件的资源requests不要给得太高记住这些组件要和业务Pod挤同一个节点。最后再补充一个小技巧如果DaemonSet管理的组件需要读取宿主机上某个易变的路径可以考虑先在initContainer里探测一下路径是否存在路径不对就直接退出并输出明确错误信息这样排查时看Pod事件比进容器翻文件快得多。这种用initContainer做前置检查和准备的思路在DaemonSet里用起来尤其顺手因为每个节点都会执行一遍相当于天然的多节点巡检。
返回列表