作者: 天翼云 云网产品事业部(弹性存储) 王伟
关键词:docker kubernetes CSI存储插件
简述
目前Kubernetes+docker的组合是我们最常见的容器编排组成,在docker中提供以下三种挂卷方式:
其中bind mount方式一般是挂载宿主机目录到容器中;volume方式一般需要创建一个docker volume,本质上是将Docker服务管理的一块区域(默认是/var/lib/docker/volumes下的文件夹)挂载到容器;而tmpfs是一种非持久化的数据存储,它仅仅将数据保存在宿主机的内存中,一旦容器停止运行,tmpfs mounts会被移除,从而造成数据丢失。
从上面可以看出,docker对卷的管理停留在宿主机的范围,这在容器编排中是远远不够的,比如,在K8S中,我们需要完成持久化卷(一般是网络卷)的创建、删除、附加到节点、从节点分离、挂载到容器、解挂载到容器,而CSI的目标就是更好的完成这些功能。
CSI是容器存储接口(Container Storage Interface)的简写, 旨在能为容器编排引擎CO(Container Orchestrator System)和存储供应商SP(Storage Provider)之间建立一套标准的存储调用接口,从而定义行业标准,使存储供应商(SP)能够开发一个符合CSI标准的插件并使其可以在多个容器编排(CO)系统中工作。也就是说,CSI是一个接口,它约定了一些标准,让所有符合CSI接口的插件在多个容器编排中都可以工作。
CSI的基本知识
CSI的主要功能
我们从CSI的目标中摘出CSI接口提供的主要功能,如下:
-
- 卷的动态创建/删除
- 从节点附加/分离卷
- 从节点挂载/卸载卷
- 快照的创建/删除(快照的来源是卷)
- 从快照创建新卷(不包括恢复快照即原始卷中的数据被擦除并替换为快照中的数据)
RPC接口规定
CSI规定CO 通过 RPC 方式与插件交互。插件分两种类型:
-
- Node Plugin:提供必须在节点上运行的 CSI RPC接口,这些接口一般用于挂载/卸载卷相关的功能。
- Controller Plugin:提供可以在任何地方运行的 CSI RPC接口, 一般用于创建/删除卷相关的的功能。
CSI中规定了三组RPC接口集合:
-
- 身份服务:Node Plugin和Controller Plugin都必须实现这些RPC集。
- 控制器服务:Controller Plugin必须实现这些RPC集。
- 节点服务:Node Plugin必须实现这些RPC集。
CSI插件的常见部署方式
CSI规范中推荐几种部署方式, 以下方式是最常见的,即插件运行在集群中的所有节点上,一个集中的控制插件在 CO master主机上可用,节点插件在所有 CO 节点上可用。
卷的生命周期
CSI中, 一个卷从创建到使用要调用不同的接口,以下是CSI的卷的生命周期中调用接口最全面的一种示意图,有些接口不需要可以不实现。
上图说明了CO 如何通过RPC调用CSI驱动程序实现的API来管理卷的生命周期,完成创建卷到挂载卷以及从卸载卷到删除卷的任务。
CSI在Kubernetes 中的应用
Kubernetes为什么要使用CSI?
在CSI出现以前,K8S存在两种方式的卷插件,一种是in-tree方式,另一种则是Flex Volume方式。这两种方式各有缺点:
In-tree插件意味着它们与核心 K8S代码一起链接、编译、构建和交付,向 K8S(卷插件)添加新的存储系统需要将代码提交到核心K8S代码库,存在很多缺点:
-
- Volume插件开发紧密耦合并依赖于 K8S版本。
- K8S开发人员/社区负责测试和维护所有卷插件,而不仅仅是测试和维护稳定的插件API。
- 卷插件中的错误可能会导致关键的 K8S组件崩溃,而不仅仅是插件。
- 卷插件获得K8S组件(kubelet和kube-controller-manager)的全部权限。
- 插件开发人员被迫提供插件源代码,并且不能选择只发布二进制文件。
由于in-tree插件存在的问题,Flex Volume插件试图通过公开一个基于 exec 的 API 进行挂载/卸载/附加/分离来解决这个问题。尽管它使第三方存储供应商能够编写out-of-tree驱动程序,但它需要访问节点和主机的根文件系统才能部署第三方驱动程序文件。
此外,它没有解决in-tree卷插件的另一个痛点:依赖项。卷插件往往有许多外部需求:例如,对挂载和文件系统工具的依赖,一般底层主机可能无法提供这些依赖,而安装依赖需要直接访问主机。
于是,K8S参与到CSI的制定中,通过使用CSI接口标准,存储供应商可以开发各自的存储插件,而不需要把代码贡献给K8S核心库,对K8S而言,采用 CSI 将具有将卷插件移出核心代码的额外好处,并使卷插件能够被容器化。
Kubernetes CSI插件现状
CSI是从K8S v1.9版本引入的容器存储接口,并于v1.13版本正式GA。K8S实现了in-tree的CSI卷插件, 以及各种Sidecar插件把CSI驱动程序和Kubernetes 的核心代码实现了解耦合, 从而方便存储厂商更加方便的开发CSI驱动程序。目前CSI还在不断的开发和完善中, 导致不同Kubernetes 版本可使用的CSI版本是不同的,在使用时要特别注意。
Kubernetes CSI Sidecar 容器
Kubernetes CSI Sidecar Containers是一组标准容器,旨在简化K8S上CSI驱动程序的开发和部署。这些容器会观察K8S API的通用逻辑,触发针对CSI卷驱动程序的适当操作,并根据需要更新 K8S API。
这些Sidecar容器由K8S存储社区开发和维护,使用时与第三方CSI 驱动程序容器捆绑在一起并作为 pod 部署。Sidecar容器使用是可选的,但社区强烈推荐使用。一般的,各大公有云厂商会选择定制化开发自己的Sidecar容器,从而更好地对接K8S和内部存储。
这些Sidecar容器的好处包括:
-
- 减少“样板”代码。CSI 驱动程序开发人员不必担心复杂的K8S特定代码。
- 关注点分离。K8SAPI 交互的代码与实现 CSI 接口的代码隔离(在不同的容器中)。
K8S开发团队维护以下 Kubernetes CSI Sidecar Containers:
external-attacher |
监视Kubernetes API 服务器中的VolumeAttachment对象并触发Controller[Publish|Unpublish]Volume |
external-provisioner |
监视 Kubernetes API 服务器中的PersistentVolumeClaim对象并触发CreateVolume和DeleteVolume |
external-snapshotter |
监视 Kubernetes API 服务器的VolumeSnapshotContent CRD 对象并调用 CreateSnapshot、DeleteSnapshot 和 ListSnapshots |
external-resizer |
监视 Kubernetes API 服务器对PersistentVolumeClaim对象的编辑并触发ControllerExpandVolume |
node-driver-registrar |
使用kubelet 插件注册机制将其注册到该节点上的kubelet, kubelet 直接针对 CSI 驱动程序发出 CSI NodeGetInfo、NodeStageVolume和NodePublishVolume调用 |
livenessprobe
|
它监视 CSI 驱动程序的健康状况并通过Liveness Probe 机制将其报告给 Kubernetes |
Kubernetes CSI 架构
综上所述,K8S为了摆脱in-tree插件以及不方便安装依赖的缺点,实现了一套复杂的逻辑来解耦CSI驱动程序和核心代码,又自身作为插件的管理者将CSI驱动程序和Sidecar插件一起使用POD(即容器)的方式来部署和管理。如上图所示。
K8S提供资源的定义,如pod、pvc、pv、volumeAttachment等, volumesnapshot和volumesnapshotcontent为CRD资源,K8S各个组件调用in-tree插件进行管理,Sidecar插件监听资源变化并调用CSI的接口执行具体动作。下面详细说明以下各个组件的功能。
K8S的存储主要完成以下三组行为:
-
- provision/delete
- attach/detach(可选)
- mount/unmount
其中,attach/detach又分两种类型,一种是在控制节点的attach/detach, 一种是在容器运行节点的attach/detach,这两类的区别主要是控制节点的attach/detach会完成一些attach/detach的准备工作,节点的attach/detach完成卷在对应节点的attach/detach。
-
- PV Controller: 监听PV,PVC,当监听到这些资源的创建、删除、修改时,PV Controller经过判断是否需要做PV/PVC 的绑定、生命周期管理,并根据需求进行树内插件(非CSI插件)管理数据卷的 Provision/Delete 操作。
- AD Controller: 监听node和pod资源,通过node和pod变更创建/删除VolumeAttachment对象,触发CSI插件在控制节点执行的 Attach/Detach 操作,比如将公有云场景下调用接口将设备挂载到目标节点(可选)。
- Volume Manager: Volume Manager不会监听API Server,在node节点所有的资源监听都是Kubelet完成的,Kubelet会监听到调度到该节点上的pod声明,缓存到Pod Manager中,Volume Manager通过Pod Manager获取PV/PVC的状态,并进行分析出具体的attach/detach, 然后调用plugin调用节点执行的 Attach/Detach 操作(可选),管理卷的 Mount/Unmount 操作、卷设备的格式化以及挂载到一些公用目录上的操作。
- CSI Plugin(outof tree): 它主要是对上面所有功能的具体实现, PV Controller、AD Controller、Volume Manager以及Sidercar插件主要是进行操作的调用,而具体操作则是由 CSI Plugin 实现的。
- Scheduler: 实现对 Pod 的调度能力,会根据一些存储相关的的定义去做一些存储相关的节点的调度。
参考文档
- https://github.com/container-storage-interface/spec/blob/master/spec.md
- https://kubernetes-csi.github.io/docs/