Kubernetes CSI注册流程

发布时间 2023-06-18 00:30:08作者: yurancensy

问题起因

近段时间,在工作中,遇到了一个问题:有一套K8S集群在做可靠性验证,在重启上下电之后,发现这个节点上的Pod状态异常。通过kubectl describe查看Pod情况,都是挂卷出错,而kubelet日志中也报了某个CSI插件没有找到。但问题是CSI插件对应的Pod是正常Running状态。那么为什么CSI会找不到呢?于是带着这个问题,研究起了k8s的代码。

代码分析

CSI没有找到,那意味着肯定是有地方储存CSI的数据,相应的有地方去创建/更新CSI的数据。这个咨询CSI业务的开发了解到,CSI注册后会更新csiNode资源,同时node资源上也会有csi的拓扑信息。
另外根据kubelet的日志报错信息:Error calling CSI NodeGetInfo()找到了CSI注册流程的代码。
CSI注册流程的代码入口在pkg/kubelet/pluginmanager/plugin_manager.go:108 (v1.25.3版本)

首先是Run方法,这里主要处理了两件事

  • 开启了kubelet对/var/lib/kubelet/plugins_registry目录的监听,如果有文件更新,会同步更新dsw的缓存
  • 开启了reconciler主循环,对比asw和dsw的差异,然后去触发CSI的卸载和注册

reconciler主循环

  • 卸载:遍历asw缓存中的插件信息,和dsw做比对。如果出现dsw没有的插件或者信息不一致的插件,则会将该插件卸载
  • 注册:遍历dsw缓存中的插件信息,如果asw没有,则会触发注册流程。通过proto文件定义双方的接口,kubelet作为客户端调用CSI Pod服务端实现的接口GetInfo、NodeGetInfo获取插件信息,然后向apiserver注册CSINode、Node信息,并在最后通过NotifyRegistrationStatus通知CSI Pod。

根据个人对代码的理解,绘制了下面的流程图。
CSI注册流程

问题分析

由于kubelet日志中出现了Error calling CSI NodeGetInfo()的报错日志,那么说明CSI Pod实现的NodeGetInfo方法出现了错误。通过CSI开发分析,是由于需要发请求,而节点刚重启,受网络配置的影响,请求超时了,导致方法报错。
那么又出了一个问题,为什么没有恢复呢?
原因在于在kubelet调用NodeGetInfo方法时,已经将asw缓存更新了,此时dsw和asw缓存一致,kubelet不会再触发卸载、注册流程。
这不合理呀,这里没有真正注册成功啊。
再次走读代码,终于识别到了一个点:kubelet是有通知事件发给CSI Pod的。在校验CSI信息和注册过程中,如果出现异常,kubelet会发一个注册失败的通知给CSI Pod。而在官方的CSI范例中,CSI是会清理自己在/var/lib/kubelet/plugins_registry目录创建的文件,并退出。因为CSI是一个Daemonset类型部署的Pod,那么在主进程退出之后,K8S会再次部署这个Pod。那么CSI Pod会再次进入CSI注册的主流程,而那个时候,网络就绪了,CSI注册也就会成功。
根据这个信息,向CSI开发确认,他们忽略了通知注册失败的处理,导致注册失败后没有自动恢复。
OK,问题定位清楚了,也趁着这个机会学习了CSI的注册流程。