k8s-CSI官方文档:
https://github.com/kubernetes-csi/docs
https://github.com/container-storage-interface/spec/blob/master/spec.md
为什么要用CSI
首先,CSI 可以满足不同编排系统的需求,除了k8s,还可以比如 Mesos,Swarm。
其次 ,CSI 是容器化部署,可以减少环境依赖,增强安全性,丰富插件的功能。
k8s挂载volume的过程

CSI简介
CSI 主要包含两个部分:CSI Controller Server 与 CSI Node Server。
- Controller Server 是控制端的功能,主要实现创建、删除、挂载、卸载等功能;
- Node Server 主要实现的是节点上的 mount、Unmount 功能。
CSI Controller Server 和 External CSI SideCar 是通过 Unix Socket 来进行通信的,CSI Node Server 和 Kubelet 也是通过 Unix Socket 来通信。

CSI 的接口主要分为三类:通用管控接口、节点管控接口、中心管控接口。
通用管控接口主要返回 CSI 的一些通用信息,像插件的名字、Driver 的身份信息、插件所提供的能力等;
节点管控接口的 NodeStageVolume 和 NodeUnstageVolume 就相当于 Flexvolume 中的 MountDevice 和 UnmountDevice。NodePublishVolume 和 NodeUnpublishVolume 就相当于 SetUp 和 TearDown 接口;
中心管控接口的 CreateVolume 和 DeleteVolume 就是我们的 Provision 和 Delete 存储卷的一个接口,ControllerPublishVolume 和 ControllerUnPublishVolume 则分别是 Attach 和 Detach 的接口。
CSI 的系统结构
CSI 是通过 CRD 的形式实现的,所以 CSI 引入了这么几个对象类型:VolumeAttachment、CSIDriver、CSINode ,以及 CSI Controller Server 与 CSI Node Server 的一个实现。

在 CSI Controller Server 中,有传统的类似 Kubernetes 中的 AD Controller 和 Volume Plugins,VolumeAttachment 对象就是由它们所创建的。
此外,还包含多个 External Plugin组件,每个组件和 CSI Plugin 组合的时候会完成某种功能。比如:
External Provisioner 和 Controller Server 组合的时候就会完成数据卷的创建与删除功能;
External Attacher 和 Controller Server 组合起来可以执行数据卷的挂载和操作;
External Resizer 和 Controller Server 组合起来可以执行数据卷的扩容操作;
External Snapshotter 和 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对象 |
CSI对象
将介绍 3 种对象:VolumeAttachment,CSIDriver,CSINode。
第一个对象是VolumeAttachment ,它描述一个 Volume 卷在一个 Pod 使用中挂载、卸载的相关信息。例如,对一个卷在某个节点上的挂载,我们通过 VolumeAttachment 对该挂载进行跟踪。AD Controller 创建一个 VolumeAttachment,而 External-attacher 则通过观察该 VolumeAttachment,根据其状态来进行挂载和卸载操作。
第二个对象是 CSIDriver,它描述了集群中所部署的 CSI Plugin 列表,需要管理员根据插件类型进行创建。例如通过 kuberctl get csidriver 我们可以看到集群里面创建的 3 种类型的 CSI Driver:一个是云盘;一个是 NAS;一个是 OSS。
在 CSI Driver 中,我们定义了它的名字,在 spec 中还定义了 attachRequired 和 podInfoOnMount 两个标签。
attachRequired 定义一个 Plugin 是否支持 Attach 功能,主要是为了对块存储和文件存储做区分。比如文件存储不需要 Attach 操作,因此我们将该标签定义为 False;
podInfoOnMount 则是定义 Kubernetes 在调用 Mount 接口时是否带上 Pod 信息。
第三个对象是 CSINode,它是集群中的节点信息,由 node-driver-registrar 在启动时创建。它的作用是每一个新的 CSI Plugin 注册后,都会在 CSINode 列表里添加一个 CSINode 信息。

CSI工作原理
CSI Controller,它是以 deployment 的形式运行在集群里面,主要负责 provision 和 attach 工作。当然 attach 不是每一个存储都会用到的,而 provision 就是在使用 StorageClass 的时候会动态创建 PV 的过程,所以 CSI Controller 在实现 provision 这个功能的时候,是 external-provisioner 这个 SideCar 去配合实现的,在实现 attach 功能的时候是external-attacher 配合它一起完成的。
注意:动态创建pv才会走到provision流程,静态并不会。
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部署
CSI 主要包含两个部分:CSI Controller Server 与 CSI Node Server。分别对应Controller Server Pod和Node Server Pod。
我们只需要部署一个 Controller Server,如果是多备份的,可以部署两个。Controller Server 主要是通过多个外部插件来实现的,比如说一个 Pod 中可以定义多个 External 的 Container 和一个包含 CSI Controller Server 的 Container,这时候不同的 External 组件会和 Controller Server 组成不同的功能。
Node Server Pod 是个 DaemonSet,它会在每个节点上进行注册。Kubelet 会直接通过 Socket 的方式直接和 CSI Node Server 进行通信、调用 Attach/Detach/Mount/Unmount 等。
Driver Registrar 只是做一个注册的功能,会在每个节点上进行部署。

文件存储和块存储的部署情况是类似的。只不过它会把 Attacher 去掉,也没有 VolumeAttachment 对象。

CSI 的其它功能
除了挂载、卸载之外,CSI 化提供了一些附加的功能。例如,在定义模板的时候往往需要一些用户名和密码信息,此时我们就可通过 Secret 来进行定义。之前我们所讲的 Flexvolume 也支持这个功能,只不过 CSI 可以根据不同的阶段定义不同的 Secret 类型,比如挂载阶段的 Secret、Mount 阶段的 Secret、Provision 阶段的 Secret。
Reference
[1] 阿里的公开课
[2] 阿里云开源的csi实现
[3] juicefs-csi-driver的最佳实践