从零开始入门 K8s | 应用存储和持久化数据卷:存储快照与拓扑调度
一、基本知识
存储快照产生背景
存储快照用户接口-Snapshot
存储快照用户接口-Restore
Topolopy-含义
第一种,在使用云存储服务的时候,经常会遇到 region,也就是地区的概念,在 K8s 中常通过 label failure-domain.beta.kubernetes.io/region 来标识。这个是为了标识单个 K8s 集群管理的跨 region 的 nodes 到底属于哪个地区;
第二种,比较常用的是可用区,也就是 available zone,在 K8s 中常通过 label failure-domain.beta.kubernetes.io/zone 来标识。这个是为了标识单个 K8s 集群管理的跨 zone 的 nodes 到底属于哪个可用区;
第三种,是 hostname,就是单机维度,是拓扑域为 node 范围,在 K8s 中常通过 label kubernetes.io/hostname 来标识,这个在文章最后讲 local pv 的时候,会详细描述。
存储拓扑调度产生背景
存储拓扑调度
首先,如果所要使用的 PV 是预分配的,如 Local PV,使用这块 PV 的 pod 它对应的 PVC 其实还没有做绑定,可以通过调度器在调度的过程中,结合 pod 的计算资源需求(如 cpu/mem) 以及 pod 的 PVC 需求,选择的 node 既要满足计算资源的需求又要 pod 使用的 pvc 要能 binding 的 pv 的 nodeaffinity 限制;
其次对动态生成 PV 的场景相当于是如果知道 pod 运行的 node 之后,就可以根据 node 上记录的拓扑信息来动态的创建这个 PV,也就是保证新创建出来的 PV 的拓扑位置与运行的 node 所在的拓扑位置是一致的,如上面所述的阿里云云盘的例子,既然知道 pod 要运行到可用区 1,那之后创建存储时指定在可用区 1 创建即可。
PV Controller 也就是 persistent volume controller,它需要支持延迟 Binding 这个操作;
另一个是动态生成 PV 的组件,如果 pod 调度结果出来之后,它要根据 pod 的拓扑信息来去动态的创建 PV;
第三组件,也是最重要的一个改动点就是 kube-scheduler。在为 pod 选择 node 节点的时候,它不仅要考虑 pod 对 CPU/MEM 的计算资源的需求,它还要考虑这个 pod 对存储的需求,也就是根据它的 PVC,它要先去看一下当前要选择的 node,能否满足能和这个 PVC 能匹配的 PV 的 nodeAffinity;或者是动态生成 PV 的过程,它要根据 StorageClass 中指定的拓扑限制来 check 当前的 node 是不是满足这个拓扑限制,这样就能保证调度器最终选择出来的 node 就能满足存储本身对拓扑的限制。
二、用例解读
Volume Snapshot/Restore示例
首先需要集群管理员,在集群中创建 VolumeSnapshotClass 对象,其中一个重要字段就是 Snapshot,它是指定真正创建存储快照所使用的卷插件,这个卷插件是需要提前部署的,稍后再说这个卷插件;
接下来用户如果要做真正的存储快照,需要声明一个 VolumeSnapshotClass,首先要指定的是 VolumeSnapshotClassName,接着它要指定的一个非常重要的字段就是 source,这个 source 其实就是指定快照的数据源是啥。
这个地方指定 name 为 disk-pvc,也就是说通过这个 pvc 对象来创建存储快照。提交这个 VolumeSnapshot 对象之后,集群中的相关组件它会找到这个 PVC 对应的 PV 存储,对这个 PV 存储做一次快照。
有了存储快照之后,接下来怎么去用存储快照恢复数据呢?这个其实也很简单,通过声明一个新的 PVC 对象并在它的 spec 下面的 DataSource 中来声明我的数据源来自于哪个 VolumeSnapshot,这里指定的是 disk-snapshot 对象,当我这个 PVC 提交之后,集群中的相关组件,它会动态生成新的 PV 存储,这个新的 PV 存储中的数据就来源于这个 Snapshot 之前做的存储快照。
Local PV 的示例
限制 Dynamic Provisioning PV 拓扑示例
首先在 storageclass 中,还是需要指定 BindingMode,就是 WaitForFirstConsumer,也就是延迟绑定;
其次特别重要的一个字段就是 allowedTopologies,限制就在这个地方。上图中可以看到拓扑限制是可用区的级别,这里其实有两层意思:
第一层意思就是说在动态创建 PV 的时候,创建出来的 PV 必须是在这个可用区可以访问的; 第二层含义是因为声明的是延迟绑定,调度器在发现使用它的 PVC 正好对应的是该 storageclass 的时候,调度 pod 就要选择位于该可用区的 nodes。
三、操作演示
四、处理流程
Kubernetes 对 Volume Snapshot/Restore 处理流程
第一部分是由 K8s 社区推动实现的 csi controller 部分,也就是这里的 csi-snapshottor controller 以及 csi-provisioner controller,这些主要是通用的 controller 部分;
另外一部分是由特定的云存储厂商用自身 OpenAPI 实现的不同的 csi-plugin 部分,也叫存储的 driver 部分。
Kubernetes 对 Volume Topology-aware Scheduling 处理流程
首先用户提交完 pod 之后,会被调度器 watch 到,它就会去首先做预选,预选就是说它会将集群中的所有 node 都来与这个 pod 它需要的资源做匹配;
如果匹配上,就相当于这个 node 可以使用,当然可能不止一个 node 可以使用,最终会选出来一批 node;
然后再经过第二个阶段优选,优选就相当于我要对这些 node 做一个打分的过程,通过打分找到最匹配的一个 node;
之后调度器将调度结果写到 pod 里面的 spec.nodeName 字段里面,然后会被相应的 node 上面的 kubelet watch 到,最后就开始创建 pod 的整个流程。
首先要找到 pod 中使用的所有 PVC,找到已经 bound 的 PVC,以及需要延迟绑定的这些 PVC;
对于已经 bound 的 PVC,要 check 一下它对应的 PV 里面的 nodeAffinity 与当前 node 的拓扑是否匹配 。如果不匹配, 就说明这个 node 不能被调度。如果匹配,继续往下走,就要去看一下需要延迟绑定的 PVC;
对于需要延迟绑定的 PVC。先去获取集群中存量的 PV,满足 PVC 需求的,先把它全部捞出来,然后再将它们一一与当前的 node labels 上的拓扑做匹配。 如果它们(存量的 PV)都不匹配,那就说明当前的存量的 PV 不能满足需求,就要进一步去看一下如果要动态创建 PV 当前 node 是否满足拓扑限制,也就是还要进一步去 check StorageClass 中的拓扑限制,如果 StorageClass 中声明的拓扑限制与当前的 node 上面已经有的 labels 里面的拓扑是相匹配的,那其实这个 node 就可以使用,如果不匹配,说明该 node 就不能被调度。
本文总结
通过对比 PVC & PV 体系讲解了存储快照的相关 K8s 资源对象以及使用方法; 通过两个实际场景遇到的问题引出存储拓扑调度功能必要性,以及 K8s 中如何通过拓扑调度来解决这些问题; 通过剖析 K8s 中存储快照和存储拓扑调度内部运行机制,深入理解该部分功能的工作原理。
系列文章推荐阅读:
☆从零开始入门 K8s | 人人都能看懂 Pod 与容器设计模式
☆从零开始入门 K8s | 应用编批与管理:Job & DaemonSet