首页 > 其他分享 >CSI架构和原理

CSI架构和原理

时间:2023-06-07 19:12:06浏览次数:49  
标签:Node 插件 架构 PVC CSI External 原理 PV

CSI

CSI简介

CSI的诞生背景

K8s 原生支持一些存储类型的 PV,如 iSCSI、NFS、CephFS 等等,这些 in-tree 类型的存储代码放在 Kubernetes 代码仓库中。这里带来的问题是 K8s 代码与三方存储厂商的代码强耦合:

  1. 更改 in-tree 类型的存储代码,用户必须更新 K8s 组件,成本较高

  2. in-tree 存储代码中的 bug 会引发 K8s 组件不稳定

  3. K8s 社区需要负责维护及测试 in-tree 类型的存储功能

  4. in-tree 存储插件享有与 K8s 核心组件同等的特权,存在安全隐患

  5. 三方存储开发者必须遵循 K8s 社区的规则开发 in-tree 类型存储代码

CSI 容器存储接口标准的出现解决了上述问题,将三方存储代码与 K8s 代码解耦,使得三方存储厂商研发人员只需实现 CSI 接口即可(无需关注容器平台是 K8s 还是 Swarm 等)。

Pod挂载volume的过程

  1. 用户创建一个包含PVC的Pod(使用动态存储卷);

  2. PV Controller发现这个PVC处于待绑定状态,调用Volume Plugin(in-tree或者out-of-tree)创建存储卷,并创建PV对象,然后将创建的PV与PVC绑定;

  3. Scheduler根据Pod的配置、节点状态、PV配置等信息,把Pod调度刀worker节点Node上;

  4. AD Controller发现Pod和PVC处于待挂载状态,调用Volume Plugin(in-tree或者out-of-tree)实现设备挂载到目标节点(/dev/vdb);

  5. 在worker节点上,kubelet(Volume Manager)等待设备挂载完成,通过Volume Plugin将设备挂载到指定目录:/var/lib/kubelet/pods/646154cf-dc72-11e9-b200-00163e007d53/volumes/alicloud~disk/pv-disk;

  6. kubelet在被告知挂载目录准备好后,启动Pod中的containers,用Docker -v方式(bind)将已经挂载到本地的卷映射到容器中;

 

CSI工作原理

CSI的部署模式

CSI 主要包含两个部分:CSI Controller Server 与 CSI Node Server,分别对应Controller Server Pod和Node Server Pod

Controller Server

只需要部署一个 Controller Server,如果是多备份的,可以部署两个。

Controller Server 主要是通过多个外部插件来实现的,一个 Pod 中可以定义多个 sideCar形式的External Container 和一个包含 CSI Controller Server 的 Container,这时候不同的 External 组件会和 Controller Server 组成不同的功能。

交互过程
External Provisioner + Controller Server 创建、删除数据卷
External Attacher + Controller Server 执行数据卷的挂载、卸载操作
Volume Manager + Volume Plugin + Node Server 执行数据卷的Mount、Umount操作
AD Controller + VolumePlugin 创建、删除VolumeAttachment对象
External Resizer + Controller Server 执行数据卷的扩容操作
ExternalSnapshotter+ControllerServer 执行数据卷的备份操作
Driver Registrar + VolumeManager + Node Server 注册CSI插件,创建CSINode对象

 

Node Server

Node Server Pod 是个 DaemonSet,它会在每个节点上进行注册。

Kubelet 会直接通过 Socket 的方式直接和 CSI Node Server 进行通信、调用 Attach/Detach/Mount/Unmount 等。

 

Driver Registrar

Driver Registrar 只是做一个注册的功能,会在每个节点上进行部署。

 

CSI的工作原理

CSI Controller

CSI Controller,它是以 deployment 的形式运行在集群里面,主要负责 provision 和 attach 工作。

attach并不是不是每一个存储都会用到的,而 provision 就是在使用 StorageClass 的时候会动态创建 PV 的过程, CSI Controller 在实现 provision 这个功能的时候,是 external-provisioner 这个 SideCar 去配合实现的,在实现 attach 功能的时候是external-attacher 这个SideCar配合它一起完成的。

注意:动态创建pv才会走到provision流程,静态并不会

CSI Node & CSI Identity

CSI Node 和 CSI Identity 通常是部署在一个容器里面的,它们是以 daemonset 的形式运行在集群里面,保证每一个节点会有一个 Pod 部署出来,这两个组件会和 CSI Controller 一起完成 volume 的 mount 操作。

CSI Identity 是用来告诉 Controller,我现在是哪一个 CSI 插件,它实现的接口会被 node-driver-registrar 调用给 Controller 去注册自己。

CSI Node 会实现一些 publish volume 和 unpublished volume 的接口,Controller 会去调用来完成 volume 的 mount 的操作,我们只需要实现这几个插件的接口就可以了。

 

CSI对象

volumeAttachment

volumeAttachment描述了一个volume卷挂载、卸载相关的信息,包含:卷的名字、挂载的节点、使用的CSIDriver插件、当前状态等信息,代表一个挂载操作的期望;

  • AD Controller在执行挂载一个CSI PV的时候,会调用csi-attacher(in-tree)创建一个volume Attachment

  • External-attacher通过watch volume attachment,发现有需要挂载的数据卷,调用csi-plugin的controllerPublishVolume方法,执行attach操作

  • volume Attachment是由AD Controller调用csi-attacher删除

❯ kubectl get volumeattachments -o wide
NAME     ATTACHER             PV                                   NODE           ATTACHED   AGE
csi-1ac8 udisk.csi.ucloud.cn   pvc-fc6b4beb-3766-488e-a261-792a25   10.13.34.201    true       103m
csi-87fe udisk.csi.ucloud.cn   pvc-65bb1aa1-b15e-45eb-82ce-29b2f7   10.13.136.103   true       103m

CSIDriver(驱动程序)

CSIDriver 用于定义和配置 CSI 驱动程序的属性和行为,是集群范围的资源对象。它描述了集群中所部署的 CSI Plugin 列表,需要管理员根据插件类型进行创建。CSIDriver 对象是集群范围的,即在整个集群中共享和使用;

可以通过 kuberctl get csidriver 可以看到集群里面创建的各种类型的 CSI Driver

❯ kubectl get csidrivers.csi.storage.k8s.io
NAME                 AGE
udisk.csi.ucloud.cn   4h9m

CSINode(节点)

CSINode 用于将 CSI 驱动程序绑定到节点上,表示节点上的 CSI 驱动程序插件,是节点级别的资源对象。它是集群中的节点信息,在 Node Driver Registrar 组件向 Kubelet 注册完毕后,Kubelet 会创建该资源,故不需要显式创建 CSINode 资源。它的作用是每一个新的 CSI Plugin 注册后,都会在 CSINode 列表里添加一个 CSINode 信息。

  • CSINode 对象用于告知 Kubernetes 集群该节点上可用的 CSI 驱动程序,以便在调度 Pod 时进行选择和匹配;

  • CSINode 对象是节点级别的,每个节点上都需要创建一个对应的 CSINode 对象;

将 Kubernetes 中 Node 资源名称与三方存储系统中节点名称(nodeID)一一对应。此处Kubelet会调用外部 CSI 插件NodeServer 的 GetNodeInfo 函数获取 nodeID。

CSINode 中 topologyKeys 用来表示存储节点的拓扑信息,卷拓扑信息会使得Scheduler在 Pod 调度时选择合适的存储节点。

❯ kubectl get csinodes.storage.k8s.io -o wide   # 集群一共有5个节点,对应5个csinode
NAME           DRIVERS   AGE
10.13.109.116   1         4h13m
10.13.116.114   1         4h13m
10.13.136.103   1         4h12m
10.13.170.186   1         4h13m
10.13.34.201    1         4h12m

 

CSI核心流程

K8s 中的 Pod 在挂载存储卷时需经历三个的阶段

  1. Provision/Delete(创盘/删盘)

  2. Attach/Detach(挂接/摘除)

  3. Mount/Unmount(挂载/卸载)

Provisioning Volumes

  • 创盘由External Provisioner来完成

  1. 集群管理员创建 StorageClass 资源,该 StorageClass 中包含 CSI 插件名称;

  2. 用户创建 PVC 资源,PVC 指定存储大小及 StorageClass;

  3. 卷控制器(PV Controller)观察到集群中新创建的 PVC 没有与之匹配的 PV,且其使用的存储类型为 out-of-tree,于是为 PVC 打 annotation:volume.beta.kubernetes.io/storage-provisioner=[out-of-tree CSI 插件名称]

  4. External Provisioner 组件观察到 PVC 的 annotation 中包含 volume.beta.kubernetes.io/storage-provisioner且其 value 是自己,于是开始创盘流程:

    1. 获取相关 StorageClass 资源并从中获取参数,用于后面 CSI 函数调用

    2. 通过 unix domain socket 调用外部 CSI 插件的CreateVolume 函数

  5. 外部 CSI 插件返回成功后表示盘创建完成,此时External Provisioner 组件会在集群创建一个 PersistentVolume 资源。

  6. 卷控制器会将 PV 与 PVC 进行绑定。

Attaching Volumes

  • 挂接由External Attacher完成

  1. AD 控制器(AttachDetachController)观察到使用 CSI 类型 PV 的 Pod 被调度到某一节点,此时AD 控制器会调用内部 in-tree CSI 插件(csiAttacher)的 Attach 函数;

  2. 内部 in-tree CSI 插件(csiAttacher)会创建一个 VolumeAttachment 对象到集群中;

  3. External Attacher 观察到该 VolumeAttachment 对象,并调用外部 CSI插件的ControllerPublish 函数以将卷挂接到对应节点上。当外部 CSI 插件挂载成功后,External Attacher会更新相关 VolumeAttachment 对象的 .Status.Attached 为 true;

  4. AD 控制器内部 in-tree CSI 插件(csiAttacher)观察到 VolumeAttachment 对象的 .Status.Attached 设置为 true,于是更新AD 控制器内部状态(ActualStateOfWorld),该状态会显示在 Node 资源的 .Status.VolumesAttached 上;

❯ kubectl get node 10.13.136.103 -o yaml | tail -n 20
...
volumesAttached:
- devicePath: ""
  name: kubernetes.io/csi/udisk.csi.ucloud.cn^bsm-jw6svmajfjn
volumesInUse:
- kubernetes.io/csi/udisk.csi.ucloud.cn^bsm-jw6svmajfjn

Mounting Volumes

  • 挂载由kubelet来完成

  1. Volume Manager(Kubelet 组件)观察到有新的使用 CSI 类型 PV 的 Pod 调度到本节点上,于是调用内部 in-tree CSI 插件(csiAttacher)的 WaitForAttach 函数;

  2. 内部 in-tree CSI 插件(csiAttacher)等待集群中 VolumeAttachment 对象状态 .Status.Attached 变为 true;

  3. in-tree CSI 插件(csiAttacher)调用 MountDevice 函数,该函数内部通过 unix domain socket 调用外部 CSI 插件的NodeStageVolume 函数;之后插件(csiAttacher)调用内部 in-tree CSI 插件(csiMountMgr)的 SetUp 函数,该函数内部会通过 unix domain socket 调用外部 CSI 插件的NodePublishVolume 函数;

Unmounting Volumes

  1. 用户删除相关 Pod;

  2. Volume Manager(Kubelet 组件)观察到包含 CSI 存储卷的 Pod 被删除,于是调用内部 in-tree CSI 插件(csiMountMgr)的 TearDown 函数,该函数内部会通过 unix domain socket 调用外部 CSI 插件的 NodeUnpublishVolume 函数;

  3. Volume Manager(Kubelet 组件)调用内部 in-tree CSI 插件(csiAttacher)的 UnmountDevice 函数,该函数内部会通过 unix domain socket 调用外部 CSI 插件的 NodeUnpublishVolume 函数。

Detaching Volumes

  1. AD 控制器观察到包含 CSI 存储卷的 Pod 被删除,此时该控制器会调用内部 in-tree CSI 插件(csiAttacher)的 Detach 函数;

  2. csiAttacher会删除集群中相关 VolumeAttachment 对象(但由于存在 finalizer,va 对象不会立即删除);

  3. External Attacher观察到集群中 VolumeAttachment 对象的 DeletionTimestamp 非空,于是调用外部 CSI 插件的ControllerUnpublish 函数以将卷从对应节点上摘除。外部 CSI 插件摘除成功后,External Attacher会移除相关 VolumeAttachment 对象的 finalizer 字段,此时 VolumeAttachment 对象被彻底删除;

  4. AD 控制器中内部 in-tree CSI 插件(csiAttacher)观察到 VolumeAttachment 对象已删除,于是更新AD 控制器中的内部状态;同时AD 控制器更新 Node 资源,此时 Node 资源的 .Status.VolumesAttached 上已没有相关挂接信息;

Deleting Volumes

  1. 用户删除相关 PVC;

  2. External Provisioner 组件观察到 PVC 删除事件,根据 PVC 的回收策略(Reclaim)执行不同操作:

    • Delete:调用外部 CSI 插件的DeleteVolume 函数以删除卷;一旦卷成功删除,Provisioner会删除集群中对应 PV 对象

    • Retain:Provisioner不执行卷删除操作;

     

CSI 组件

sidecar组件由Kubernetes官方维护

Cluster Driver Registrar

功能

Cluster Driver Registrar 负责自动注册 CSI 驱动程序到 Kubernetes 集群中

注册范围:Cluster Driver Registrar 在整个集群范围内工作,负责集群中所有节点上的 CSI 驱动程序的注册

注册过程

  • 注册过程:它会监听 Kubernetes API 中的 CSI 驱动程序配置对象(CSIDriver 对象),当有新的配置创建或更新时,Cluster Driver Registrar 将相应的 CSI 驱动程序注册到集群中。

  • 功能扩展:除了注册驱动程序,Cluster Driver Registrar 还负责更新驱动程序的信息到 Kubernetes API 中的其他对象,如 CSINode 和 CSIDriver 对象。

Cluster Driver Registrar & Node Driver Registrar

  • Cluster Driver Registrar 在整个集群范围内工作,负责自动注册和更新 CSI 驱动程序的信息。

  • Node Driver Registrar 在每个节点上运行,负责启动和管理该节点上的 CSI 驱动程序,并处理与存储卷附加和卸载相关的操作。

Node Driver Registrar

功能

Node-Driver-Registrar 组件会将外部 CSI 插件注册到Kubelet,从而使Kubelet通过特定的 Unix Domain Socket 来调用外部 CSI 插件函数

Kubelet 会调用外部 CSI 插件的 NodeGetInfo、NodeStageVolume、NodePublishVolume、NodeGetVolumeStats 等函数

注册成功后的操作

  1. Kubelet为本节点 Node 资源打 annotation:Kubelet调用外部 CSI 插件的NodeGetInfo 函数,其返回值 [nodeID]、[driverName] 将作为值用于 “csi.volume.kubernetes.io/nodeid” 键;

  2. Kubelet更新 Node Label:将NodeGetInfo 函数返回的 [AccessibleTopology] 值用于节点的 Label;

  3. Kubelet更新 Node Status:将NodeGetInfo 函数返回的 maxAttachLimit(节点最大可挂载卷数量)更新到 Node 资源的 Status.Allocatable:attachable-volumes-csi-[driverName]=[maxAttachLimit]

  4. Kubelet更新 CSINode 资源(没有则创建):将 [driverName]、[nodeID]、[maxAttachLimit]、[AccessibleTopology] 更新到 Spec 中(拓扑仅保留 Key 值);

External Provisioner

功能

External Provisioner用于创建/删除实际的存储卷,以及代表存储卷的 PV 资源

External-Provisioner在启动时需指定参数 — provisioner,该参数指定 Provisioner 名称,与 StorageClass 中的 provisioner 字段对应。

watch集群的PVC和PV资源

External-Provisioner启动后会 watch 集群中的 PVC 和 PV 资源:

1)对于PVC资源:

  1. 判断 PVC 是否需要动态创建存储卷,标准如下:

    • PVC 的 annotation 中是否包含 “volume.beta.kubernetes.io/storage-provisioner” 键(由卷控制器创建)并且其值是否与 Provisioner 名称相等

    • PVC 对应 StorageClass 的 VolumeBindingMode 字段:

      • 若为 WaitForFirstConsumer,则 PVC 的 annotation 中必须包含 “volume.kubernetes.io/selected-node” 键,且其值不为空;

      • 若为 Immediate 则表示需要 Provisioner 立即提供动态存储卷;

  2. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 CreateVolume 函数;

  3. 创建 PV 资源,PV 名称为 [Provisioner 指定的 PV 前缀] – [PVC uuid]

2)对于PV资源:

  1. 判断 PV 是否需要删除,标准如下:

    • 判断其 .Status.Phase 是否为 Release;

    • 判断其 .Spec.PersistentVolumeReclaimPolicy 是否为 Delete;

    • 判断其是否包含 annotation(pv.kubernetes.io/provisioned-by),且其值是否为自己;

  2. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 DeleteVolume 接口;

  3. 删除集群中的 PV 资源;

 

External Attacher

功能

External Attacher用于挂接/摘除存储卷

watch集群的VA和PV资源

External-Attacher 内部会时刻 watch 集群中的 VolumeAttachment 资源和 PersistentVolume 资源

1)对于VolumeAttachment资源:

  1. 从 VolumeAttachment 资源中获得 PV 的所有信息,如 volume ID、node ID、挂载 Secret 等

  2. 判断 VolumeAttachment 的 DeletionTimestamp 字段是否为空来判断其为卷挂接或卷摘除;

    • 若为卷挂接则通过特定的 Unix Domain Socket 调用外部 CSI 插件的ControllerPublishVolume 接口;

    • 若为卷摘除则通过特定的 Unix Domain Socket 调用外部 CSI 插件的ControllerUnpublishVolume 接口;

2)对于PV资源:

  1. 在挂接时为相关 PV 打上 Finalizer:external-attacher/[driver 名称]

  2. 当 PV 处于删除状态时(DeletionTimestamp 非空),删除 Finalizer:external-attacher/[driver 名称]

 

External Resizer

功能

External Resizer用于扩容存储卷

watch集群的PVC资源

External-Resizer内部会 watch 集群中的 PersistentVolumeClaim 资源

判断 PersistentVolumeClaim 资源是否需要扩容:PVC 状态需要是 Bound 且 .Status.Capacity 与 .Spec.Resources.Requests 不等

  1. 更新 PVC 的 .Status.Conditions,表明此时处于 Resizing 状态

  2. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 ControllerExpandVolume 接口

  3. 更新 PV 的 .Spec.Capacity

  4. 若 CSI 支持文件系统在线扩容,ControllerExpandVolume 接口返回值中 NodeExpansionRequired 字段为 true,External-Resizer更新 PVC 的 .Status.Conditions 为 FileSystemResizePending 状态;

  5. 若不支持则扩容成功,External-Resizer更新 PVC 的 .Status.Conditions 为空,且更新 PVC 的 .Status.Capacity

Volume Manager(Kubelet 组件)观察到存储卷需在线扩容,于是通过特定的 Unix Domain Socket 调用外部 CSI 插件的NodeExpandVolume 接口实现文件系统扩容

 

livenessprobe

livenessprobe用于检查CSI插件是否正常

通过对外暴露一个 / healthz HTTP 端口以服务 kubelet 的探针探测器,内部是通过特定的 Unix Domain Socket 调用外部 CSI 插件的 Probe 接口

 

第三方厂商要实现的CSI接口

IdentityServer

IdentityServer 主要用于认证 CSI 插件的身份信息

ControllerServer

ControllerServer 主要负责存储卷及快照的创建/删除以及挂接/摘除操作

NodeServer

NodeServer 主要负责存储卷挂载/卸载操作

 

标签:Node,插件,架构,PVC,CSI,External,原理,PV
From: https://www.cnblogs.com/hgzero/p/17464313.html

相关文章

  • 针对 B/S、C/S 架构的 180 个简单测试案例——GUI 和可用性测试场景
    这是一个针对web应用和桌面应用程序的测试清单假设:假定你的应用程序支持下列功能:-带有多种字段的表单-子窗口-与数据库交互-多种查询过滤规则和结果显示-图片上传-邮件发送-数据导出GUI和可用性测试场景1.页面中的所有字段(如:文本框,单选选项,下拉列表)应该适当对齐2.除特殊指定外,数......
  • 针对 B/S、C/S 架构的 180 个简单测试案例—窗口测试用例
    -测试清单可以提供给开发人员查阅,以保证在开发阶段就避免出现一些常见的问题。几点说明:1)用不同的用户角色执行这些测试场景,如:管理用户,来宾用户等。2)对于web应用,这些场景应该在客户认可的多种浏览器的各个版本上进行测试,如:IE,Firefox,Chrome,Safari等。3)用不同的屏幕分辨率进行测试,如......
  • 针对 B/S、C/S 架构的 180 个简单测试案例——结果表测试场景
    这是一个针对web应用和桌面应用程序的测试清单。假设:假定你的应用程序支持下列功能:-带有多种字段的表单-子窗口-与数据库交互-多种查询过滤规则和结果显示-图片上传-邮件发送-数据导出结果表测试场景1.当结果页面加载时长超过默认时长时,应该显示“页面加载中”之类的提示信息2.检......
  • 针对 B/S、C/S 架构的 180 个简单测试案例——图像上传功能的测试场景(也适用于其他文
    1.检查图片上传路径2.检查图像上传和修改功能3.检查各种扩展图像文件的上传(例如JPEG、PNG、BMP等).4.检查文件名中含有空格或其他可用特殊字符的图片的上传5.检查重复名称图片上传6.图片尺寸大于最大允许值,上传时应该显示适当的错误消息.7.检查上传的图片文件类型外的其它文件......
  • 架构师如何最大化自己的增量价值
    有的架构师不关注软件之外的事情,比如很少关心公司或部门的收入。这种性格虽然可以让他专注于软件工作,但从长期来看,如果不去思考如何通过技术为公司创造商业价值,那就很难保持或扩大自己在团队的影响力,职业发展也可能受挫。你可能听说过“在小数据里看大机会,在大数据里看小机会”这句......
  • .net低代码开发平台 快速实现组织架构数字化
    在数字化时代,企业面临着巨大的转型挑战。数字化赋予了企业极大的流程优化和高效性,提供了以前难以想象的效率。数字化让企业可以通过机器学习、大数据、自动化流程等手段,实现更快、更精确的决策和反应性。其中,企业组织架构数字化尤为重要。数字化转型是企业的整体转型,是对企业业务......
  • 如何做架构设计?
    也许您对软件设计存在一些疑惑,或者缺乏明确思路,那么本文将非常适合您。1、设计很重要我们可以看一下周边的事物,那些好的东西,他们并不会天然存在,都是被设计出来的,因此设计就是创造和改善事物的重要过程。设计的重要之处在于,最初的设计往往决定最终的结果,甚至决定着事物的长期的发......
  • GaussDB(DWS)查询过滤器原理与应用
    摘要:GaussDB(DWS)查询过滤器(黑名单)提供查询过滤功能,支持自动隔离反复被终止的查询,防止烂SQL再次执行。本文分享自华为云社区《GaussDB(DWS)查询过滤器原理与应用》,作者:门前一棵葡萄树。一、概述GaussDB(DWS)查询过滤器(黑名单)提供查询过滤功能,支持自动隔离反复被终止的查询,防止......
  • MySQL逻辑架构及执行过程
    一:MySQL逻辑架构1:MySQL逻辑架构客户端进程发送请求后服务器进程对接受的客户端请求做了什么处理,才能产生最后的处理结果呢?下面就看看大致的逻辑架构图上图基本组件介绍:连接层:Connectors【连接器】:MySQL服务之外的客户端程序请求MySQL或进行权限验证......
  • 即时通信聊天工具的原理与设计
     该软件采用P2P方式,各个客户端之间直接发消息进行会话聊天,服务器在其中只扮演协调者的角色(混合型P2P)。1.会话流程设计     当一个新用户通过自己的客户端登陆系统后,从服务器获取当前在线的用户信息列表,列表信息包括了系统中每个用户的地址。用户就可以开始独立工作,自主地......