首页
视频
资源
登录
原
Kubernetes 应用存储和持久化数据卷:存储快照与拓扑调度
4723
人阅读
2021/3/18 16:01
总访问:
2592859
评论:
0
收藏:
0
手机
分类:
容器编排
![Kubernetes](https://img.tnblog.net/arcimg/hb/274ef6f115de4c9b8e40ded5deb2f4c2.jpg "Kubernetes") >#Kubernetes 应用存储和持久化数据卷:存储快照与拓扑调度 [TOC] 基本知识 ------------ ### 存储快照产生背景 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 1. 如何保证重要数据在误操作之后可以快速恢复,以提高数据操作容错率? 2. 如何能够快速进行复制,迁移重要数据的动作?如进行环境复制与数据开发等。 Kubernetes CSI Snapshotter controller正是为了解决这些需求而设计的。 </p> ### 存储快照用户接口 - snapshot ![](https://img.tnblog.net/arcimg/hb/02fb4843f47143478dd8f94fcfd9d508.png) <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> k8s中通过pvc以及pv的设计体系来简化用户对存储的使用,而存储快照的设计其实是仿照 pvc & pv体系的思想设计。当用户需要存储快照功能时,可以通过 `VolumeSnapshot` 对象来声明,并指定相应的 `VolumeSnapshotClass`。如下对比图所示,动态生成 `VolumeSnapshotContent` 和动态生成 pv 的流程是非常相似的。 </p> ### 存储快照用户接口 - restore ![](https://img.tnblog.net/arcimg/hb/943942038d724570aaf828d2304f1964.png) <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 有了存储快照之后,如何将快照数据快速恢复过来呢? 如上图所示的流程,可以借助PVC对象将其的 `dataSource` 字段指定为 `VolumeSnapshot`对象。这样当 PVC 提交后,会由集群中的相关组件找到 `dataSource` 所指向的存储快照数据,然后新创建对应的存储以及 pv 对象,将存储快照数据恢复到新的pv中,这样数据就恢复回来了,这就是存储快照的restore用法。 </p> ### Topolopy-含义 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 首先了解一下拓扑是什么意思:这里所说的拓扑是K8s集群中为管理的nodes划分的一种"位置"关系,意思为:可以通过在node的labels信息里面填写某一个node属于某一个拓扑。 常见的有三种,这三种在使用时经常会遇到的: - 第一种,在使用云存储访问的时候,经常会遇到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 的时候,会再详细描述。 上面讲到的三个拓扑是比较常用的,而拓扑其实是可以自己定义的。可以定义一个字符串来标识一个拓扑域,这个 key 所对应的值其实就是拓扑域下不同的拓扑位置。 举个例子:可以以机房中的机架这个纬度来做一个拓扑域。这样就可以将不同机架(rack)上面的机器标记为不同的拓扑位置,也就是说可以将不同机架上机器的位置关系通过rack这个纬度来标识。属于 rack1 上的机器,node label 中都添加 rack 的标识,它的value就标识成 rack1,即 rack=rack1;另外一组机架上的机器可以标识为rack=rack2,这样就可以通过机架的纬度就来区分来k8s中的node所处的位置。 接下来就一起来看看拓扑再 K8s 存储中的使用。 </p> ### 存储拓扑调度产生背景 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> Kubernetes 中通过PVC&PV 体系将存储与计算分离,即使用不同的Controllers管理存储资源和计算资源。但如果创建的PV有访问 "位置" (.spec.nodeAffinity)的限制,也就是只在特定的一些Nodes上才能访问PV。原有的创建Pod的与创建PV的流程的分离,就无法保证新创建的Pod被调度到可以访问PV的Node上,最终导致Pod无法正常运行起来。 如场景一:Local PV只能在指定的Node上被Pod使用 ![](https://img.tnblog.net/arcimg/hb/b44b4a2e1199470dad7a5f767b5178cf.png) 场景二:单 Region 多 Zone K8s 集群,阿里云云盘当前只能被同一个Zone的Node上的Pod访问 ![](https://img.tnblog.net/arcimg/hb/59dd360e1ff64d76ac96c3be554aef2e.png) </p> ### 存储拓扑调度 >1. 本质问题 PV在Binding或者Dynamic Provisioning时,并不知道使用它的Pod被会调度到哪些Node上?但PV本身的访问对Node的 "位置"(拓扑)有限制。 2. 流程改进 Binding/Dynamic Provisioning PV的操作 Delay 到 Pod 调度结果确定之后做,好处: - 对于pre-provisioned 的含有 Node Affinity的PV对象,可以在Pod运行的Node确认之后,根据Node找到合适的PV对象,然后与Pod中使用的PVC Binding,保证Pod运行的Node满足PV对访问 "位置"(拓扑)的要求。 - 对于dynamic provisioning PV 场景,在Pod运行的Node确认之后,在Pod运行的Node确认之后,可以结合Node的 "位置"(拓扑)信息创建可被该 Node 访问的PV对象 3. Kubernetes 相关组件改进 - PV Controller:支持延迟Binding操作 - Dynamic PV Provisioner:动态创建PV时要结合Pod待运行的Node的 "位置"(拓扑)信息 - Scheduler:选择Node时要考虑Pod的PVC Binding需求,也就是要结合 pre-provisioned 的 PV 的 Node Affinity 以及 dynamic provisioning 时 PVC 指定 StorageClass.AllowedTopologies的限制 ### Volume Snapshot/Restore示例 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 首先需要集群管理员,在集群中创建 VolumeSnapshotClass 对象,VolumeSnapshotClass 中一个重要字段就是 Snapshot,它是指定真正创建存储快照所使用的卷插件,这个卷插件是需要提前部署的,稍后再说这个卷插件。 接下来用户他如果要做真正的存储快照,需要声明一个 VolumeSnapshotClass,VolumeSnapshotClass 首先它要指定的是 VolumeSnapshotClassName,接着它要指定的一个非常重要的字段就是 source,这个 source 其实就是指定快照的数据源是啥。这个地方指定 name 为 disk-pvc,也就是说通过这个 pvc 对象来创建存储快照。提交这个 VolumeSnapshot 对象之后,集群中的相关组件它会找到这个 PVC 对应的 PV 存储,对这个 PV 存储做一次快照。 有了存储快照之后,那接下来怎么去用存储快照恢复数据呢?这个其实也很简单,通过声明一个新的 PVC 对象并在它的 spec 下面的 DataSource 中来声明我的数据源来自于哪个 VolumeSnapshot,这里指定的是 disk-snapshot 对象,当我这个 PVC 提交之后,集群中的相关组件,它会动态生成新的 PV 存储,这个新的 PV 存储中的数据就来源于这个 Snapshot 之前做的存储快照。 </p> ```yaml # 创建VolumeSnapshotClass对象 apiVersion: snapshot.storage.k8s.io/v1alpha1 kind: VolumeSnapshotClass metadata: name: disk-snapshotclass snapshotter: diskplugin.csi.alibabacloud.com # 指定Volume Snapshot时使用的Volume Plugin ``` ```yaml # 创建VolumeSnapshot对象 apiVersion: snapshot.storage.k8s.io/v1alpha1 kind: VolumeSnapshot metadata: name: disk-snapshot spec: snapshotClassName: disk-snapshotclass source: name: disk-pvc # Snapshot的数据源 kind: PersistentVolumeClaim ``` ```yaml # 从snapshot中恢复数据到新生成的PV对象中 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: restore-pvc spec: dataSource: name: disk-snapshot kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io accessModes: - ReadWriteOnce resources: requests: storage: 20Gi storageClassName: csi-disk ``` ### Local PV示例 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> Local PV 大部分使用的时候都是通过静态创建的方式,也就是要先去声明PV对象,即然Local PV只能是本地访问,就需要在声明PV对象的,在PV对象中通过`nodeAffinity`来限制我中国PV只能在单`node`上访问也就是给中国PV加上拓扑限制。如下列代码所示,`key`用`kubernetes.io/hostname`来做标记,也就是只能在`node1`访问。如果想用这个PV,你的`pod`必须要调度到`node1`上。 </p> ```yaml # 创建Local PV对象 apiVersion: v1 kind: PersistentVolume metadata: name: local-pv spec: capacity: storage: 60Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage local: path: /share nodeAffinity: # 限制该PV只能再node1上被使用 required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node1 ``` <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 即然是静态创建PV的方式,这里为什么还需要 `storageClassName` 呢?前面也说了,在 Local PV 中,如果要想让它正常工作,需要用到延迟绑定特性才行,那即然是延迟绑定,当用户在写完PVC提交之后,即使集群中有相关的PV能与它匹配,也就是说 `PV controller` 不能马上去做binding,这个时候你就要通过一种手段来告诉`PV controller`,声明情况下是不能立即做binding。这里的`storageClass`就是为了起到这个作用,我们可以可以看到 `storageClass` 里面的 `provisioner` 指定的是 `no-provisioner`,其实就是相当于告诉 k8s 它不会去动态创建 PV,它主要用到 `storageclass` 的 `VolumeBindingMode` 字段,叫 `WaitForFirstConsumer`,可以先简单地认为他是延迟绑定。 </p> ```yaml # 创建一个no-provisioner StorageClass对象,目的是告诉PV # Controller遇到 .spec.storageClassName 为 local-storage的 # PVC暂不做Binding对象 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer # deley binding ``` <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 当用户开始提交 PVC 的时候,`PV Controller`在看到这个pvc的时候,它会找到相应的 `storageClass`,发现这个`BindingMode`是延迟绑定,它就不会做任何事情。 之后当真正使用这个 pvc 的 pod,在调度的时候,当它恰好调度在符合 `PV Nodeaffinity` 的 node 的上面后,这个pod里面所使用的PVC才会真正地与PV做绑定,这样就保证我pod调度到这台node上之后,这个PV`才与这个PV绑定,最终保证的是创建出来的pod能访问这块Local PV,也就是静态 `Provisioning` 场景下怎么去满足PV的拓扑限制。 </p> ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: local-pvc spec: storageClassName: local-storage accessModes: - ReadWriteOnce resources: requests: storage: 60Gi ``` ### 限制Dynamic Provisioning PV拓扑示例 <p style=" font-weight: 400; line-height: 1.5; color: #212529; -webkit-tap-highlight-color: transparent; box-sizing: border-box; padding: 0px 20px 20px 20px; border: 1px solid #e9ecef; border-left-width: .25rem; border-radius: .25rem; display: block; border-left-color: #5bc0de;"> 动态就是指动态创建 PV 就有拓扑位置的限制,那怎么去指定? 首先在`storageclass`还是需要指定 `BindingMode`,就是 `WaitForFirstConsumer`,就是延迟绑定。 其次特别重要的一个字段就是 `allowedTopologies`,限制就在这个地方。上图中可用看到拓扑限制是可用区的级别,这里其实有两层意思: 1. 第一层意思就是说我创建PV的时候,创建出来的PV必须是在中国可用区可以访问的; 2. 第二层含义是因为声明的是延迟绑定,那调度器在发现使用它的PVC正好对应的是该 `storageclass`的时候,调度 pod 就要选择位于该可用区的nodes。 总之,就是要从两方面保证,一是动态创建出来的存储时要能被中国可用区访问的,二是我调度器在选择node的时候,要落在中国可用区内的,这样的话就保证我的存储和我要使用存储的这个 pod 它所对应的 node,它们之间的拓扑域在同一个拓扑域,用户在写 PVC 文件的时候,写法是跟以前的写法是一样的,主要是在 `storageclass` 中要做一些拓扑限制。 </p> ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: csi-disk provisioner: diskplugin.csi.alibabacloud.com parameters: regionId: cn-hangzhou fsType: ext4 type: cloud_ssd volumeBindingMode: WaitForFirstConsumer allowedTopologies: - matchLabelExpressions: # 拓扑域限制: 动态创建的PV只能在可用区 cn-hangzhou-d被使用 - key: topology.diskplugin.csi.alibabacloud.com/zone values: - cn-hangzhou-d reclaimPolicy: Delete ``` ```yaml # 当该PVC对象被创建之后由于对应StorageClass的BindingMode为 # WaitForFirstConsumer并不会马上动态生成PV对象,耳塞要等到使用 # 该PVC对象的第一个Pod调度结果出来之后,而且kube-scheduler在调度 # Pod的时候去选择满足StorageClass.allowedTopologies中指定的 # 拓扑限制的Nodes apiVersion: v1 kind: PersistentVolumeClaim metadata: name: disk-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 30Gi storageClassName: csi-disk ```
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739
👈{{preArticle.title}}
👉{{nextArticle.title}}
评价
{{titleitem}}
{{titleitem}}
{{item.content}}
{{titleitem}}
{{titleitem}}
{{item.content}}
尘叶心繁
这一世以无限游戏为使命!
博主信息
排名
6
文章
6
粉丝
16
评论
8
文章类别
.net后台框架
168篇
linux
17篇
linux中cve
1篇
windows中cve
0篇
资源分享
10篇
Win32
3篇
前端
28篇
传说中的c
4篇
Xamarin
9篇
docker
15篇
容器编排
101篇
grpc
4篇
Go
15篇
yaml模板
1篇
理论
2篇
更多
Sqlserver
4篇
云产品
39篇
git
3篇
Unity
1篇
考证
2篇
RabbitMq
23篇
Harbor
1篇
Ansible
8篇
Jenkins
17篇
Vue
1篇
Ids4
18篇
istio
1篇
架构
2篇
网络
7篇
windbg
4篇
AI
18篇
threejs
2篇
人物
1篇
嵌入式
2篇
python
13篇
HuggingFace
8篇
pytorch
9篇
opencv
6篇
最新文章
最新评价
{{item.articleTitle}}
{{item.blogName}}
:
{{item.content}}
关于我们
ICP备案 :
渝ICP备18016597号-1
网站信息:
2018-2024
TNBLOG.NET
技术交流:
群号656732739
联系我们:
contact@tnblog.net
欢迎加群
欢迎加群交流技术