一、背景
外部存储接入 Kubernetes 的方式主要有两种:In-Tree 和 Out-of-Tree:
In-Tree 是指存储驱动的源码都在 Kubernetes 代码库中,与 Kubernetes 一起发布、迭代、管理,这种方式灵活性较差,且门槛较高。
Out-of-Tree 是指存储插件由第三方编写、发布、管理,作为一种扩展与 Kubernetes 配合使用。Out-of-Tree 主要有 FlexVolume 和 CSI 两种实现方式,其中,FlexVolume 因为其命令式的特点,不易维护和管理,从 Kubernetes v1.23 版本开始已被弃用。因此 CSI 已经成为 Kubernetes 存储扩展( Out-of-Tree )的唯一方式。
外部存储最终的效果是将存储(磁盘、obs、nas 盘等)挂载到容器中被业务使用,所以一般包括存在两个过程:
attach 是将存储介质在指定虚拟机上绑盘,部分存储介质才需要 attach 操作,比如容器中使用块存储,大致流程是 1)需要先调用 openstack 接口,将某块 evs 绑到某个虚拟机上,成为虚拟机设备;2)在将存储设备挂载到容器目录上
mount 将某个存储挂载到对应文件系统上,是操作系统层面的行为,所有的存储介质挂载到容器中都需要 mount 阶段,比如容器中使用 nas 或者 obs,本质上就是执行 nfs 命令将网络存储挂载到容器目录上
二、CSI 架构解读
Kubernetes CSI 存储插件的关键组件与推荐的容器化部署架构 
三、动态创建 Volume 执行过程
以块类型存储为例,从声明 PVC 到 Pod 挂载卷成功时序图:

一、涉及组件解读
- PV Controller :负责处理集群中的 PVC/PV 对象,对 PVC/PV 对象进行状态转换,并根据需求进行数据卷的 Provision/Delete 操作(注:Static PV 不会触发 provisioner、Dynamic PV 才会触发 provisioner)
- AD Controller :负责 VolumeAttachement 的生命周期管理,并通过 external-attacher 将设备挂载到目标节点或从目标节点卸载。VolumeAttachement 是控制块存储设备的 Attach/Detach 操作的逻辑对象。(注:可通过 kubelet 配置文件开关控制节点是否由 AD Controller 管理)。
- Kubelet 主要包含与存储相关的两个插件
1) Volume Manager:管理存储卷的 Mount/Unmount 操作、卷设备的格式化等操作(注:如果当前节点并没有交给 AD Controller 管理,那么就是 volumeManager 负责管理 VolumeAttachement 的生命周期)
2)Volume Plugin :K8S 平台为存储提供商提供存储接入的插件接口,其中包含 in-tree 的多种存储插件和 out-tree 的两种存储插件。通过该插件机制进而为容器应用提供各种类型的存储。社区推荐的是 CSI 架构的扩展插件
二、涉及资源解读
- PV: PersistentVolume,集群级别的资源,由集群管理员 or External Provisioner 创建。PV 的生命周期独立于使用 PV 的 Pod,PV 的 .Spec 中保存了存储设备的详细信息。
1 | kind: PersistentVolume |
- PVC: PersistentVolumeClaim,命名空间(namespace)级别的资源,由用户 or StatefulSet 控制器(根据 VolumeClaimTemplate)创建。PVC 类似于 Pod,Pod 消耗 Node 资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存),而 PVC 可以请求特定存储卷的大小及访问模式(Access Mode)。
1 | kind: PersistentVolumeClaim |
- SC: StorageClass 是集群级别的资源,由集群管理员创建。SC 为管理员提供了一种动态提供存储卷的”类”模板,SC 中的 .Spec 中详细定义了存储卷 PV 的不同服务质量级别、备份策略等等。
1 | apiVersion: storage.k8s.io/v1 |
三、涉及 CSI API 对象
- CSINode
- 判断外部 CSI 插件是否注册成功。在 Node Driver Registrar 组件向 Kubelet 注册完毕后,Kubelet 会创建该资源,故不需要显式创建 CSINode 资源
- 将 Kubernetes 中 Node 资源名称与三方存储系统中节点名称(nodeID)一一对应。此处 Kubelet 会调用外部 CSI 插件 NodeServer 的 GetNodeInfo 函数获取 nodeID。
- 显示卷拓扑信息。CSINode 中 topologyKeys 用来表示存储节点的拓扑信息,卷拓扑信息会使得 Scheduler 在 Pod 调度时选择合适的存储节点。
1 | apiVersion: storage.k8s.io/v1 |
- CSIDriver
- 简化外部 CSI 插件的发现。由集群管理员创建,通过 kubectl get csidriver 即可得知环境上有哪些 CSI 插件。
- 自定义 Kubernetes 行为,如一些外部 CSI 插件不需要执行卷挂接(VolumeAttach)操作,则可以设置 .spec.attachRequired 为 false。
1 | apiVersion: storage.k8s.io/v1 |
VolumeAttachment
AD Controller 创建一个 VolumeAttachment,而 External-attacher 则通过观察该 VolumeAttachment,根据其状态属性来进行存储的挂载和卸载操作。
1 | apiVersion: storage.k8s.io/v1 |
四、存储拓展-延迟绑定
Kubernetes 里面有两个绑定:
kube-schedule 将 Pod 和 Node 绑定
PVC Controller 将 PVC 和 PV 绑定。正常情况下,kube-schedule 绑定 Pod 和 Node 时候,如果 Pod 有 PVC,会等待 PVC 和 PV 绑定完成后根据 PV 所在的 az 选择 Node 过滤一部分不满足节点,然后再完成绑定 Pod 和 Node。延迟绑定场景,kube-schedule 先不等待 PVC 和 PV 绑定,先预调度 Node,然后把预调度结果写到 PVC 注解中,PVC 控制器获取到预调度 az 信息后,再完成 PV 创建和 PV 绑定。所以,延迟绑定时延迟了 PVC 和 PV 绑定阶段。
StorageClass 延迟绑定作用字段:VolumeBindingMode
Immediate :表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备(不参与调度)。 对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume 会在不知道 Pod 调度要求的情况下绑定或者制备。
WaitForFirstConsumer :该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。 PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。 这些包括但不限于资源需求、 节点筛选器、 Pod 亲和性和互斥性、 以及污点和容忍度。
