转载自
- k8s通过ceph-csi接入存储的概要分析 - 良凯尔 - 博客园
- 如何接入 K8s 持久化存储?K8s CSI 实现机制浅析 - 腾讯云原生 - 博客园
- 29 | PV、PVC体系是不是多此一举?从本地持久化卷谈起-深入剖析 Kubernetes-极客时间
- K8S-StorageClass资源-实践【补充知识点】 - 小粉优化大师 - 博客园
- 29 | PV、PVC体系是不是多此一举?从本地持久化卷谈起 - 极客时间已完结课程限时免费阅读
概述
下面的分析是k8s通过ceph-csi(csi plugin)接入ceph存储(csi相关组件的分析以rbd为例进行分析),对csi系统结构、所涉及的k8s对象与组件进行了简单的介绍,以及k8s对存储进行相关操作的流程分析,存储相关操作包括了存储创建、存储扩容、存储挂载、解除存储挂载以及存储删除操作。
csi系统结构
这是一张k8s csi的系统架构图,图中所画的组件以及k8s对象,接下来会一一进行分析。
csi简介
CSI是Container Storage Interface(容器存储接口)的简写。
CSI的目的是定义行业标准"容器存储接口",使存储供应商(SP)能够开发一个符合CSI标准的插件并使其可以在多个容器编排(CO)系统中工作。CO包括Cloud Foundry, Kubernetes, Mesos等。
CSI组件一般采用容器化部署,减少了环境依赖。
组件及部署模型
概念
概念 | ||
---|---|---|
in-tree | k8s 默认支持的存储(如 nfs、hostpath),相关存储管控代码直接包含在 Kubernetes 项目代码中 | |
out-tree | 想要引入的外部存储,编写项目满足 k8s 规定的 csi 接口规范,该项目独立于 Kubernetes 项目代码 | |
相当于一个插件,k8s 使用此存储时会调用,每种想支持的外部存储都需要开发一个这样的插件,他们有个统称叫 csi-plugin | ||
如下面 ceph-csi 就是个 csi-plugin |
1. In-tree 逻辑
- 只需要提供 Provisioner :在 Kubernetes 中,in-tree 插件(如 NFS、AWS EBS 等)是内置的存储插件,通常只需要在
StorageClass
中指定相应的 provisioner 名称。 - 工作方式
- Provisioner 负责动态创建存储卷,并且直接与 Kubernetes 的 API 进行交互。
- 这些 in-tree 插件的代码是集成在 Kubernetes 的代码库中,因此不需要额外的组件来管理存储请求。
- K8S-StorageClass资源-实践【补充知识点】 - 小粉优化大师 - 博客园 此插件就是 in-tree 插件,k8s 社区开发
2. Out-of-tree 逻辑
- 需要提供 External-Provisioner 和 CSI Plugin
- External-Provisioner:作为一个外部组件,负责处理 Kubernetes 中的 PVC 请求。它会监听 PVC 创建事件,并调用 CSI 插件。
- CSI Plugin:这是与底层存储系统直接交互的插件,执行存储卷的创建、删除、挂载等操作。CSI 插件是根据 Kubernetes 的 CSI 规范实现的,能够支持多种存储后端(如 Ceph、GCE PD、OpenStack 等)。
3. 总结
- In-tree 插件:只需配置 provisioner,利用内置逻辑动态管理存储。
- Out-of-tree 插件:需要组合使用 external-provisioner 和 CSI plugin,来实现更复杂和灵活的存储管理。
这种分离使得 Kubernetes 在扩展性和灵活性方面更具优势,允许用户选择和使用适合其环境的存储解决方案。
部署模型
部署模型 | ||
---|---|---|
控制端 | 单 Pod 内包含 csi-plugin、external-provisioner、external-attacher、external-resizer、external-snapshotter | statefulset 或 deployment, 一般部署在 master 节点上 |
作用:可以理解为,创建存储,并挂载到 Pod 对应的 Node 上,作为一个 dev | ||
业务端 | 单 Pod 内包含 csi-plugin、node-driver-register | daemonset,所有节点 |
作用:可以理解为,将上面的存储 dev,挂载全局目录,在挂载到目标 Pod 的目录 |
组件
组件 | ||
---|---|---|
csi-plugin | 外部存储的统称,用于外部存储的创建,挂载等,主要包含两部分逻辑 Controller Server 和 Node Server 这两个 Server 都在这一个项目中,不过在不同端运行不同的模块而已 | |
Controller Server 工作在控制端, 负责外部存储的创建、删除 还负责将外部存储挂载到 node 及卸载(Attach、mount 操作) 由 external-XXX 插件 sidecar 容器,触发调用 | ||
Node Server 工作在业务端, 负责将挂载在 node 上的外部存储 dev,挂载到 staging path(全局目录,mount 操作) 还负责将 staging path 挂载到 pod path(pod目录,bind-mount 操作) 由 node 上的 kubelet 触发调用 | ||
external-provisioner | 辅助作用,负责调用 csi-plugin 的接口,进行外部存储的创建和删除 | |
watch pvc(只关注具有此 csi-plugin provision annotation 标识的 pvc,pvc 创建时会从 storageclass 获取此标识) 之后调用 csi-plugin 的 CreateVolume 方法,创建外部存储 | ||
watch pv 的变化(由bound变为release),调用 csi-plugin 的 DeleteVolume 方法,删除外部存储 | ||
external-attacher | 辅助作用,负责调用 csi-plugin 的接口,将外部存储挂载到 node 及卸载 | |
watch VolumeAttachment 资源的变化 之后调用 csi-plugin 的 ControllerPublishVolume 方法,将外部存储挂载到指定 node 上 | ||
in-tree 逻辑,不需要此 VolumeAttachment 资源 | ||
VA 是如何产生的? kube-controller-manager 中的 AD controller 会根据缓存中记录的 node 上的 预期 pv 挂载<信息 dsw 和 实际 pv 挂载信息 asw,进行对比,产生此 VolumeAttachment 资源 | ||
external-resizer | 辅助作用,调用 csi-plugin 对应的接口,进行外部存储的底层扩容 | |
external-snapshotter | 辅助作用,调用 csi-plugin 对应的接口,对外部存储打个快照(备份) | |
大体流程梳理
存储的挂载
存储的挂载 | ||
---|---|---|
用户创建 pod 使用远端存储 ceph | 首先用户创建 pvc,然后创建 pod 配置此 pvc | |
pv controller 监控到 pvc 的创建,查找合适的 pv 进行绑定 - 若 pvc 的 storageclass 中配置了延迟绑定,此处就不进行绑定,调度过程中再绑定 | ||
若 pv controller 找不到合适的 pv,将会根据 pvc 中记录的 storageclass,找对应的 volume plugin 进行 pv 的创建 -in-tree逻辑:调用内置的volume plungin来创建存储(好像支持 nfs,本地卷的话,需要手动提前创建pv),并创建pv对象。 - out-tree逻辑:pv controller来给pvc对象设置annotation:volume.beta.kubernetes.io/storage-provisioner:{plugin name} ,让相应的plugin来创建存储,并创建pv对象(例:当volume plugin为ceph-csi时,由external provisioner来完成创建存储与pv对象的操作)。下面主要介绍 out-tree 逻辑 |
||
若找不到现存合适的pv对象时,将更新pvc对象,添加annotation:volume.beta.kubernetes.io/storage-provisioner ,让 external-provisioner组件开始开始创建存储与pv对象的操作 |
||
external-provisioner组件监听到pvc的新增事件,判断pvc的annotation:volume.beta.kubernetes.io/storage-provisioner 的值,即判断是否是自己来负责做创建操作,是则调用ceph 的 csi-plugin组件进行存储 pv 的创建(这里称为 ceph-csi) |
||
pv controller 监听到 pv 的创建,将 pvc 和 pv 进行绑定(若是延迟绑定,此处就不进行绑定,调度过程中再绑定) | ||
pod 进行调度,调度到合适的 node 节点(若是延迟绑定,此时将 pvc 和 pv 进行绑定,调度器中有个逻辑类似 pv controller 可以实现 pvc 和 pv 的绑定) | ||
ad controller 维护一个缓存,记录 node 上的预期 pv (dsw)和实际 pv (asw)的关系,发现实际不符合预期(也就是 pv 没有挂载上来),将会产生一个调谐 | ||
调谐逻辑 - in-tree,调用 k8s 内部逻辑(完成 pv 挂载到该 node 上,称为 attach ,就是 mount 操作) - out-tree,需要触发外部进行 attach 操作(将 pv mount 到 node 上),因此会产生一个 VolumeAttachment 资源 | ||
external-attacher 监听到 VolumeAttachment 资源的变化,将调用 csi-plugin 的接口,将 pv mount 到 node 上作为一个 dev (称为 attach 操作),之后会更新 VolumeAttachment.status.attached=true | ||
AD controller watch 到 VolumeAttachment 资源的变化,更新node对象的.Status.VolumesAttached属性值,将该volume记为attached | ||
kubelet中的volume manager获取node.Status.VolumesAttached属性值,发现volume已被标记为attached | ||
于是 volume manager中的reconcile()调用 csi-plugin 组件的NodeStageVolume与NodePublishVolume完成挂载 | ||
NodeStageVolume 是将外部存储挂载的dev,挂载到 staging path(全局目录,用于多次挂载到 pod 目录),此处为 mount 操作 | ||
NodePublishVolume 是将 staging path 挂载到 pod path,此处为 bind-mount 操作 |
存储的卸载
存储的卸载 | ||
---|---|---|
用户删除声明了pvc的pod | ||
AD controller或volume manager中的reconcile()发现有volume未执行dettach操作,于是进行dettach操作,即删除VolumeAttachment对象 | ||
AD controller或volume manager等待VolumeAttachment对象删除成功 | ||
AD controller更新新node对象的.Status.VolumesAttached属性值,将标记为attached的该volume从属性值中去除 | ||
kubelet中的volume manager获取node.Status.VolumesAttached属性值,找不到相关的volume信息 | ||
于是volume manager中的reconcile()调用ceph-csi组件的NodeUnpublishVolume与NodeUnstageVolume完成解除挂载 | ||
删除存储 | ||
用户删除pvc对象 | ||
pv controller发现与pv绑定的pvc对象被删除,于是更新pv的状态为released | ||
external-provisioner watch到pv更新事件,并检查pv的状态是否为released,以及回收策略是否为delete | ||
接下来external-provisioner组件会调用ceph-csi的DeleteVolume来删除存储 | ||
ceph-csi组件的DeleteVolume方法,调用ceph集群命令,删除底层存储 | ||
external-provisioner组件删除pv对象 |
各组件 watch 逻辑
pv controller | watch pvc,寻找 pv 进行绑定;找不到去触发 pv 创建,给 pvc 加上 storageclass 中 provisioner annotation:volume.beta.kubernetes.io/storage-provisioner:{plugin name} |
|
external-provisioner | watch pvc 的变化,发现是自己的 annotation,调用 csi-plugin 进行存储的创建 | |
ad controller | pod 调度完成后,发现 node 上预期 pv 和实际 pv 不符(ASW 和 DSW),创建 VA(volumeAttachment) | |
external-attacher | watch VA 的变化,调用 csi-plugin,将存储卷挂载到 node 上作为 dev,成功后更新 VolumeAttachment.status.attached=true | |
ad controller | watch 到 VolumeAttachment 资源的变化,更新node对象的.Status.VolumesAttached属性值,将该volume记为attached | |
kubelet中的volume manage | 获取node.Status.VolumesAttached属性值,发现volume已被标记为attached 于是 volume manager中的reconcile()调用 csi-plugin 组件的NodeStageVolume与NodePublishVolume完成挂载 | |
mount 和 bind-mount 的区别
mount
是一个通用的命令,用于将文件系统(如硬盘分区、CD-ROM、网络文件系统等)挂载到 Linux 文件系统的某个目录上,使该文件系统的内容可以在该目录中访问。mount <device> <mount_point>
:将指定的设备(如硬盘分区/dev/sda1
)挂载到指定的挂载点(如/mnt
)。- 适用于挂载一个物理或虚拟设备上的文件系统。例如,将一个硬盘分区挂载到
/mnt/data
,或将一个 NFS(网络文件系统)挂载到本地目录。 - 常见的挂载类型有
ext4
、xfs
、ntfs
、vfat
、nfs
等。 - 挂载后,指定设备的文件系统内容将显示在挂载点目录中。原挂载点的内容会被临时隐藏,直到卸载为止。
bind-mount
是一种特殊的挂载方式,它并不挂载一个新的文件系统,而是将已有文件系统上的一个目录绑定到另一个目录,使两个目录看起来共享相同的内容。mount --bind <source_directory> <target_directory>
:将已有的源目录<source_directory>
绑定到目标目录<target_directory>
。- 适用于当需要将一个目录的内容共享到另一个位置而不改变底层文件系统时,可以使用
bind-mount
。常用于容器、chroot 环境或某些系统管理任务中。例如,将/var/log
目录绑定到/mnt/log
,这样两个目录都指向相同的数据。 bind-mount
没有挂载类型,因为它只是绑定一个已有的目录。bind-mount
后,源目录和目标目录都会显示相同的文件和内容。它们实际上是相同的目录,所有对文件的修改都会反映在两个位置上。- 不改变原始目录的文件系统类型,也不需要重新格式化设备。
涉及k8s对象
1. PersistentVolume
持久存储卷,集群级别资源,代表了存储卷资源,记录了该存储卷资源的相关信息。
回收策略
(1)retain:保留策略,当删除PVC的时候,PV与外部存储资源仍然存在。
(2)delete:删除策略,当与pv绑定的pvc被删除的时候,会从k8s集群中删除PV对象,并执行外部存储资源的删除操作。
(3)resycle(已废弃)
pv状态迁移
available --> bound --> released
2. PersistentVolumeClaim
持久存储卷声明,namespace级别资源,代表了用户对于存储卷的使用需求声明。
示例:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: csi-cephfs-sc
volumeMode: Filesystem
pvc状态迁移
pending --> bound
3. StorageClass
定义了创建pv的模板信息,集群级别资源,用于动态创建pv。
示例:
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-rbd-sc
parameters:
clusterID: ceph01
imageFeatures: layering
imageFormat: "2"
mounter: rbd
pool: kubernetes
provisioner: rbd.csi.ceph.com
reclaimPolicy: Delete
volumeBindingMode: Immediate
4. VolumeAttachment
VolumeAttachment 记录了pv的相关挂载信息,如挂载到哪个node节点,由哪个volume plugin来挂载等。
AD Controller 创建一个 VolumeAttachment,而 External-attacher 则通过观察该 VolumeAttachment,根据其状态属性来进行存储的挂载和卸载操作。
示例:
yaml
apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
name: csi-123456
spec:
attacher: cephfs.csi.ceph.com
nodeName: 192.168.1.10
source:
persistentVolumeName: pvc-123456
status:
attached: true
5. CSINode
CSINode 记录了csi plugin的相关信息(如nodeId、driverName、拓扑信息等)。
当Node Driver Registrar向kubelet注册一个csi plugin后,会创建(或更新)一个CSINode对象,记录csi plugin的相关信息。
示例:
yaml
apiVersion: storage.k8s.io/v1
kind: CSINode
metadata:
name: 192.168.1.10
spec:
drivers:
- name: cephfs.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
- name: rbd.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
涉及组件与作用
下面先简单介绍下涉及的组件与作用,后面会有单独详细的介绍各个组件的作用。
1. volume plugin
扩展各种存储类型的卷的管理能力,实现第三方存储的各种操作能力与k8s存储系统的结合。调用第三方存储的接口或命令,从而提供数据卷的创建/删除、attach/detach、mount/umount的具体操作实现,可以认为是第三方存储的代理人。前面分析组件中的对于数据卷的创建/删除、attach/detach、mount/umount操作,全是调用volume plugin来完成。
后续对volume plugin的详细分析,以通过ceph-csi操作rbd为例进行分析。
根据源码所在位置,volume plugin分为in-tree与out-of-tree。
in-tree
在k8s源码内部实现,和k8s一起发布、管理,更新迭代慢、灵活性差。
out-of-tree
代码独立于k8s,由存储厂商实现,有csi、flexvolume两种实现。
csi plugin
本次的分析为k8s通过ceph-csi来使用ceph存储,ceph-csi属于csi plugin。csi plugin分为ControllerServer与NodeServer,各负责不同的存储操作。
external plugin
external plugin包括了external-provisioner、external-attacher、external-resizer、external-snapshotter等,external plugin辅助csi plugin组件,共同完成了存储相关操作。external plugin负责watch pvc、volumeAttachment等对象,然后调用volume plugin来完成存储的相关操作。如external-provisioner watch pvc对象,然后调用csi plugin来创建存储,最后创建pv对象;external-attacher watch volumeAttachment对象,然后调用csi plugin来做attach/dettach操作;external-resizer watch pvc对象,然后调用csi plugin来做存储的扩容操作等。
Node-Driver-Registrar
Node-Driver-Registrar组件负责实现csi plugin(NodeServer)的注册,让kubelet感知csi plugin的存在。
组件部署方式
csi plugin controllerServer与external plugin作为容器,使用deployment部署,多副本可实现高可用;而csi plugin NodeServer与Node-Driver-Registrar作为容器,使用daemonset部署,即每个node节点都有。
2. kube-controller-manager
PV controller
负责pv、pvc的绑定与生命周期管理(如创建/删除底层存储,创建/删除pv对象,pv与pvc对象的状态变更)。
创建/删除底层存储、创建/删除pv对象的操作,由PV controller调用volume plugin(in-tree)来完成。本次分析的是k8s通过ceph-csi来使用ceph存储,volume plugin为ceph-csi,属于out-tree,所以创建/删除底层存储、创建/删除pv对象的操作由external-provisioner来完成。
AD controller
AD Cotroller全称Attachment/Detachment 控制器,主要负责创建、删除VolumeAttachment对象,并调用volume plugin来做存储设备的Attach/Detach操作(将数据卷挂载到特定node节点上/从特定node节点上解除挂载),以及更新node.Status.VolumesAttached等。
不同的volume plugin的Attach/Detach操作逻辑有所不同,如通过ceph-csi(out-tree volume plugin)来使用ceph存储,则的Attach/Detach操作只是修改VolumeAttachment对象的状态,而不会真正的将数据卷挂载到节点/从节点上解除挂载,真正的节点存储挂载/解除挂载操作由kubelet中volume manager调用rc.operationExecutor.MountVolume/rc.operationExecutor.UnmountDevice方法时,调用ceph-csi来完成,后面会有博文详细做介绍。
3. kubelet
volume manager
主要是管理卷的Attach/Detach(与AD controller作用相同,通过kubelet启动参数控制哪个组件来做该操作,后续会详细介绍)、mount/umount等操作。
本次的分析为k8s通过ceph-csi来使用ceph存储。本次分析中,volume manager的Attach/Detach操作只创建/删除VolumeAttachment对象,而不会真正的将数据卷挂载到节点/从节点上解除挂载;csi-attacer组件也不会做挂载/解除挂载操作,只是更新VolumeAttachment对象,真正的节点挂载/解除挂载操作由kubelet中volume manager调用rc.operationExecutor.MountVolume/rc.operationExecutor.UnmountDevice方法时,调用ceph-csi来完成,后面会有博文详细做介绍。
kubernetes创建与挂载volume(in-tree volume plugin)
先来看下kubernetes通过in-tree volume plugin来创建与挂载volume的流程
(1)用户创建pvc;
(2)PV controller watch到pvc的创建,寻找合适的pv与之绑定。
(3)(4)当找不到合适的pv时,将调用volume plugin来创建volume,并创建pv对象,之后该pv对象与pvc对象绑定。
(5)用户创建挂载pvc的pod;
(6)kube-scheduler watch到pod的创建,为其寻找合适的node调度。
(7)(8)pod调度完成后,AD controller/volume manager watch到pod声明的volume没有进行attach操作,将调用volume plugin来做attach操作。
(9)volume plugin进行attach操作,将volume挂载到pod所在node节点,成为如/dev/vdb的设备。
(10)(11)attach操作完成后,volume manager watch到pod声明的volume没有进行mount操作,将调用volume plugin来做mount操作。
(12)volume plugin进行mount操作,将node节点上的第(9)步得到的/dev/vdb设备挂载到指定目录。
kubernetes创建与挂载volume(out-of-tree volume plugin)
再来看下kubernetes通过out-of-tree volume plugin来创建与挂载volume的流程,以csi-plugin为例。
(1)用户创建pvc;
(2)PV controller watch到pvc的创建,寻找合适的pv与之绑定。当寻找不到合适的pv时,将更新pvc对象,添加annotation:volume.beta.kubernetes.io/storage-provisioner
,让external-provisioner组件开始开始创建存储与pv对象的操作。
(3)external-provisioner组件watch到pvc的创建,判断annotation:volume.beta.kubernetes.io/storage-provisioner
的值,即判断是否是自己来负责做创建操作,是则调用csi-plugin ControllerServer来创建存储,并创建pv对象。
(4)PV controller watch到pvc,寻找合适的pv(上一步中创建)与之绑定。
(5)用户创建挂载pvc的pod;
(6)kube-scheduler watch到pod的创建,为其寻找合适的node调度。
(7)(8)pod调度完成后,AD controller/volume manager watch到pod声明的volume没有进行attach操作,将调用csi-attacher来做attach操作(实际上只是创建volumeAttachement对象)。
(9)external-attacher组件watch到volumeAttachment对象的新建,调用csi-plugin进行attach操作(如果volume plugin是ceph-csi,external-attacher组件watch到volumeAttachment对象的新建后,只是修改该对象的状态属性,不会做attach操作,真正的attach操作由kubelet中的volume manager调用volume plugin ceph-csi来完成)。
(10)csi-plugin ControllerServer进行attach操作,将volume挂载到pod所在node节点,成为如/dev/vdb的设备。
(11)(12)attach操作完成后,volume manager watch到pod声明的volume没有进行mount操作,将调用csi-mounter来做mount操作。
(13)csi-mounter调用csi-plugin NodeServer进行mount操作,将node节点上的第(10)步得到的/dev/vdb设备挂载到指定目录。
kubernetes存储相关操作流程具体分析(out-of-tree volume plugin,以ceph-csi为例)
下面来看下kubernetes通过out-of-tree volume plugin来创建/删除、挂载/解除挂载volume的流程。
下面先对每个操作的整体流程进行分析,后面会对涉及的每个组件进行源码分析。
1. 存储创建
流程图
流程分析
(1)用户创建pvc对象;
(2)pv controller监听pvc对象,寻找现存的合适的pv对象,与pvc对象绑定。当找不到现存合适的pv对象时,将更新pvc对象,添加annotation:volume.beta.kubernetes.io/storage-provisioner
,让external-provisioner组件开始开始创建存储与pv对象的操作;当找到时,将pvc与pv绑定,结束操作。
(3)external-provisioner组件监听到pvc的新增事件,判断pvc的annotation:volume.beta.kubernetes.io/storage-provisioner
的值,即判断是否是自己来负责做创建操作,是则调用ceph-csi组件进行存储的创建;
(4)ceph-csi组件调用ceph创建底层存储;
(5)底层存储创建完成后,external-provisioner根据存储信息,拼接pv对象,创建pv对象;
(6)pv controller监听pvc对象,寻找合适的pv对象,与pvc对象绑定。
2. 存储扩容
流程图
流程分析
(1)修改pvc对象,修改申请存储大小(pvc.spec.resources.requests.storage);
(2)修改成功后,external-resizer监听到该pvc的update事件,发现pvc.Spec.Resources.Requests.storgage比pvc.Status.Capacity.storgage大,于是调ceph-csi组件进行 controller端扩容;
(3)ceph-csi组件调用ceph存储,进行底层存储扩容;
(4)底层存储扩容完成后,external-resizer组件更新pv对象的.Spec.Capacity.storgage的值为扩容后的存储大小;
(5)kubelet的volume manager在reconcile()调谐过程中发现pv.Spec.Capacity.storage大于pvc.Status.Capacity.storage,于是调ceph-csi组件进行 node端扩容;
(6)ceph-csi组件对dnode上存储对应的文件系统扩容;
(7)扩容完成后,kubelet更新pvc.Status.Capacity.storage的值为扩容后的存储大小。
3. 存储挂载
流程图
kubelet启动参数--enable-controller-attach-detach,该启动参数设置为 true 表示启用 Attach/Detach controller进行Attach/Detach 操作,同时禁用 kubelet 执行 Attach/Detach 操作(默认值为 true)。实际上Attach/Detach 操作就是创建/删除VolumeAttachment对象。
(1)kubelet启动参数--enable-controller-attach-detach=true,Attach/Detach controller进行Attach/Detach 操作
(2)kubelet启动参数--enable-controller-attach-detach=false,kubelet端volume manager进行Attach/Detach 操作
流程分析
(1)用户创建一个挂载了pvc的pod;
(2)AD controller或volume manager中的reconcile()发现有volume未执行attach操作,于是进行attach操作,即创建VolumeAttachment对象;
(3)external-attacher组件list/watch VolumeAttachement对象,更新VolumeAttachment.status.attached=true;
(4)AD controller更新node对象的.Status.VolumesAttached属性值,将该volume记为attached;
(5)kubelet中的volume manager获取node.Status.VolumesAttached属性值,发现volume已被标记为attached;
(6)于是volume manager中的reconcile()调用ceph-csi组件的NodeStageVolume与NodePublishVolume完成挂载。
4. 解除存储挂载
流程图
(1)AD controller
(2)volume manager
流程分析
(1)用户删除声明了pvc的pod;
(2)AD controller或volume manager中的reconcile()发现有volume未执行dettach操作,于是进行dettach操作,即删除VolumeAttachment对象;
(3)AD controller或volume manager等待VolumeAttachment对象删除成功;
(4)AD controller更新新node对象的.Status.VolumesAttached属性值,将标记为attached的该volume从属性值中去除;
(5)kubelet中的volume manager获取node.Status.VolumesAttached属性值,找不到相关的volume信息;
(6)于是volume manager中的reconcile()调用ceph-csi组件的NodeUnpublishVolume与NodeUnstageVolume完成解除挂载。
5. 删除存储
流程图
流程分析
(1)用户删除pvc对象;
(2)pv controller发现与pv绑定的pvc对象被删除,于是更新pv的状态为released;
(3)external-provisioner watch到pv更新事件,并检查pv的状态是否为released,以及回收策略是否为delete;
(4)接下来external-provisioner组件会调用ceph-csi的DeleteVolume来删除存储;
(5)ceph-csi组件的DeleteVolume方法,调用ceph集群命令,删除底层存储;
(6)external-provisioner组件删除pv对象。
Node Driver Registrar注册csi plugin driver步骤
- Node Driver Registrar连接csi plugin组件暴露的grpc服务socket地址,调用GetPluginInfo接口,获取csi plugin的driver名称。
- 在
kubelet-registration-path
目录下启动一个 socket,对外暴露GetInfo 和 NotifyRegistrationStatus 两个接口。kubelet 通过 Watcher可以发现该socket。 - kubelet 通过 Watcher监控
/var/lib/kubelet/plugins_registry/
目录,发现上述socket 后,通过该 socket 调用 Node-Driver-Registrar 的 GetInfo 接口,获取csi plugin组件暴露的grpc服务socket地址以及csi plugin组件的driver名称。 - kubelet 通过csi plugin组件暴露的grpc服务socket地址对其NodeGetInfo接口进行调用,获取csi plugin的nodeID等信息。
- kubelet根据上一步获得的信息,去更新node节点的 Annotations、Labels、status.allocatable 等信息,同时创建(或更新)一个 CSINode 对象。
- kubelet通过socket调用Node-Driver-Registrar容器的NotifyRegistrationStatus接口,通知注册csi plugin成功。