news 2026/11/1 5:56:31

Kubernetes调度门禁:RDMA资源消失与CSI挂载失败的共同根因

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes调度门禁:RDMA资源消失与CSI挂载失败的共同根因

RDMA资源说没就没,这种事我一年里能碰上两三次。一开始总怀疑网卡、驱动、固件哪个环节出了问题,后来定位定位着才发现,根子往往不在硬件,而在Kubernetes的调度门禁上——节点被打上了污点,而设备插件、CSI存储组件这些平台服务没有对应的容忍度,调度器直接把它们全拦在了门外。节点上的RDMA资源看起来是"消失"了,CSI挂载也跟着失败,实际上它们踩的是同一个坑。

这里涉及的三个技术点都不算新:RDMA是高性能网络里绕开内核协议栈、靠网卡卸载实现低延迟通信的路线,DPDK、XDP是它旁边的兄弟方案;Kubernetes里的RDMA资源需要靠Device Plugin以Extended Resource的形式上报;而存储插件CSI要拿到节点上的RDMA设备,又通常部署成DaemonSet。这篇文章就从一次真实排障出发,把"RDMA资源消失"和"CSI挂载失败"之间那条隐藏的调度链路拆开讲清楚,包括排查命令、修复方式和生产环境里的避坑经验。适合正在维护GPU/RDMA节点池或者跟CSI存储问题死磕的工程师。

1. 现象还原:节点上的RDMA资源怎么就没了

1.1 第一现场:Allocatable里的RDMA条目消失

事情开始于一次例行的集群巡检。我习惯性地用kubectl describe node查看一批RDMA节点的资源,结果发现某个节点的资源表少了一行。正常情况下,节点上应该同时显示CPU、内存、GPU数量和RDMA设备数量,大致长这样:

Capacity: cpu: 96 memory: 786396Mi nvidia.com/gpu: 8 rdma/hca: 8 Allocatable: cpu: 96 memory: 786396Mi nvidia.com/gpu: 8 rdma/hca: 8

但现场只有GPU,没有rdma/hca。第一反应是网卡掉线了。跑到服务器上敲ibstat、ibv_devinfo,硬件状态都是健康,再用rdma link show看网卡链路,也是ACTIVE状态。硬件和驱动都没问题,集群却认为节点没有RDMA能力,这个矛盾很关键。

接着去看设备插件的运行状态,一下就发现了缺口:RDMA设备插件的DaemonSet在该节点上的Pod是Pending,始终没有调度上去。Kubernetes里的扩展资源和内置资源不一样,节点上有没有rdma/hca,完全取决于设备插件有没有在节点上正常运行并向kubelet上报。插件没跑起来,节点资源表里对应条目自然消失。到这里,问题的性质已经从硬件故障变成了调度问题。

1.2 连带故障:CSI卷挂载开始卡死

如果故障只停留在RDMA资源消失,影响范围还算可控,最多是那些声明了resources.limits["rdma/hca"]的业务Pod无法调度。但这次真正引爆问题的,是一批与RDMA无关的Pod开始报存储挂载失败。

节点上大量Pod的事件里反复出现FailedMount、MountVolume.MountDevice failed、volume attachment is being deleted这类字样,kubelet日志里也能看到调用CSI的NodePublishVolume超时记录。排查后发现,存储后端的CSI插件同样部署成了DaemonSet,它需要在每个节点上都运行一个Node组件,才能在节点本机执行挂载动作。而在这个故障节点上,CSI Node组件也是Pending状态。

两条故障线同时出现,表面看一个属于RDMA资源,一个属于存储插件,八竿子打不着。但只要看一眼调度器的事件,就会明白两者踩中同一个机制:节点上有污点没有被匹配掉,而这两个平台组件的Pod模板里都没有对应容忍度,调度器不允许它们落在节点上。资源消失、挂载失败,都是这个根因的两个出口。

1.3 我第一次踩这个坑时走的弯路

第一次遇到同类问题时,我排查得相当狼狈。先怀疑驱动,重装了rdma-core,又把网卡固件刷了一版,故障纹丝不动。接着去看设备插件日志,发现根本没有日志可看——Pod压根没被创建,自然没有进程在跑。正当我准备去存储厂商那边对接CSI问题的时候,运维同事发来一条变更记录,说前一天晚上为了给这批节点做维护,执行了一条命令:

kubectl taint nodes <node-name> dedicated=rdma:NoSchedule

这条记录当时就把整条逻辑串通了。运维的本意只是"临时维护,让新Pod别来",但没意识到设备插件和CSI组件这些平台服务同样会被挡在外面。这个坑我踩过一次之后,后来再看到任何DaemonSet长期Pending,都会先看节点污点,再看容忍度,这个习惯帮我省了很多时间。

2. 根因拆解:污点、容忍、扩展资源的三角关系

2.1 30秒复习污点与容忍的调度语义

Taint是打在节点上的标记,格式是key=value:effect,effect有三种:

  • NoSchedule:新的Pod不能调度上来,已经运行的Pod不受影响。
  • NoExecute:不仅新Pod进不来,已经在节点上的不匹配Pod也会被驱逐。Pod可以通过tolerationSeconds延期,但到期后仍会被清理。
  • PreferNoSchedule:软约束,调度器尽量不往这个节点放Pod,但不绝对禁止。

Toleration是加在Pod上的豁免声明。调度器看到节点有污点,会回头检查Pod是否声明了能匹配的容忍:要么精确匹配污点的key、value、effect,要么用operator: Exists放宽匹配条件。生活化一点,污点就是园区门口挂着"施工区域谢绝入内"的牌子,容忍就是施工人员手里的门禁卡。没有门禁卡的人,保安绝不会放你进去。

关键在这里:容忍度不是Pod的默认属性。绝大多数Pod模板里如果不写tolerations,那就是完全没有豁免权,节点上任何污点都会把它挡在外面。这个规则对DaemonSet同样生效。设备插件、CSI Node组件、日志采集、监控采集这类面向全节点部署的平台组件,最容易在这个机制上翻车。

2.2 RDMA扩展资源的生命周期绑定设备插件

CPU和内存是Kubernetes的内置资源,节点天然具备,调度器不用问就知道。RDMA网卡这类硬件则完全不同,调度器默认不认识。要让调度器理解"这台机器上有8张RDMA网卡",必须通过Device Plugin机制:节点上的kubelet向设备插件实例发起ListAndWatch调用,插件把设备列表上报,kubelet再把结果写进节点的Capacity和Allocatable。集群里常见的扩展资源名包括nvidia.com/gpu、rdma/hca等。

这意味着一个很容易被忽略的事实:节点有没有RDMA资源,不是静态硬件状态,而是"设备插件有没有在该节点正常运行"的动态结果。插件没跑起来,节点在调度器眼里就等同于没有这个设备。应用声明要2个rdma/hca,节点资源表里没这个条目,那这个节点根本进不了候选列表。这就是RDMA资源"消失"的底层机制。

设备插件本身通常以DaemonSet方式部署,这就回到了2.1的规则里。节点一打上污点,设备插件Pod先阵亡,节点资源表里RDMA条目随即消失,上层所有依赖RDMA资源的业务开始连锁反应。如果不理解这层绑定关系,很容易在硬件排查上浪费好几个小时。

2.3 CSI节点组件为何也躲不开污点

CSI的架构很清晰:Controller组件负责卷的创建、绑定、挂接等控制面操作,Node组件负责在节点本机执行挂载。为了让集群里所有节点都能挂卷,Node组件基本都以DaemonSet方式发布,它的存在假设就是"每个节点上都有我"。

但这个假设在污点面前会直接破碎。节点被标记污点后,如果CSI Node组件没有对应容忍度,调度器就不会把它放到这个节点上。后续只要有Pod需要在这个节点挂载存储卷,kubelet就找不到本机的CSI插件去执行NodePublishVolume,报错信息就是挂载超时。注意,这里并不要求业务Pod本身使用RDMA——只要节点上的存储数据面依赖RDMA通道,或者单纯因为这个节点还需要CSI插件来挂卷,故障都会发生。

我碰到的实际情况里,很多业务Pod的PVC一直报FailedMount,但RDMA设备、驱动、网络地址全部正常,最后查下来就是CSI Node组件的DaemonSet没有调度上来。如果连CSI Controller也带有资源亲和性,同样会卡在Pending状态。这类问题如果不懂调度机制,很容易在存储协议和网络层反复排查,完全走偏。

2.4 一条完整的故障链

把所有环节串起来,这次故障的完整因果链是这样的:

  1. 运维为了节点维护,给节点打上dedicated=rdma:NoSchedule污点。
  2. RDMA设备插件和CSI Node组件都没有对应容忍度,全部无法调度,Pod卡在Pending。
  3. 设备插件不运行,节点Allocatable里的rdma/hca消失。
  4. 依赖RDMA资源的新Pod无法再调度到该节点,或者尝试申请资源时报不足。
  5. 节点上已存在的Pod如果需要挂载卷,因为CSI Node组件不可用而挂载失败,报FailedMount。
  6. 如果污点用的是NoExecute,节点上已有的不匹配Pod还会被驱逐,故障范围进一步扩大。

这条链里每一环都有可能单独断开。设备插件有容忍而CSI没有,RDMA资源还在,但存储挂载照样断;反过来,只有CSI有容忍而设备插件没有,资源照样消失。所以排查这种故障,绝不能只盯着某一个组件,必须把节点上的所有平台组件都过一遍。

3. 定位链路:一份可以直接照做的排查清单

3.1 确认"资源消失":是硬件故障还是插件没上报

拿到"RDMA资源消失"的反馈,先做两个操作,把节点资源状态完整拉出来:

kubectl describe node <node-name> | head -n 40 kubectl get node <node-name> -o json | jq '.status.capacity, .status.allocatable'

重点看rdma/hca或类似的扩展资源是否还在。如果不在了,立刻转去看设备插件:

kubectl get pods -n kube-system -o wide | grep rdma kubectl describe pod -n kube-system <rdma-device-plugin-pod> | tail -n 30

如果设备插件Pod是Pending,Events里通常会直接出现had untolerated taint字样。这个信号是整个排查链路里最重要的分水岭:一旦看到taint相关描述,就不用再去底层翻驱动了,直接进入调度问题排查阶段。顺手也要确认硬件状态,避免被其他因素带偏,RDMA网卡的实操工具两个够用:ibstat和rdma link show。

3.2 从挂载失败反查CSI组件调度

另一个方向是从挂载失败反查。找到挂载失败的业务Pod,看事件:

kubectl describe pod <business-pod> -n <namespace> | grep -A 20 Events

然后看目标节点上CSI Node组件的状态:

kubectl get pods -n <storage-namespace> -o wide | grep csi-node

如果CSI Node组件没有调度到目标节点,Pod列表里该节点对应的行会一直显示Pending,或者干脆不存在。再进一步确认kubelet日志里的挂载动作超时:

journalctl -u kubelet -f | grep MountVolume journalctl -u kubelet -f | grep NodePublishVolume

我建议不要跳过这个反查动作。它能帮你快速判断故障是单一调度问题,还是多组件故障并发。生产环境里RDMA设备插件和CSI组件同时挂是常见情况,只看一边容易得出"另一个组件也刚好坏了"的错误结论。两边同时确认,才能直接锁定"缺容忍"这个根因。

3.3 核对节点污点与DaemonSet容忍

查完了组件状态,接下来就是核对机制本身。

# 直接看节点污点 kubectl get node <node-name> -o json | jq '.spec.taints' kubectl describe node <node-name> | grep -A 10 Taints # 检查DaemonSet模板里的容忍配置 kubectl get daemonset rdma-device-plugin -n kube-system -o yaml | grep -A 20 tolerations kubectl get daemonset csi-node -n <storage-namespace> -o yaml | grep -A 20 tolerations

节点上有污点而组件的tolerations字段为空,基本就可以定案。要注意,很多平台组件在Helm Chart或Operator部署时会有默认容忍,但只会覆盖常用污点,比如node-role.kubernetes.io/control-plane、node.kubernetes.io/not-ready。自定义污点比如dedicated=rdma,通常不在默认列表里,这正是盲区所在。

3.4 最小修复三步走

如果污点只是临时维护用的,最干净的做法是把污点移除:

kubectl taint nodes <node-name> dedicated=rdma:NoSchedule-

注意:命令末尾的减号不是笔误,它表示删除该污点,而不是添加。移除后调度器会自动把Pending的DaemonSet Pod调度上来,节点资源也会逐步回归。

如果污点是长期规划的一部分,比如这批节点就是专供RDMA业务使用的专用节点池,那就需要给所有必须跑在上面的平台组件补上容忍度。以DaemonSet为例,在模板中增加:

tolerations: - key: "dedicated" operator: "Equal" value: "rdma" effect: "NoSchedule"

改完后执行滚动更新并确认状态:

kubectl rollout restart daemonset rdma-device-plugin -n kube-system kubectl rollout restart daemonset csi-node -n <storage-namespace> kubectl rollout status daemonset rdma-device-plugin -n kube-system kubectl rollout status daemonset csi-node -n <storage-namespace>

验证顺序建议是:先看节点Capacity里的rdma/hca是否回归,再看CSI Node Pod是否Running,最后验证业务PVC是否挂载成功。不要跳过前两步直接刷业务Pod状态,否则一旦恢复失败,排查又得从头再来。

4. 生产实践:我踩过的坑和沉淀下来的避坑清单

4.1 一张速查表对应常见现象与根因

现象直接原因排查看点
节点Allocatable里RDMA资源消失设备插件Pod未调度或未上报kubectl describe node+ RDMA设备插件Pod状态
CSI卷一直FailedMountCSI Node组件未运行在目标节点业务Pod的Events + csi-node Pod列表
DaemonSet Pod长期Pending节点污点与Pod容忍不匹配事件里的had untolerated taint
节点Ready但业务Pod无法调度节点有自定义污点或资源不足.spec.taints+.status.allocatable
故障集中在某一批特定节点专用节点池污点被平台组件漏配对比同池节点污点与DaemonSet容忍
业务能跑但RDMA通信异常设备没有真正映射给容器设备插件分配日志、容器设备挂载

这张表的用法是帮助你快速把"看起来不相关的故障"映射到同一类机制上。先做方向判断,再进具体命令验证,比直接从底层日志往上翻效率高很多。

4.2 平台组件容忍配置的三条经验

第一,全节点型平台组件建议默认带上对通用运维污点的容忍,但不要全容忍。operator: Exists不加key的写法会把节点上所有污点都认了,如果某个节点被打了一个含义完全不同的污点,组件也可能跑上去,反而造成资源抢占甚至安全隐患。尽量按key和effect精确匹配。

第二,不要把调度设计全押在污点上。生产环境里我习惯用标签、污点、亲和性三件套组合:标签表达节点身份,比如rdma=true;污点表达"默认不要来";亲和性表达"哪些组件需要来"。平台组件可以在tolerations里精确放行dedicated=rdma,同时用nodeSelector或nodeAffinity进一步收敛目标节点,既灵活又安全。

第三,控制面节点的污点不要随意容忍。node-role.kubernetes.io/control-plane这类污点如果被宽松的operator: Exists覆盖,资源型组件可能被调度到控制面节点,挤占管理面资源。更稳妥的做法是给不同组件单独配置,而不是图省事一刀切。

4.3 打污点之前先做一次组件覆盖检查

我现在做任何节点污点变更前,都会先跑一遍全平台组件检查,确认哪些DaemonSet没有对应容忍。

for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do kubectl get daemonset -n $ns -o json 2>/dev/null | \ jq -r '.items[] | [.metadata.namespace, .metadata.name, (.spec.template.spec.tolerations // [])] | @tsv' done

这个脚本很粗糙,但能在一分钟内把"哪个DaemonSet没有容忍"列出来,再手动核对是否需要补。核心是养成习惯:任何平台变更,先评估调度影响面。我有一次就是因为跳过这一步,给RDMA节点打污点后设备插件和CSI组件同时Pending,该节点上几十个业务Pod的存储挂载全部报错,恢复花了近两个小时。如果当时提前三分钟跑一次覆盖检查,这个事故完全可以避免。

4.4 沉淀到平台组件发版清单里的调度体检项

经过几次折腾,我把平台组件的调度健康检查沉淀成了固定清单,每次发版前都会过一遍:

  • 该组件是否需要在所有节点上运行?如果是,默认的容忍覆盖是否齐全?
  • 节点的污点集合有哪些?组件模板里是否显式声明了对应容忍?
  • 组件是否与某个扩展资源强绑定?设备插件挂了,上层组件能否独立存活?
  • 组件之间是否存在调度依赖?比如CSI Node组件是否依赖设备插件先运行?
  • 监控告警是否覆盖了"DaemonSet期望副本数与实际Running数不匹配"这个指标?

这套检查项后来帮我避掉了至少三起同类事故。Kubernetes的调度机制本身并不算难,难的是把它纳入日常变更管理的视野。RDMA、CSI这些技术名词看起来各不相同,但在污点与容忍这道调度关卡下,它们共用同一套规则。

最后说一句实在话:RDMA资源消失这类故障,放到Kubernetes的机制里看,往往不是资源真的丢了,而是承载资源的平台组件进不去。下次再遇到CSI挂载失败,先别看存储配置,先看节点污点,再数一下设备插件和CSI组件的Pod数量,大概率能省下半天时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 2:18:13

嘉立创OPS接口板从设计到手工焊接全流程实操指南

最近在嘉立创做了一块带 80pin OPS 接口的板子。拆开静电袋的那一刻&#xff0c;和画板时的状态完全不同&#xff1a;画板阶段担心的是 DRC 过不过、阻抗算得对不对、封装会不会偏&#xff1b;拿到实物后&#xff0c;第一眼看的是焊盘颜色、丝印方向、板厚和连接器位置。整个过…

作者头像 李华
网站建设 2026/10/28 9:28:00

AF405-NHS荧光标记全攻略:原理、实操与避坑指南

前几天实验室里有个做流式的师弟跑过来&#xff0c;屏幕上是他的补偿矩阵&#xff0c;450/50 这个通道的阳性群怎么都推不开&#xff0c;背景还压不下来。我看了眼他那管抗体的制备记录&#xff0c;几乎立刻就知道问题出在哪儿&#xff1a;染料母液用 DMSO 溶解后在抽屉里放了三…

作者头像 李华
网站建设 2026/10/31 19:08:37

ESP32选型避坑指南:从SoC芯片到模组开发板的完整拆解

1. 从一颗芯片到一张订单&#xff1a;为什么“ESP32”这三个字最容易让人踩坑如果你在电子圈里待过一阵子&#xff0c;一定见过这样的场景&#xff1a;某位朋友兴冲冲地说“我要用ESP32做个项目”&#xff0c;然后打开购物网站&#xff0c;搜出来一堆长得差不多、名字却五花八门…

作者头像 李华
网站建设 2026/10/28 14:11:38

JavaEE图书管理系统期末大作业:从源码部署到答辩完整指南

简介&#xff1a;这是一份 JavaEE 图书管理系统完整项目&#xff0c;专为计算机专业课程设计、期末大作业及 JavaWeb 入门实战者准备。项目以图书馆日常业务为场景&#xff0c;涵盖用户登录、图书借还、订单管理等典型功能&#xff0c;代码结构清晰且确保可直接运行&#xff0c…

作者头像 李华
网站建设 2026/10/28 3:00:29

嵌入式文件管理:从inode、位示图到Flash掉电实战

1. 嵌入式场景下的文件管理&#xff1a;考试要分&#xff0c;项目要命&#xff0c;两头都得抓做嵌入式的朋友看到"文件管理"四个字&#xff0c;第一反应多半是&#xff1a;Linux下不就是cp、mv、rm的事吗&#xff0c;有必要单独当一章来啃&#xff1f;还真有必要。计…

作者头像 李华