K8s-存储原理说明
K8s存储主要是封装了对存储组件的细节,让我们可以通过pvc,pv操控就可以对存储进行管理。下面我们开始分析在k8s中是如何实现存储组件处理逻辑的。
前言
K8s存储主要是封装了对存储组件的细节,让我们可以通过pvc,pv操控就可以对存储进行管理。下面我们开始分析在k8s中是如何实现存储组件处理逻辑的。
容器挂载原理
我们知道,容器是以OverlayFS的形式存储在系统中,每一个容器都有它独有的目录,而容器的存储挂载就是将远端文件目录挂载到容器目录当中。我们可以使用mount查看当前系统上的挂载点,从这些信息中我们就可以找到每个容器合并呈现后的目录。
OverlayFS详细讲解请参考
https://zhuanlan.zhihu.com/p/436450556
K8s存储历史
http://www.c4a15wh.cn/index.php/archives/50/
总结一句话就是 一开始是in-tree形式,这种形式难以扩容(现在envoy也是这种方式,苦恼),然后是FlexVolume这种模式,这种模式与cni机制一致,都是通过调用二进制或者脚本文件来完成功能。最后就是现在推荐的方式CSI,这种方式会在下文详细讲解。
K8s存储逻辑的处理
存储逻辑大致流程为下面三步
- 在远端存储介质上分配出一块空间
- 如果是块存储需要进行attach操作,文件系统可以忽略此步
- mount挂载操作,如果是块操作需要先挂载到kubelet全局存储目录当中,因为块设备只能挂载一次,所以以全局目录作为一个中转站再挂载到pod内部。文件系统直接挂载到pod内部就行。
k8s的存储逻辑可以分为两个部分,一个是对pvc,pv的处理比如pvc与pv的连接,这一部分由controller manager进行管理。另一部分就是pod与存储目录的挂载操作,这一部分由kubelet去进行实现。
下面我们列出他们的处理流程
Controller Manager
- 监听pvc,当pvc被创建后触发下面逻辑。由PersistentVolumeController控制管理
- 判断pvc 是否指定了pv,如果指定了则获取pv然后进行绑定(将pvc的状态修改为Bound)
- 如果没有指定pv,首先会查询所有pv是否有符合绑定的条件,符合则进行绑定(条件是 访问权限大于或者等于,容量大于或者等于)。
- 如果没有匹配到适合的pv,则判断是否指定了StorageClassName,如果指定了则根据name获取相应的存储插件(后面会讲到 存储插件的注册机制),会调用 Provision() 方法,创建pv。
- 因为历史的缘故,之前的in-tree存储插件还保留了下来,所以对于一些组件的操作是需要 第 4 步,而对于现在的nfs 还是csi来说,他们的逻辑是在 自己的控制器中处理的,就拿csi举例,它通过external-provisioner 控制器监听pvc,判断当前pvc中的StorageClassName是否是自己注册的name(下面会将到csi的注册方式)。如果是则创建pv,而controller manager一直在监听pv,当pv创建后会触发bind方法对当前pvc建立绑定关系。(这里需要注意,controller manager 会定时刷新所有的pvc,pv 更新其状态,也会监听两个资源更新状态)。
- 等待调度器设置pod的nodename,然后ADcontroller manager会定期查询pvc所绑定的pod,是否有nodename如果有则开始获取pv所使用的存储组件,然后判断是否可以进行attach方法调用的是CanAttach()。如果可以attach操作,那么就执行attach方法。AD Cotroller与kubelet中的volume manager逻辑相似,都可以做Attach/Detach操作,但是kube-controller-manager与kubelet中,只会有一个组件做Attach/Detach操作,通过kubelet启动参数–enable-controller-attach-detach设置。设置为 true 表示启用kube-controller-manager的AD controller来做Attach/Detach操作,同时禁用 kubelet 执行 Attach/Detach 操作(默认值为 true)。controller manager 通过volumes.kubernetes.io/controller-managed-attach-detach 这个注解来判断是否使用kubelet 的ad controller manager 还是自己的。
kubelet
- volume manager 监听当前节点的pod,触发后获取pvc以及pv
- 开始调用waitForVolumeAttach 方法,如果开启了kubelet adcontroller 则开始进行attach操作。对于csi来说就是创建volumeAttachments资源然后触发 external attacher 组件进行attach操作。判断是csi是否有attach功能是通过volumePluginMgr 获取csiCSIDriver资源的attachRequired属性来判断的。
- 运行mountAttachedVolumes ,判断当前是否进行了attach操作,如果是则等待attach完成。然后挂载到全局目录,然后调用SetUp() 挂载到pod中。
存储插件注册
上面我们讲解了存储组件从attach到mount的操作。下面我们将讲解一下 存储组件是如何注册到kubelet。本文重点讲解csi注册的方式。
- 通过getPluginsRegistrationDir() 方法获取插件注册目录,然后监听里面的sock文件,调用RegisterPlugin方法将插件注册进去 具体逻辑在GenerateRegisterPluginFunc () 中
- 比如csi,node driver registrar会在该目录创建一个socker然后监听NodeGetInfo 接口。
- kubelet调用首先调用NodeGetInfo 获取当前csi插件的信息。ndr(node driver registrar)会返回Driver name以及CSI-Plugin的监听地址。
- 然后调用RegisterPlugin将当前信息注册到csi插件队列当中。然后创建CSInode 资源。
- 在使用的时候首先根据pv下面指定的插件类型 (假设csi),去csi map中查找当前csi驱动,然后调用nodepushlishVolume 将目录挂载到pod中。
CSI组件
上面是对k8s存储机制进行了分析,下面开始对csi机制进行分析。
CSI完美的诠释了什么CRD开发,它将in-tree的逻辑进行拆分出来,通过out-tree部署方式对资源进行监听触发的形式完成所有的存储功能。
比如attach操作,原本是由kubelet或者controller manager来完成的,在csi中是由 external Attacher 组件来完成的。
下面我们将介绍一下这几个组件有什么功能,以及它的一个原理。
- external Attacher 提供attach 与dettach操作,它会监听volumeattachment资源,然后在相应的节点上执行attach方法,该资源是由controller manager 的 ad controller manager 进行创建的。
- external Provisoner 存储制备器,它监听pvc资源,然后判断pvc是否指定了当前csi组件的sc ,然后根据sc设置的 远程存储服务器的地址以及参数,创建相应的存储空间然后创建pv
- external resizer 它会监听pvc,当发现pvc.Spec.Resources.Requests.storgage比pvc.Status.Capacity.storgage大时,进行底层存储系统的扩容。
- external snapshotter 拥有独有的资源VolumeSnapshot 根据它来对存储快照进行操作,创建删除等等。
- node driver registrar 通过unix 与kubelet,csi plugin 建立连接,主要用来将csi 插件注册到kubelet中,上面有说注册流程。
- csi plugin 它实现了csi介绍里面的所有接口,上面的这些组件最终都会调用该插件的方法,所以我们只需要实现它就可完成一个存储组件的适配工作。上面的组件都是通过grpc的形式进行连接,而地址默认为套接字的形式,所以一般将他们部署在同一个pod上。
参考文献
https://blog.csdn.net/weixin_39722563/article/details/111686325
https://blog.csdn.net/kyle18826138721/article/details/118873959 (扩容)
更多推荐
所有评论(0)