#yyds干货盘点# Kubernetes 怎样对业务数据进行持久化存储?(10)
Posted wzlinux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# Kubernetes 怎样对业务数据进行持久化存储?(10)相关的知识,希望对你有一定的参考价值。
通过前面的讲解,我们知道了如何在 Pod 中使用 Volume 来保存数据。Volume 跟 Pod 的生命周期是绑定的,当 Pod 被删除后,Volume 中的数据有可能会一同被删除,具体需要看对应的 volume plugin 的使用要求,你可以看上节的对比表格。
存储需求
而这里我们还需要考虑如下几个问题。
- 共享 Volume。目前 Pod 内的 Volume 其实跟 Pod 是存在静态的一一绑定关系,即生命周期绑定。这导致不同 Pod 之间无法共享 Volume。
- 复用 Volume 中的数据。当 Pod 由于某种原因失败,被工作负载控制器删除重新创建后,我们需要能够复用 Volume 中的旧数据。
- Volume 自身的一些强关联诉求。对于有状态工作负载 StatefulSet 来说,当其管理的 Pod 由于所在的宿主机出现一些硬件或软件问题,比如磁盘损坏、kernel 异常等,Pod 重新“长”到别的节点上,这时该如何保证 Volume 和 Pod 之间强关联的关系?
- Volume 功能及语义扩展,比如容量大小、标签信息、扩缩容等。
为此我们在 Kubernetes 中引入了一个专门的对象 Persistent Volume(简称 PV),将计算和存储进行分离,可以使用不同的控制器来分别管理。
同时通过 PV,我们也可以和 Pod 自身的生命周期进行解耦。一个 PV 可以被几个 Pod 同时使用,即使 Pod 被删除后,PV 这个对象依然存在,其他新的 Pod 依然可以复用。为了更好地描述这种关联绑定关系,易于使用,并且屏蔽更多用户并不关心的细节参数(比如 PV 由谁提供、创建在哪个 zone/region、怎么去访问到,等等),我们通过一个抽象对象 Persistent Volume Claim(PVC)来使用 PV。
我们可以把 PV 理解成是对实际的物理存储资源描述,PVC 是便于使用的抽象 API。在 Kubernetes 中,我们都是在 Pod 中通过PVC 的方式来使用 PV 的,见下图。
在 Kubernetes 中,创建 PV(PV Provision) 有两种方式,即静态和动态,如下图所示。
静态 PV
我们先来看静态 PV(Static PV),管理员通过手动的方式在后端存储平台上创建好对应的 Volume,然后通过 PV 定义到 Kubernetes 中去。开发者通过 PVC 来使用。我们来看个 HostPath 类型的 PV 例子:
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume # pv 的名字
labels: # pv 的一些label
type: local
spec:
storageClassName: manual
capacity: # 该 pv 的容量
storage: 10Gi
accessModes: # 该 pv 的接入模式
- ReadWriteOnce
hostPath: # 该 pv 使用的 hostpath 类型,还支持通过 CSI 接入其他 plugin
path: "/mnt/data"
这里,我们定义了一个名为task-pv-volume的 PV,PV 是集群的资源,并不属于某个 namespace。其中storageClassName这个字段是某个StorageClass对象的名字。我们会在下一段动态 PV 中讲解StorageClass的作用。
对于每一个 PV,我们都要为其设置存储能力,目前只支持对存储空间的设置,比如我们这里设置了 10G 的空间大小。未来社区还会加入其他的配置,诸如 IOPS(Input/Output Operations Per Second,每秒输入输出次数)、吞吐量等。
这里头accessMode可以指定该 PV 的几种访问挂载方式:
- ReadWriteOnce(RWO)表示该卷只可以以读写方式挂载到一个 Pod 内;
- ReadOnlyMany( ROX)表示该卷可以挂载到多个节点上,并被多个 Pod 以只读方式挂载;
- ReadWriteMany(RWX)表示卷可以被多个节点以读写方式挂载供多个 Pod 同时使用。
注意一个 PV 只能有一种访问挂载模式。不同的 volume plugin 支持的 accessMode 并不相同,在使用的时候,你可以参照官方的这个表格进行选择。
我们创建好后查看这个 PV:
$ kubectl get pv task-pv-volume
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual 4s
可以看到,这个 PV 的状态为Available(可用)。
这里我们还看到上面kubectl get
的输出里面有个 ReclaimPolicy 字段,该字段表明对 PV 的回收策略,默认是 Retain,即 PV 使用完后数据保留,需要由管理员手动清理数据。除了 Retain 外,还支持如下策略:
- Recycle,即回收,这个时候会清除 PV 中的数据;
- Delete,即删除,这个策略常在云服务商的存储服务中使用到,比如 AWS EBS。
下面我们再创建一个 PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
namespace: dmeo
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
创建好了以后,Kubernetes 会为 PVC 匹配满足条件的 PV。我们在 PVC 里面指定storageClassName为 manua,这个时候就只会去匹配storageClassName同样为 manual 的 PV。一旦发现合适的 PV 后,就可以绑定到该 PV 上。
PVC 是 namespace 级别的资源,我们来创建看看:
$ kubectl get pvc -n demo
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
task-pv-claim Bound task-pv-volume 10Gi RWO manual 9s
我们可以看到 这个 PVC 已经和我们上面的 PV 绑定起来了。我们再来查看下task-pv-volume这个 PV 对象,可以看到它的状态也从Available变成了 Bound。
$ kubectl get pv task-pv-volume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual 2m12s
PV 一般会有如下五种状态:
- Pending 表示目前该 PV 在后端存储系统中还没创建完成;
- Available 即闲置可用状态,这个时候还没有被绑定到任何 PVC 上;
- Bound 就像上面例子里似的,这个时候已经绑定到某个 PVC 上了;
- Released 表示已经绑定的 PVC 已经被删掉了,但资源还未被回收掉;
- Failed 表示回收失败。
同样,对于 PVC 来说,也有如下三种状态:
- Pending 表示还未绑定任何 PV;
- Bound 表示已经和某个 PV 进行了绑定;
- Lost 表示关联的 PV 失联。
下面我们来看看,如何在 Pod 中使用静态的 PV。看如下的例子:
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
namespace: demo
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx:1.14.2
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
创建完成以后:
$ kubectl get pod task-pv-pod -n demo
NAME READY STATUS RESTARTS AGE
task-pv-pod 1/1 Running 1 82s
$ kubectl exec -it task-pv-pod -n demo -- /bin/bash
root@task-pv-pod:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 40G 5.0G 33G 14% /
tmpfs 64M 0 64M 0% /dev
tmpfs 996M 0 996M 0% /sys/fs/cgroup
/dev/vda1 40G 5.0G 33G 14% /etc/hosts
shm 64M 0 64M 0% /dev/shm
overlay 996M 4.0M 992M 1% /usr/share/nginx/html
tmpfs 996M 12K 996M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 996M 0 996M 0% /proc/acpi
tmpfs 996M 0 996M 0% /sys/firmware
可以看到,PV 已经正确挂载到 Pod 内。
静态 PV 最大的问题就是使用起来不够方便,都是管理员提前创建好一批指定规格的 PV,无法做到按需创建。使用过程中,经常会遇到由于资源大小不匹配,规格不对等,造成 PVC 无法绑定 PV 的情况。同时还会造成资源浪费,比如一个只需要 1G 空间的 Pod,绑定了 10G 的 PV。
这些问题,都可以通过动态 PV 来解决。
动态 PV
要想动态创建 PV,我们需要一些参数来帮助我们创建 PV。这里我们用 StorageClass 这个对象来描述,你可以在 Kubernetes 中定义很多的 StorageClass,如下就是一个 Storage 的定义例子:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-rbd-sc
annotation:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/rbd # 必填项,用来指定volume plugin来创建PV的物理资源
parameters: # 一些参数
monitors: 10.16.153.105:6789
adminId: kube
adminSecretName: ceph-secret
adminSecretNamespace: kube-system
pool: kube
userId: kube
userSecretName: ceph-secret-user
userSecretNamespace: default
fsType: ext4
imageFormat: "2"
imageFeatures: "layering"
你可以通过注释storageclass.kubernetes.io/is-default-class
来指定默认的 StorageClass。这样新创建出来的 PVC 中的 storageClassName 字段就会自动使用默认的 StorageClass。
这里有个 provisioner 字段是必填项,主要用于指定使用那个 volume plugin 来创建 PV。没错,这里正是对应我们上节课讲过的 CSI driver 的名字。
现在我们来讲一下动态 PV 工作的过程:
首先我们定义了一个 StorageClass。当用户创建好 Pod 以后,指定了 PVC,这个时候 Kubernetes 就会根据 StorageClass 中定义的 Provisioner 来调用对应的 plugin 来创建 PV。PV 创建成功后,跟 PVC 进行绑定,挂载到 Pod 中使用。
StatefulSet 中怎么使用 PV 和 PVC?
还记得我们之前讲 StatefulSet 中遗留的问题吗?对于 StatefulSet 管理的 Pod,每个 Pod 使用的 Volume 中的数据都不一样,而且相互之间关系是需要强绑定的。这个时候就不能在 StatefulSet 的spec.template
去直接指向 PV 和 PVC了。于是我们在 StatefulSet 中使用了volumeClaimTemplate,有了这个 template 我们就可以为每一个 Pod 生成一个单独的 PVC,并且绑定 PV 了,从而实现有状态服务各个 Pod 都有自己专属的存储。这里生成的 PVC 名字跟 StatefulSet 的 Pod 名字一样,都是带有特定的序列号的。
你可以看看这里 StatefulSet 的例子:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
最后
本章节我们讲了 PV、PVC 以及 StorageClass,它们直接的关系以及设计思路。你也许刚接触这几个概念的时候,有些稀里糊涂,但是通过分析各个对象要解决的问题,可以帮助你更好地掌握它们。
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
欢迎大家扫码关注,获取更多信息
以上是关于#yyds干货盘点# Kubernetes 怎样对业务数据进行持久化存储?(10)的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点# Kubernetes 怎样控制业务的资源水位?(16)
#yyds干货盘点# Kubernetes 搞定网络原来可以如此简单?(25)
#yyds干货盘点# Kubernetes 集群权限管理那些事儿(17)
#yyds干货盘点#spring-cloud-kubernetes背后的三个关键知识点