
Kubernetes 应用存储和持久化数据卷:存储快照与拓扑调度
基本知识
存储快照产生背景
1. 如何保证重要数据在误操作之后可以快速恢复,以提高数据操作容错率?
2. 如何能够快速进行复制,迁移重要数据的动作?如进行环境复制与数据开发等。
Kubernetes CSI Snapshotter controller正是为了解决这些需求而设计的。
存储快照用户接口 - snapshot
k8s中通过pvc以及pv的设计体系来简化用户对存储的使用,而存储快照的设计其实是仿照 pvc & pv体系的思想设计。当用户需要存储快照功能时,可以通过 VolumeSnapshot
对象来声明,并指定相应的 VolumeSnapshotClass
。如下对比图所示,动态生成 VolumeSnapshotContent
和动态生成 pv 的流程是非常相似的。
存储快照用户接口 - restore
有了存储快照之后,如何将快照数据快速恢复过来呢?
如上图所示的流程,可以借助PVC对象将其的 dataSource
字段指定为 VolumeSnapshot
对象。这样当 PVC 提交后,会由集群中的相关组件找到 dataSource
所指向的存储快照数据,然后新创建对应的存储以及 pv 对象,将存储快照数据恢复到新的pv中,这样数据就恢复回来了,这就是存储快照的restore用法。
Topolopy-含义
首先了解一下拓扑是什么意思:这里所说的拓扑是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 存储中的使用。
存储拓扑调度产生背景
Kubernetes 中通过PVC&PV 体系将存储与计算分离,即使用不同的Controllers管理存储资源和计算资源。但如果创建的PV有访问 “位置” (.spec.nodeAffinity)的限制,也就是只在特定的一些Nodes上才能访问PV。原有的创建Pod的与创建PV的流程的分离,就无法保证新创建的Pod被调度到可以访问PV的Node上,最终导致Pod无法正常运行起来。
如场景一:Local PV只能在指定的Node上被Pod使用
场景二:单 Region 多 Zone K8s 集群,阿里云云盘当前只能被同一个Zone的Node上的Pod访问
存储拓扑调度
- 本质问题
PV在Binding或者Dynamic Provisioning时,并不知道使用它的Pod被会调度到哪些Node上?但PV本身的访问对Node的 “位置”(拓扑)有限制。- 流程改进
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对象
- 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示例
首先需要集群管理员,在集群中创建 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 之前做的存储快照。
# 创建VolumeSnapshotClass对象
apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
name: disk-snapshotclass
snapshotter: diskplugin.csi.alibabacloud.com
# 指定Volume Snapshot时使用的Volume Plugin
# 创建VolumeSnapshot对象
apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
name: disk-snapshot
spec:
snapshotClassName: disk-snapshotclass
source:
name: disk-pvc # Snapshot的数据源
kind: PersistentVolumeClaim
# 从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示例
Local PV 大部分使用的时候都是通过静态创建的方式,也就是要先去声明PV对象,即然Local PV只能是本地访问,就需要在声明PV对象的,在PV对象中通过nodeAffinity
来限制我中国PV只能在单node
上访问也就是给中国PV加上拓扑限制。如下列代码所示,key
用kubernetes.io/hostname
来做标记,也就是只能在node1
访问。如果想用这个PV,你的pod
必须要调度到node1
上。
# 创建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
即然是静态创建PV的方式,这里为什么还需要 storageClassName
呢?前面也说了,在 Local PV 中,如果要想让它正常工作,需要用到延迟绑定特性才行,那即然是延迟绑定,当用户在写完PVC提交之后,即使集群中有相关的PV能与它匹配,也就是说 PV controller
不能马上去做binding,这个时候你就要通过一种手段来告诉PV controller
,声明情况下是不能立即做binding。这里的storageClass
就是为了起到这个作用,我们可以可以看到 storageClass
里面的 provisioner
指定的是 no-provisioner
,其实就是相当于告诉 k8s 它不会去动态创建 PV,它主要用到 storageclass
的 VolumeBindingMode
字段,叫 WaitForFirstConsumer
,可以先简单地认为他是延迟绑定。
# 创建一个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
当用户开始提交 PVC 的时候,PV Controller
在看到这个pvc的时候,它会找到相应的 storageClass
,发现这个BindingMode
是延迟绑定,它就不会做任何事情。
之后当真正使用这个 pvc 的 pod,在调度的时候,当它恰好调度在符合 PV Nodeaffinity
的 node 的上面后,这个pod里面所使用的PVC才会真正地与PV做绑定,这样就保证我pod调度到这台node上之后,这个PV才与这个PV绑定,最终保证的是创建出来的pod能访问这块Local PV,也就是静态
Provisioning` 场景下怎么去满足PV的拓扑限制。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-pvc
spec:
storageClassName: local-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 60Gi
限制Dynamic Provisioning PV拓扑示例
动态就是指动态创建 PV 就有拓扑位置的限制,那怎么去指定?
首先在storageclass
还是需要指定 BindingMode
,就是 WaitForFirstConsumer
,就是延迟绑定。
其次特别重要的一个字段就是 allowedTopologies
,限制就在这个地方。上图中可用看到拓扑限制是可用区的级别,这里其实有两层意思:
1. 第一层意思就是说我创建PV的时候,创建出来的PV必须是在中国可用区可以访问的;
2. 第二层含义是因为声明的是延迟绑定,那调度器在发现使用它的PVC正好对应的是该 storageclass
的时候,调度 pod 就要选择位于该可用区的nodes。
总之,就是要从两方面保证,一是动态创建出来的存储时要能被中国可用区访问的,二是我调度器在选择node的时候,要落在中国可用区内的,这样的话就保证我的存储和我要使用存储的这个 pod 它所对应的 node,它们之间的拓扑域在同一个拓扑域,用户在写 PVC 文件的时候,写法是跟以前的写法是一样的,主要是在 storageclass
中要做一些拓扑限制。
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
# 当该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

