简介
拿存储这件事来说,开发人员到底关注什么呢?围绕存储这个概念,我们可以说出一堆名词,块设备、文件系统、对象存储、本地磁盘、磁盘阵列、NFS、Ceph等等。这些名词毋庸置疑都与存储相关,也的确会被各种业务系统所使用,但我相信,绝大多数的开发人员对这些名词并不关心。
- 作为用户,开发人员只关心一件事情,我所负责的业务系统,指定目录中的数据需要被持久化的保存下来。
- 使用复杂存储的场景更多见于业务系统所调用的各种中间件中,比如数据库需要高速稳定的块设备类型存储,再比如大数据领域的“存算分离”场景下对接对象存储等等。然而在大多数场景下,这些复杂中间件的维护并不是开发人员应该关心的事情。它们由专门的运维人员进行维护,开发人员只需要得到访问它们的地址即可。
如何接入 K8s 持久化存储?K8s CSI 实现机制浅析 未读
浅谈如何实现一个 CSI 插件 未读。
Kubernetes存储方案发展过程概述
- 最开始是通过Volume Plugin实现集成外部存储系统,即不同的存储系统对应不同的Volume Plugin。Volume Plugin实现代码全都放在了Kubernetes主干代码中(in-tree),也就是说这些插件与核心Kubernetes二进制文件一起链接、编译、构建和发布。
- 从1.8开始,Kubernetes停止往Kubernetes代码中增加新的存储支持。从1.2开始,推出了一种新的插件形式支持外部存储系统,即FlexVolume。FlexVolume类似于CNI插件,通过外部脚本集成外部存储接口,这些脚本默认放在
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/
,需要安装到所有Node节点上。它是只针对 Kubernetes 的私有的存储扩展,目前已经处于冻结状态,可以正常使用但不再发展新功能了。 - 从1.9开始又引入了Container Storage Interface(CSI)容器存储接口,CSI 是公开的技术规范,是目前 Kubernetes 重点发展的扩展机制。
与容器服务 ACK 发行版的深度对话最终弹:如何通过 open-local 玩转容器本地存储
CSI规范
CSI 规范可以分为需要容器系统去实现的组件,以及需要存储提供商去实现的组件两大部分。
- 前者包括了存储整体架构、Volume 的生命周期模型、驱动注册、Volume 创建、挂载、扩容、快照、度量等内容,这些 Kubernetes 都已经完整地实现了,大体上包括以下几个组件:
- Driver Register:负责注册第三方插件,CSI 0.3 版本之后已经处于 Deprecated 状态,将会被Node Driver Register所取代。
- External Provisioner:调用第三方插件的接口来完成数据卷的创建与删除功能。
- External Attacher:调用第三方插件的接口来完成数据卷的挂载和操作。
- External Resizer:调用第三方插件的接口来完成数据卷的扩容操作。
- External Snapshotter:调用第三方插件的接口来完成快照的创建和删除。
- External Health Monitor:调用第三方插件的接口来提供度量监控数据。
- 后者定义了外部存储挂载到容器过程中所涉及操作的抽象接口和具体的通讯方式,主要包括以下三个 gRPC 接口:
- CSI Identity 接口:用于描述插件的基本信息,比如插件版本号、插件所支持的 CSI 规范版本、插件是否支持存储卷创建、删除功能、是否支持存储卷挂载功能等等。
- CSI Controller 接口:用于从存储系统的角度对存储资源进行管理,比如准备和移除存储(Provision、Delete 操作)、附加与分离存储(Attach、Detach 操作)、对存储进行快照等等。存储插件并不一定要实现这个接口的所有方法,对于存储本身就不支持的功能,可以在 CSI Identity 接口中声明为不提供。
- CSI Node 接口:用于从集群节点的角度对存储资源进行操作,比如存储卷的分区和格式化、将存储卷挂载到指定目录上,或者将存储卷从指定目录上卸载,等等。
CSI 插件本身是由一组标准的 Kubernetes 资源所构成,CSI Controller 接口是一个以 StatefulSet 方式部署的 gRPC 服务,CSI Node 接口则是基于 DaemonSet 方式部署的 gRPC 服务。
整体设计
CSI 包括CSI Controller 和 CSI Node。 PS:传统的master + agent 工作模式,master 掌握所有信息,做出决策, agent 根据决策执行落地
- CSI Controller的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。
- CSI Node主要功能是对主机(Node)上的Volume进行管理和操作。PS:可以猜测:pod声明了pvc,scheduler 为pod调度了node后,对应node 上的csi node 组件开始mount nfs目录到某个本地目录,创建pod后,再bind mount 到容器内。
CSI 插件体系的设计思想,就是把Dynamic Provision 阶段以及 Kubernetes 里的一部分存储管理功能(比如“Attach 阶段”和“Mount 阶段”,实际上是通过调用 CSI 插件来完成的),从主干代码里剥离出来,做成了几个单独的组件。这些组件会通过 Watch API 监听 Kubernetes 里与存储相关的事件变化,比如 PVC 的创建,来执行具体的存储管理动作。
浅析 CSI 工作原理CSI 的设计思想,把插件的职责从“两阶段处理”,扩展成了Provision、Attach 和 Mount 三个阶段。不过不是每个存储方案都会经历这三个阶段,比如 NFS 就没有 Attach/Detach 阶段。其中
- Provision 等价于“创建磁盘”。它的逆操作是移除(Delete)存储。
- 当 PVController watch 到集群中有 PVC 创建时,会判断当前是否有 in-tree plugin 与之相符,如果没有则判断其存储类型为 out-of-tree 类型,于是给 PVC 打上注解 volume.beta.kubernetes.io/storage-provisioner={csi driver name};
- 当 extenal-provisioner watch 到 PVC 的注解 csi driver 与自己的 csi driver 一致时,调用 CSI Controller 的 CreateVolume 接口;
- 当 CSI Controller 的 CreateVolume 接口返回成功时,extenal-provisioner 会在集群中创建对应的 PV;
- PVController watch 到集群中有 PV 创建时,将 PV 与 PVC 进行绑定。
- Attach 等价于“挂载磁盘到虚拟机”,此时尽管设备还不能使用,但你已经可以用操作系统的
fdisk -l
命令查看到设备。它的逆操作是分离(Detach)存储设备。- ADController 监听到 pod 被调度到某节点,并且使用的是 CSI 类型的 PV,会调用内部的 in-tree CSI 插件的接口,该接口会在集群中创建一个 VolumeAttachment 资源;
- external-attacher 组件 watch 到有 VolumeAttachment 资源创建出来时,会调用 CSI Controller 的 ControllerPublishVolume 接口;
- 当 CSI Controller 的 ControllerPublishVolume 接口调用成功后,external-attacher 将对应的 VolumeAttachment 对象的 Attached 状态设为 true;
- ADController watch 到 VolumeAttachment 对象的 Attached 状态为 true 时,更新 ADController 内部的状态 ActualStateOfWorld。
- Mount 等价于“将该磁盘格式化后,挂载在 Volume 的宿主机目录上”,也就是操作系统中mount命令的作用。它的逆操作是卸载(Unmount)存储设备。
- kubelet 在创建 pod 的过程中,会调用 CSI Node 插件,执行 mount 操作