一图看懂CSI插件如何注册至Kubernetes

2,347 阅读5分钟

背景

Kubernetes CSI演变过程

  • kubernetes v1.9 版本——Alpha版本
  • kubernetes v1.10版本——Beta版本
  • kubernetes v1.12版本——GA版本

什么是Kubernetes CSI?

CSI,即容器存储接口,Kubernetes为了满足存储厂商作为云存储接入的统一存储接口,容器存储接口定义了动态卷供给、卷挂载、卷扩容、卷快照等功能。CSI的实现是一种“Out Tree”的方式,但在存储使用上如同“In Tree”的存储一样。通过CSI的方式,新增存储的适配不在核心代码中进行维护与优化,从而大大减轻了Kubernetes的核心开发人员工作量。

为什么Kubernetes要引入CSI?

  1. 减轻Kubernetes核心代码负担
  2. 降低新增存储插件代码的发布风险
  3. 提高存储插件的扩展性
  4. 发展多样化的云存储

CSI插件注册

注册流程图

本系列主要面向的阅读对象为初步学习CSI、着手开发CSI插件以及深入理解CSI原理等用户,并在后续会持续更新CSI其他模块的原理解析。本篇基于node-driver-registrar源码进行分析,结合Kubernetes中Kubelet、Volume模块,完成CSI插件的整个注册过程,如下图所示:

2AFD79C7-6C90-4F61-81E7-999CD22B39CC.png

注册流程说明

CSI模块

这里的CSI模块主要包含node-driver-registrar和csi-driver,一般情况下它们以DaemonSet形式部署在Kubernetes上,并存在于同一个Pod中。 简单介绍一下这2个模块的作用:

  • csi-driver的作用主要是对卷进行附加、分离、挂载、卸载等操作,除此之外,还为其它模块提供插件信息和能力的GRPC服务(即IdentityServer);
  • node-driver-registrar的主要作用则是获取插件信息并通过GRPC服务(即RegistrationServer)向Kubelet提供插件的注册信息。 流程:
  1. csi-driver容器在启动时,会创建一个socket文件,通过该文件创建基于socket协议的grpc服务IdentityServer,IdentityServer提供插件信息、插件能力以及探针功能。
# csi插件的socket文件
/var/lib/kubelet/csi-plugins/<driverName>/csi.sock
  1. node-driver-registrar容器是作为一个sidecar容器存在,通过csi-driver创建的socket文件,向csi-driver发起插件信息请求。
  2. 获取信息后,node-driver-registrar将创建一个socket文件,提供注册信息服务RegistrationServer。
# 注册表socket文件,格式:<driverName>-reg.sock
/var/lib/kubelet/plugins_registry/<driverName>-reg.sock

Kubernetes-Kubelet模块

Kubelet模块中包含PluginManager,它的主要作用是通过PluginWatcher监听插件注册表目录下的socket文件创建与删除事件,来更新DesiredStateOfWorld和ActualStateOfWorld缓存,最终缓存信息被Volume模块进行消费。

# 注册表目录
/var/lib/kubelet/plugins_registry/

流程:

  1. PluginWatcher监听到注册表目录有新的插件创建,通过grpc请求服务端RegistrationServer校验插件是否规范。
  2. 校验成功后,会将需要添加的插件以"map[string]PluginInfo"类型存入DesiredStateOfWorld和ActualStateOfWorld缓存等待被消费。
  3. PluginManager通过Volume模块中的RegistrationHandler注册插件信息,不管成功与失败,RegistrationHandler都会通知node-driver-registrar插件是否成功被注册。
  4. PluginWatcher监听到注册表目录有插件删除,会从ActualStateOfWorld缓存中剔除已注册的插件。

Kubernetes-Volume模块

Volume模块中包含RegistrationHandler,主要处理缓存中的插件注册与注销操作。通过NodeInfoManager进行插件的安装与卸载,该行为实质是对资源CSINode和Node的设置,最终用于卷调度。 处理类型:

  • 注册插件
  1. DesiredStateOfWorld缓存 > ActualStateOfWorld缓存。
  • 注销插件
  1. ActualStateOfWorld缓存 > DesiredStateOfWorld缓存。
  2. 同一个插件,ActualStateOfWorld缓存的注册时间 ≠ DesiredStateOfWorld缓存的注册时间。

流程:

# 示例
driverNames: 
- testplugin1.csi.abc.com
- testplugin2.csi.abc.com
nodes: 
- node-1
  1. RegistrationHandler处理ResgisterPlugin操作,触发NodeInfoManager插件安装,资源CSINode添加drivers记录、资源Node添加annotations。
# 资源CSINode插件安装
apiVersion: storage.k8s.io/v1
kind: CSINode
metadata:
  name: node-1
  ownerReferences:
  - apiVersion: v1
    kind: Node
    name: node-1
    uid: e3954ac2-3742-457c-a94c-173e54e2fa35
spec:
  drivers:
  - name: testplugin1.csi.abc.com
    nodeID: node-1
    topologyKeys:
    - kubernetes.io/hostname
  # 添加testplugin2.csi.abc.com信息至drivers
  - name: testplugin2.csi.abc.com
    nodeID: node-1
    topologyKeys:
    - kubernetes.io/hostname
# 资源Node插件安装
apiVersion: v1
kind: Node
metadata:
  name: node-1
  annotations:
    # 添加testplugin2.csi.abc.com信息至annotations
    csi.volume.kubernetes.io/nodeid: '{"testplugin1.csi.abc.com":"node-1", "testplugin2.csi.abc.com":"node-1"}'
  1. RegistrationHandler处理DeResgisterPlugin操作,触发NodeInfoManager插件卸载,资源CSINode更新drivers记录、资源Node更新annotations。
# 资源CSINode插件卸载
apiVersion: storage.k8s.io/v1
kind: CSINode
metadata:
  name: node-1
  ownerReferences:
  - apiVersion: v1
    kind: Node
    name: node-1
    uid: e3954ac2-3742-457c-a94c-173e54e2fa35
spec:
  drivers:
  - name: testplugin1.csi.abc.com
    nodeID: node-1
    topologyKeys:
    - kubernetes.io/hostname
  # 添加testplugin2.csi.abc.com信息被删除
# 资源Node插件卸载
apiVersion: v1
kind: Node
metadata:
  name: node-1
  annotations:
    # 添加testplugin2.csi.abc.com信息被删除
    csi.volume.kubernetes.io/nodeid: '{"testplugin1.csi.abc.com":"node-1"}'

总结

至此,CSI插件的整个注册过程已经完成,Kubernetes和CSI很好的利用了C/S模式,通过GRPC实现通信。有人可能会问Kubernetes为什么不直接对/var/lib/kubelet/csi-plugins目录进行扫描,还需要通过node-driver-registrar组件? 我认为,node-driver-registrar组件的存在,一方面与csi-driver业务实现进行分离,另一方面在Kubelet注册组件之前保证CSI插件是可用的。另外,最重要的是/var/lib/kubelet/plugins_registry/目录是Kubelet统一插件注册表目录,而/var/lib/kubelet/csi-plugins仅仅只是插件中的一种,通过这种方式Kubernetes无需维护不同插件的逻辑,而是由插件提供方来将插件进行统一注册。