kubernetes的存储类介绍
k8s有很多的服务,很多的资源对象。
如果要去创建服务,做数据持久化,需要预先知道可用PV有哪些?
如果为了这个服务去提前创建PV,那么我们还需要知道,这个服务,大概需要多大的空间?
环境介绍
主机 | IP地址 | 服务 |
---|---|---|
master | 192.168.1.21 | k8s |
node01 | 192.168.1.22 | k8s |
node02 | 192.168.1.23 | k8s |
基于[ https://blog.51cto.com/14320361/2464655]() 的实验继续进行
存储类介绍
Kubernetes集群管理员通过提供不同的存储类,可以满足用户不同的服务质量级别、备份策略和任意策略要求的存储需求。动态存储卷供应使用StorageClass进行实现,其允许存储卷按需被创建。如果没有动态存储供应,Kubernetes集群的管理员将不得不通过手工的方式类创建新的存储卷。通过动态存储卷,Kubernetes将能够按照用户的需要,自动创建其需要的存储。
基于StorageClass的动态存储供应整体过程如下图所示:
1)集群管理员预先创建存储类(StorageClass);
2)用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim);
3)存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume);
4)系统读取存储类的信息;
5)系统基于存储类的信息,在后台自动创建PVC需要的PV;
6)用户创建一个使用PVC的Pod;
7)Pod中的应用通过PVC进行数据的持久化;
8)而PVC使用PV进行数据的最终持久化处理。
先来简单看一下这张图实现的过程,然后我们再来研究一下
说在前面的话,静态供给的话,会需要我们手动去创建pv,如果没有足够的资源,找不到合适的pv,那么pod就会处于pending等待的状态,就是说找不到合适的伴侣了,所以解决这两种问题,就给出了这种动态供给,主要是能够自动帮你创建pv
,就是你需要多大的容量,就自动给你创建多大的容量,也就是pv,k8s帮你创建了,创建pvc的时候就需要找pv了,这个时候就交给这个存储类了,而存储类呢,去帮你创建这些pv,存储类呢,就是实现了对指定存储的一个支持,直接帮你去调用api去创建存储类,所以就不需要人工的去帮你创建pv了。
而你去想想,当节点比较多,业务比较多的时候,再去人工手动创建pv,量还是很大的,而且也不是很好去维护。
而动态供给主要的一个实现就是StorageClass存储对象,其实它就是声明你使用哪个存储,然后呢帮你去连接,再帮你去自动创建pv。
举个例子更好去理解
话不多说下图
其实它是一个基于NFS实现的一个pv供给,它大概流程是这样的,我们可能会创建一个statefulset有状态的应用存储,然后有一个管理的nfs-storageClass,因为nfs目前是不支持这个自动的创建pv的,我们可以利用社区实现的插件来完成这个pv的自动创建,也就是StorageClass这一块,创建完之后,然后pod再去引用。
一,Storage Class(存储类)
作用:它可以动态的自动的创建所需要的PV
Provisioner(供给方,提供者):及提供了存储资源的存储系统。k8s内建有多重供给方,这些供给方的名字都以"kubernetes.io"为前缀。并且还可以自定义。
Parameters(参数):存储类使用参数描述要关联到的存储卷,注意不同的供给方参数也不同。
ReclaimPlicy: PV的回收策略,可用值有Delete(默认)和Retain
(1)确定基于NFS服务来做的SC。NFS开启
[root@master yaml]# showmount -e
(2)需要RBAC权限。
RBAC:rbac是k8s的API的安全策略,是基于用户的访问权限的控制。规定了谁,可以有什么样的权限。
为了给SC资源操作k8s集群的权限。
[root@master yaml]# vim rbac-rolebind.yamlkind: NamespaceapiVersion: v1metadata: name: bdqn-test---apiVersion: v1kind: ServiceAccountmetadata: name: nfs-provisioner namespace: bdqn-test---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: nfs-provisioner-runner namespace: bdqn-testrules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["watch", "create", "update", "patch"] - apiGroups: [""] resources: ["services", "endpoints"] verbs: ["get","create","list", "watch","update"] - apiGroups: ["extensions"] resources: ["podsecuritypolicies"] resourceNames: ["nfs-provisioner"] verbs: ["use"]---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: run-nfs-provisionersubjects: - kind: ServiceAccount name: nfs-provisioner namespace: bdqn-testroleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io
运行一下
[root@master yaml]# kubectl apply -f rbac-rolebind.yaml
(3)nfs-deployment
作用:其实它是一个NFS客户端。但它通过K8S的内置的NFS驱动挂载远端的NFS服务器到本地目录;然后将自身作为storage provider,关联storage class。
[root@master yaml]# vim nfs-deployment.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata: name: nfs-client-provisioner namespace: bdqn-testspec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: serviceAccount: nfs-provisioner #指定账户 containers: - name: nfs-client-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes #指定容器内的挂载目录 env: - name: PROVISIONER_NAME #这是这个容器内置的变量 value: bdqn-test #这是上面变量的值(名字) - name: NFS_SERVER #内置变量,用于指定nfs服务的IP value: 192.168.1.21 - name: NFS_PATH #内置变量,指定的是nfs共享的目录 value: /nfsdata volumes: #这下面是指定上面挂载到容器内的nfs的路径及IP - name: nfs-client-root nfs: server: 192.168.1.21 path: /nfsdata
执行一下
[root@master yaml]# kubectl apply -f nfs-deployment.yaml
(4)创建storageclass
[root@master yaml]# vim test-storageclass.yamlapiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: stateful-nfs namespace: bdqn-testprovisioner: bdqn-test #这里要和第三个nfs-client-provisioner的env环境变量中的value值对应。reclaimPolicy: Retain #回收策略为:retain,还有一个默认的值为"default"
执行一下
[root@master yaml]# kubectl apply -f test-storageclass.yaml
(5)创建PVC
[root@master yaml]# vim test-pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata: name: test-claim namespace: bdqn-testspec: storageClassName: stateful-nfs #定义存储类的名字,要和SC的名字对应 accessModes: - ReadWriteMany #访问模式为RWM resources: requests: storage: 500Mi
执行一下
[root@master yaml]# kubectl apply -f test-pvc.yaml
查看一下
[root@master yaml]# kubectl get pvc
(6)创建一个Pod
[root@master yaml]# vim test-pod.yamlkind: PodapiVersion: v1metadata: name: test-pod namespace: bdqn-testspec: containers: - name: test-pod image: busybox args: - /bin/sh - -c - sleep 30000 volumeMounts: - name: nfs-pvc mountPath: /test restartPolicy: OnFailure volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-claim #这的名字要和PVC的名字一致
执行一下
[root@master yaml]# kubectl apply -f test-pod.yaml
查看一下
[root@master yaml]# kubectl get pod -n bdqn-test
(7)容器中添加内容,并查看挂载目录
进入容器修改页面内容
[root@master yaml]# kubectl exec -it test-pod -n bdqn-test /bin/sh/ # cd test//test # touch test-file/test # echo 123456 > test-file /test # cat test-file 123456
查看挂载目录
[root@master yaml]# ls /nfsdata/bdqn-test-test-claim-pvc-79ddfcf1-65ae-455f-9e03-5bcfe6c6ce15web1web2[root@master yaml]# cat /nfsdata/bdqn-test-test-claim-pvc-79ddfcf1-65ae-455f-9e03-5bcfe6c6ce15/test-file 123456
如果,K8S集群中, 有很多类似的PV, PVC在去向PV申请空间的时候,不仅会考虑名称以及访问控制模式,还会考虑你申请空间的大小,会分配给你最合适大小的PV。
运行一个web服务,采用Deployment资源,基于nginx镜像,replicas为3个。数据持久化目录为nginx服务的主访问目录:/usr/share/nginx/html
创建一个PVC,与上述资源进行关联。
1. 基于nfs服务来做的PV和pvc
下载nfs所需安装包
[root@node02 ~]# yum -y install nfs-utils rpcbind
创建共享目录
[root@master ~]# mkdir /nfsdata
创建共享目录的权限
[root@master ~]# vim /etc/exports/nfsdata *(rw,sync,no_root_squash)
开启nfs和rpcbind
[root@master ~]# systemctl start nfs-server.service [root@master ~]# systemctl start rpcbind
测试一下
[root@master ~]# showmount -e
2.先创建两个PV, web- pV1(1G) ,web-pv2 (2G)
web1
[root@master yaml]# vim web.yaml apiVersion: v1kind: PersistentVolumemetadata: name: web-pvspec : capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata/web1 server: 192.168.1.21
web2
[root@master yaml]# vim web2.yaml apiVersion: v1kind: PersistentVolumemetadata: name: web-pv2spec : capacity : storage: 2Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata/web2 server: 192.168.1.21
3.创建所需文件夹
[root@master yaml]# mkdir /nfsdata/web1[root@master yaml]# mkdir /nfsdata/web2
4.执行一下web和web2
[root@master yaml]# kubectl apply -f web.yaml [root@master yaml]# kubectl apply -f web2.yaml
5.查看一下
[root@master yaml]# kubectl get pv
6.创建web的pvc的yaml文件
[root@master yaml]# vim web-pvc.yaml apiVersion: v1kind: PersistentVolumeClaimmetadata: name: web-pvcspec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: nfs
执行一下
[root@master yaml]# kubectl apply -f web-pvc.yaml
查看一下
[root@master yaml]# kubectl get pvc
系统会自动给pvc一个相近内存的pv,所以选择了1G的那个
7.创建pod的yaml文件
[root@master yaml]# vim web-pod.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata: name: web-podspec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx volumeMounts: - name: web-test mountPath: /usr/share/nginx/html volumes: - name: web-test persistentVolumeClaim: claimName: web-pvc
执行一下
[root@master yaml]# kubectl apply -f web-pod.yaml
查看一下
[root@master yaml]# kubectl get pod
8. 访问一下nginx的网页
查看一下nginx的ip
[root@master yaml]# kubectl get pod -o wide
进入容器设置网页内容
root@master yaml]# kubectl exec -it web-pod-8686d9c594-qxhr9 /bin/bashroot@web-pod-8686d9c594-qxhr9:/# cd /usr/share/nginx/html/root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# lsroot@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# echo 123456 > index.htmlroot@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# exit
访问一下
[root@master yaml]# curl 10.244.2.17
如果两个PV,大小一样,名称一样,访问控制模式不一样,PVC会关联哪一个? (验证PV和PVC 关联的时候,访问模式必须一样)
两个PV,大小一样,名称一样,访问控制模式不一样
<1>创建两个pv
web1
[root@master yaml]# vim web1.yaml apiVersion: v1kind: PersistentVolumemetadata: name: web-pvspec : capacity: storage: 1Gi accessModes: - ReadWriteOnce #能以读-写mount到单个的节点 persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata/web1 server: 192.168.1.21
web2
[root@master yaml]# vim web2.yaml apiVersion: v1kind: PersistentVolumemetadata: name: web-pvspec : capacity: storage: 1Gi accessModes: - ReadWriteMany #能以读-写mount到多个的节点 persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfsdata/web1 server: 192.168.1.21
创建所需文件
[root@master yaml]# mkdir /nfsdata/web1
执行一下
[root@master yaml]# kubectl apply -f web1.yaml [root@master yaml]# kubectl apply -f web2.yaml
<2>创建pvc
[root@master yaml]# vim web-pvc.yaml apiVersion: v1kind: PersistentVolumeClaimmetadata: name: web-pvcspec: accessModes: - ReadWriteMany #能以读-写mount到多个的节点 resources: requests: storage: 1Gi storageClassName: nfs
执行一下
[root@master yaml]# kubectl apply -f web-pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pv
[root@master yaml]# kubectl get pvc
现在可以看到pv和pvc关联成功,但是为什么只有一个pv呢?(pv挂载的目录要相同)
那是因为当创建了两个相同名字的pv时它并不会认为这是两个不同的pv,而会把他们当成是同一个pv,后创建的pv会刷新前面创建的pv。然后,当创建了pvc,并且pvc的访问模式和后面创建pv的访问模式一样,他们就会关联成功,反之不成功。(当然这些条件下还需要考虑,pv的内存)
(1)以自己的名称创建一个名称空间。以下所有资源都在此名称空间之下。
<1>编写namespace的yam文件
[root@master yaml]# vim namespace.yaml kind: NamespaceapiVersion: v1metadata: name: xgp-znb
<2>执行一下
[root@master yaml]# kubectl apply -f namespace.yaml
<3>查看一下
[root@master yaml]# kubectl get ns
(2)设置rbac权限。
下载所需镜像
docker pull registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
<1>编写rbac的yam文件
[root@master yaml]# vim rbac-rolebind.yamlkind: NamespaceapiVersion: v1metadata: name: xgp-znb---apiVersion: v1kind: ServiceAccountmetadata: name: nfs-provisioner namespace: xgp-znb---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: nfs-provisioner-runner namespace: xgp-znbrules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["watch", "create", "update", "patch"] - apiGroups: [""] resources: ["services", "endpoints"] verbs: ["get","create","list", "watch","update"] - apiGroups: ["extensions"] resources: ["podsecuritypolicies"] resourceNames: ["nfs-provisioner"] verbs: ["use"]---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: run-nfs-provisionersubjects: - kind: ServiceAccount name: nfs-provisioner namespace: xgp-znbroleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io
<2>执行一下
[root@master yaml]# kubectl apply -f rbac-rolebind.yaml
(3)创建nfs-deployment.yaml
<1>编写deployment的yam文件
[root@master yaml]# vim nfs-deployment.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata: name: nfs-client-provisioner namespace: xgp-znbspec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: serviceAccount: nfs-provisioner containers: - name: nfs-client-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: xgp-znb - name: NFS_SERVER value: 192.168.1.21 - name: NFS_PATH value: /nfsdata volumes: - name: nfs-client-root nfs: server: 192.168.1.21 path: /nfsdata
<2>执行一下
[root@master yaml]# kubectl apply -f nfs-deployment.yaml
(4)创建storageclass自动创建PV。
<1>编写storageclass的yam文件
[root@master yaml]# vim storageclass.yamlapiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: test-scprovisioner: xgp-znb #通过provisioner字段关联到上述DeployreclaimPolicy: Retain
<2>执行一下
[root@master yaml]# kubectl apply -f storageclass.yaml
(5)创建PVC
<1>编写PVC的yaml文件
[root@master yaml]# vim pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata: name: test-claim namespace: xgp-znbspec: storageClassName: test-sc accessModes: - ReadWriteMany resources: requests: storage: 500Mi
<2>执行一下
[root@master yaml]# kubectl apply -f pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pvc -n xgp-znb
(6)创建一个Pod, 基于nginx运行一个web服务,使用Deployment资源对象,replicas=3.持久化存储目录为默认主目录
<1>编写deployment的yam文件
[root@master yaml]# vim pod.yaml apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: web-pod namespace: xgp-znbspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx volumeMounts: - name: web-test mountPath: /usr/share/nginx/html volumes: - name: web-test persistentVolumeClaim: claimName: test-claim
<2>执行一下
[root@master yaml]# kubectl apply -f pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pod -n xgp-znb
(7)访问nginx页面
修改nginx主页
[root@master yaml]# kubectl exec -it web-pod-8cd956cc7-6szjb -n xgp-znb /bin/bash//进入容器之中root@web-pod-8cd956cc7-6szjb:/# echo xgp-znb > /usr/share/nginx/html/index.html//添加自定义内容主机
访问一下
[root@master yaml]# curl 10.244.2.18
五个可移植性建议
- 把你的 pvc,和 其它一系列配置放一起, 比如说deployment,configmap
- 不要把你的pv放在其它配置里, 因为用户可能没有权限创建pv
- 初始化pvc 模版的时候, 提供一个storageclass
- 在你的工具软件中,watch那些没有bound的pvc,并呈现给用户
- 集群启动的时候启用DefaultStorageClass, 但是不要指定某一类特定的class, 因为不同provisioner的class,参数很难一致
volumn phase
1. 在PVC中绑定一个PV,可以根据下面几种条件组合选择
- Access Modes, 按照访问模式选择pv
- Resources, 按照资源属性选择, 比如说请求存储大小为8个G的pv
- Selector, 按照pv的label选择
- Class, 根据StorageClass的class名称选择, 通过annotation指定了Storage Class的名字, 来绑定特定类型的后端存储