导读: 容器存储是 Kubernetes 系统中提供数据持久化的基础组件,是实现有状态服务的重要保证。Kubernetes 默认提供了主流的存储卷接入方案(In-Tree),同时也提供了插件机制(Out-Of-Tree),允许其他类型的存储服务接入 Kubernetes 系统服务。本文将从 Kubernetes 存储架构、存储插件原理、实现等方面进行讲解,希望大家有所收获。
一、Kubernetes 存储体系架构
引例: 在 Kubernetes 中挂载一个 Volume
首先以一个 Volume 的挂载例子来作为引入。
如下图所示,左边的 YAML 模板定义了一个 StatefulSet 的一个应用,其中定义了一个名为 disk-pvc 的 volume,挂载到 Pod 内部的目录是 /data。disk-pvc 是一个 PVC 类型的数据卷,其中定义了一个 storageClassName。
因此这个模板是一个典型的动态存储的模板。右图是数据卷挂载的过程,主要分为 6 步:
- 第一步 :用户创建一个包含 PVC的 Pod;
- 第二步 :PV Controller 会不断观察 ApiServer,如果它发现一个 PVC 已经创建完毕但仍然是未绑定的状态,它就会试图把一个 PV 和 PVC 绑定;
我们可以在数据卷中添加一些标签,这样使用这个 pv 的 Pod 就会由于标签的限制,被调度器调度到期望的节点上。
- 第四步 :如果有一个 Pod 调度到某个节点之后,它所定义的 PV 还没有被挂载(Attach),此时 AD Controller 就会调用 VolumePlugin,把远端的 Volume 挂载到目标节点中的设备上(如:/dev/vdb);
- 第五步: 当 Volum Manager 发现一个 Pod 调度到自己的节点上并且 Volume 已经完成了挂载,它就会执行 mount 操作,将本地设备(也就是刚才得到的 /dev/vdb)挂载到 Pod 在节点上的一个子目录中。同时它也可能会做一些像格式化、是否挂载到 GlobalPath 等这样的附加操作。
- 第六步 :绑定操作,就是将已经挂载到本地的 Volume 映射到容器中。
Kubernetes 的存储架构
- PV Controller : 负责 PV/PVC 的绑定、生命周期管理,并根据需求进行数据卷的 Provision/Delete 操作;
- AD Controller :负责存储设备的 Attach/Detach 操作,将设备挂载到目标节点;
- Volume Manager :管理卷的 Mount/Unmount 操作、卷设备的格式化以及挂载到一些公用目录上的操作;
- Volume Plugins :它主要是对上面所有挂载功能的实现;
PV Controller、AD Controller、Volume Manager 主要是进行操作的调用,而具体操作则是由 Volume Plugins 实现的。
PV Controller
例如,我们去挂载一个远端的 NAS 的时候,这个 NAS 的具体参数就要定义在 PV 中。一个 PV 是没有 NameSpace 限制的,它一般由 Admin 来创建与维护;
它是用户所使用的存储接口,对存储细节无感知,主要是定义一些基本存储的 Size、AccessMode 参数在里面,并且它是属于某个 NameSpace 内部的。
一个动态存储卷会按照 StorageClass 所定义的模板来创建一个 PV,其中定义了创建模板所需要的一些参数和创建 PV 的一个 Provisioner(就是由谁去创建的)。
下图是一个 PVC 去绑定 PV 时对 PV 筛选的一个流程图。就是说一个 PVC 去绑定一个 PV 的时候,应该选择一个什么样的 PV 进行绑定。
- 首先 它会检查 VolumeMode 这个标签,PV 与 PVC 的 VolumeMode 标签必须相匹配。VolumeMode 主要定义的是我们这个数据卷是文件系统 (FileSystem) 类型还是一个块 (Block) 类型;
- 第二个部分 是 LabelSelector。当 PVC 中定义了 LabelSelector 之后,我们就会选择那些有 Label 并且与 PVC 的 LabelSelector 相匹配的 PV 进行绑定;
- 第三个部分 是 StorageClassName 的检查。如果 PVC 中定义了一个 StorageClassName,则必须有此相同类名的 PV 才可以被筛选中。
一个 PVC 的 Size 必须小于等于 PV 的 Size,这是因为 PVC 是一个声明的 Volume,实际的 Volume 必须要大于等于声明的 Volume,才能进行绑定。
接下来,我们看一个 PV Controller 的一个实现。
PV Controller 中主要有两个实现逻辑:一个是 ClaimWorker;一个是 VolumeWorker。
通过系统标签 "pv.kubernetes.io/bind-completed" 来标识一个 PVC 的状态。
再看 VolumeWorker 的操作。它实现的则是 PV 的状态迁移。
AD Controller
AD Controller 是 Attach/Detach Controller 的一个简称。
它有两个核心对象,即 DesiredStateofWorld 和 ActualStateOfWorld。
它有两个核心逻辑,desiredStateOfWorldPopulator 和 Reconcile。
- desiredStateOfWorldPopulator 主要是用来同步集群的一些数据以及 DSW、ASW 数据的更新,它会把集群里面,比如说我们创建一个新的 PVC、创建一个新的 Pod 的时候,我们会把这些数据的状态同步到 DSW 中;
- Reconcile 则会根据 DSW 和 ASW 对象的状态做状态同步。它会把 ASW 状态变成 DSW 状态,在这个状态的转变过程中,它会去执行 Attach、Detach 等操作。
下面这个表分别给出了 desiredStateOfWorld 以及 actualStateOfWorld 对象的一个具体例子。
- desiredStateOfWorld 会对每一个 Worker 进行定义,包括 Worker 所包含的 Volume 以及一些试图挂载的信息;
- actualStateOfWorl 会把所有的 Volume 进行一次定义,包括每一个 Volume 期望挂载到哪个节点上、挂载的状态是什么样子的等等。
从中我们可以看到,AD Controller 中有很多 Informer,Informer 会把集群中的 Pod 状态、PV 状态、Node 状态、PVC 状态同步到本地。
Volume Manager
这里我们需要讲一下 Attach/Detach 这两个操作:
我们可以看到,最外层是一个循环,内部则是根据不同的对象,包括 desireStateofWorld, actualStateofWorld 的不同对象做一个轮询。
Volume Plugins
根据源码的位置可将 Volume Plugins 分为 In-Tree 和 Out-of-Tree 两类:
- In-Tree 表示源码是放在 Kubernetes 内部的,和 Kubernetes 一起发布、管理与迭代,缺点及时迭代速度慢、灵活性差;
- Out-of-Tree 类的 Volume Plugins 的代码独立于 Kubernetes,它是由存储商提供实现的,目前主要有 Flexvolume 和 CSI 两种实现机制,可以根据存储类型实现不同的存储插件。所以我们比较推崇 Out-of-Tree 这种实现逻辑。
Volume Plugins 的具体实现会放到后面去讲。这里主要看一下 Volume Plugins 的插件管理。
PV Controller、AD Controller 以及 Volume Manager 在启动的时候会执行一个 InitPlugins 方法来对 VolumePluginsMgr 做一些初始化。
当上层调用 refreshProbedPlugins 时,Prober 就会把这些事件进行一个更新,如果是 Create,就将其添加到插件列表;如果是 Remove,就从插件列表中删除一个插件。
Kubernetes 存储卷调度
存储卷具体调度信息的实现可以参考《从零开始入门 K8s | 应用存储和持久化数据卷:存储快照与拓扑调度》,这里会有一个更加详细的介绍。
二、Flexvolume 介绍及使用
在下图中我们可以看到,Volume Plugins 其实包含了一部分 Flexvolume 的实现代码,但这部分代码其实只有一个 “Proxy”的功能。
Flexvolume是可被 Kubelet 驱动的可执行文件,每一次调用相当于执行一次 shell 的 ls 这样的脚本,都是可执行文件的命令行调用,因此它不是一个常驻内存的守护进程。
Flexvolume 的 Stdout 作为 Kubelet 调用的返回结果,这个结果需要是 JSON 格式。
Flexvolume默认的存放地址为 "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/alicloud~disk/disk"。
Flexvolume 的接口介绍
- init : 主要做一些初始化的操作,比如部署插件、更新插件的时候做 init 操作,返回的时候会返回刚才我们所说的 DriveCapabilities 类型的数据结构,用来说明我们的 Flexvolume 插件有哪些功能;
- GetVolumeName : 返回插件名;
- Attach : 挂载功能的实现。根据 --enable-controller-attach-detach 标签来决定是由 AD Controller 还是 Kubelet 来发起挂载操作;
- WaitforAttach : Attach 经常是异步操作,因此需要等待挂载完成,才能需要进行下面的操作;
- MountDevice:它是 mount 的一部分。这里我们将 mount 分为 MountDevice 和 SetUp 两部分,MountDevice 主要做一些简单的预处理工作,比如将设备格式化、挂载到 GlobalMount 目录中等;
- GetPath :获取每个 Pod 对应的本地挂载目录;
- Setup :使用 Bind 方式将 GlobalPath 中的设备挂载到 Pod 的本地目录;
- TearDown 、 UnmountDevice 、 Detach 实现的是上面一些借口的逆过程;
- ExpandVolumeDevice :扩容存储卷,由 Expand Controller 发起调用;
- NodeExpand : 扩容文件系统,由 Kubelet 发起调用。
上面这些接口不一定需要全部实现,如果某个接口没有实现的话,可以将返回结果定义成:
{
"status": "Not supported",
"message": "error message"
}复制代码
在定义 PV 时可以通过 secretRef 字段来定义一些 secret 的功能。比如挂载时所需的用户名和密码,就可以通过 secretRef 传入。
Flexvolume 的挂载分析
从挂载流程和卸载流程两个方向来分析 Flexvolume 的挂载过程。
Flexvolume 的代码示例
Flexvolume 的使用
- driver 定义的是我们实现的某种驱动,比如图中的是 aliclound/disk,也可以是 aliclound/nas 等;
- fsType 定义的是文件系统类型,比如 "ext4";
- options 包含了一些具体的参数,比如定义云盘的 id 等。
我们也可以像其它类型一样,通过 selector 中的 matchLabels 定义一些筛选条件。同样也可以定义一些相应的调度信息,比如定义 zone 为 cn-shenzhen-a。
三、CSI 介绍及使用
和 Flexvolume 类似,CSI 也是为第三方存储提供数据卷实现的抽象接口。
有了 Flexvolume,为何还要 CSI 呢? Flexvolume 只是给 kubernetes 这一个编排系统来使用的,而 CSI 可以满足不同编排系统的需求,比如 Mesos,Swarm。
CSI 主要包含两个部分:CSI Controller Server 与 CSI Node Server。
下图给出了 CSI 的接口。主要分为三类:通用管控接口、节点管控接口、中心管控接口。
- 通用管控接口主要返回 CSI 的一些通用信息,像插件的名字、Driver 的身份信息、插件所提供的能力等;
- 节点管控接口的 NodeStageVolume 和 NodeUnstageVolume 就相当于 Flexvolume 中的 MountDevice 和 UnmountDevice。NodePublishVolume 和 NodeUnpublishVolume 就相当于 SetUp 和 TearDown 接口;
- 中心管控接口的 CreateVolume 和 DeleteVolume 就是我们的 Provision 和 Delete 存储卷的一个接口,ControllerPublishVolume 和 ControllerUnPublishVolume 则分别是 Attach 和 Detach 的接口。
CSI 的系统结构
此外,还包含多个 External Plugin组件,每个组件和 CSI Plugin 组合的时候会完成某种功能。比如:
- External Provisioner 和 Controller Server 组合的时候就会完成数据卷的创建与删除功能;
- External Attacher 和 Controller Server 组合起来可以执行数据卷的挂载和操作;
- External Resizer 和 Controller Server 组合起来可以执行数据卷的扩容操作;
- External Snapshotter 和 Controller Server 组合则可以完成快照的创建和删除。
以上就是 CSI 的整个拓扑结构,接下来我们将分别介绍不同的对象和组件。
CSI 对象
我们将介绍 3 种对象:VolumeAttachment,CSIDriver,CSINode。
status 中 attached 指示了挂载的状态,如果是 False, External-attacher 就会执行一个挂载操作。
第二个对象是 CSIDriver,它描述了集群中所部署的 CSI Plugin 列表,需要管理员根据插件类型进行创建。
在 CSI Driver 中,我们定义了它的名字,在 spec 中还定义了 attachRequired 和 podInfoOnMount 两个标签。
- attachRequired 定义一个 Plugin 是否支持 Attach 功能,主要是为了对块存储和文件存储做区分。比如文件存储不需要 Attach 操作,因此我们将该标签定义为 False;
- podInfoOnMount 则是定义 Kubernetes 在调用 Mount 接口时是否带上 Pod 信息。
CSI 组件之 Node-Driver-Registrar
Node-Driver-Registrar 主要实现了 CSI Plugin 注册的一个机制。我们来看一下下图中的流程图。
- 第 2 步 ,Node-Driver-Registrar 会监听 GetInfo 和 NotifyRegistrationStatus 两个接口;
- 第 3 步 ,会在 /var/lib/kuberlet/plugins_registry 这个目录下启动一个 Socket,生成一个 Socket 文件 ,例如:"diskplugin.csi.alibabacloud.com-reg.sock",此时 Kubelet 通过 Watcher 发现这个 Socket 后,它会通过该 Socket 向 Node-Driver-Registrar 的 GetInfo 接口进行调用。GetInfo 会把刚才我们所获得的的 CSI-Plugin 的信息返回给 Kubelet,该信息包含了 CSI-Plugin 的监听地址以及它的 Driver name;
- 第 4 步 ,Kubelet 通过得到的监听地址对 CSI-Plugin 的 NodeGetInfo 接口进行调用;
- 第 5 步 ,调用成功之后,Kubelet 会去更新一些状态信息,比如节点的 Annotations、Labels、status.allocatable 等信息,同时会创建一个 CSINode 对象;
- 第 6 步 ,通过对 Node-Driver-Registrar 的 NotifyRegistrationStatus 接口的调用告诉它我们已经把 CSI-Plugin 注册成功了。
CSI 组件之 External-Attacher
CSI 部署
之前提到 CSI 的 Controller 分为两部分,一个是 Controller Server Pod,一个是 Node Server Pod。
Driver Registrar 只是做一个注册的功能,会在每个节点上进行部署。
文件存储和块存储的部署情况是类似的。只不过它会把 Attacher 去掉,也没有 VolumeAttachment 对象。
CSI 使用示例
可以看到,它和其它的定义并没什么区别。主要的区别在于类型为 CSI,里面会定义 driver,volumeHandle,volumeAttribute,nodeAffinity 等。
- driver 就是定义是由哪一个插件来去实现挂载;
- volumeHandle 主要是指示 PV 的唯一标签;
- volumeAttribute 用于附加参数,比如 PV 如果定义的是 OSS,那么就可以在 volumeAttribute 定义 bucket、访问的地址等信息在里面;
- nodeAffinity 则可以定义一些调度信息。与 Flexvolume 类似,还可以通过 selector 和 Label 定义一些绑定条件。
中间的图给出了一个动态调度的例子,它和其它类型的动态调度是一样的。只不过在定义 provisioner 的时候指定了一个 CSI 的 provisioner。
CSI 的其它功能
Skip Attach 和 PodInfo On Mount 是刚才我们所讲过的 CSI Driver 中的两个功能。
CSI 的近期 Features
阿里云在 GitHub 上开源了 CSI 的实现,大家有兴趣的可以看一下,做一些参考。
四、本文总结
本文主要介绍了 Kubernetes 集群中存储卷相关的知识,主要有以下三点内容:
- 第一部分讲述了 Kubernetes 存储架构,主要包括存储卷概念、挂载流程、系统组件等相关知识;
- 第二部分讲述了 Flexvolume 插件的实现原理、部署架构、使用示例等;
- 第三部分讲述了 CSI 插件的实现原理、资源对象、功能组件、使用示例等;
希望上述知识点能让各位同学有所收获,特别是在处理存储卷相关的设计、开发、故障处理等方面有所帮助。
查看更多:https://yq.aliyun.com/articles/743613?utm_content=g_1000103097
上云就看云栖号:更多云资讯,上云案例,最佳实践,产品入门,访问:https://yqh.aliyun.com/