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.yaml
kind: Namespace
apiVersion: v1
metadata:
name: bdqn-test
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: bdqn-test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
namespace: bdqn-test
rules:
- 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: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: bdqn-test
roleRef:
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.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: bdqn-test
spec:
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.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: stateful-nfs
namespace: bdqn-test
provisioner: 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.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
namespace: bdqn-test
spec:
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.yaml
kind: Pod
apiVersion: v1
metadata:
name: test-pod
namespace: bdqn-test
spec:
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-5bcfe6c6ce15
web1
web2
[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: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
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: v1
kind: PersistentVolume
metadata:
name: web-pv2
spec :
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: v1
kind: PersistentVolumeClaim
metadata:
name: web-pvc
spec:
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.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web-pod
spec:
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/bash
root@web-pod-8686d9c594-qxhr9:/# cd /usr/share/nginx/html/
root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# ls
root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# echo 123456 > index.html
root@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: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
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: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
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: v1
kind: PersistentVolumeClaim
metadata:
name: web-pvc
spec:
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: Namespace
apiVersion: v1
metadata:
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.yaml
kind: Namespace
apiVersion: v1
metadata:
name: xgp-znb
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: xgp-znb
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
namespace: xgp-znb
rules:
- 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: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: xgp-znb
roleRef:
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.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: xgp-znb
spec:
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.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: test-sc
provisioner: xgp-znb #通过provisioner字段关联到上述Deploy
reclaimPolicy: Retain
<2>执行一下
[root@master yaml]# kubectl apply -f storageclass.yaml
(5)创建PVC
<1>编写PVC的yaml文件
[root@master yaml]# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
namespace: xgp-znb
spec:
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/v1beta1
kind: Deployment
metadata:
name: web-pod
namespace: xgp-znb
spec:
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的名字, 来绑定特定类型的后端存储
2. 关于根据class过滤出pv的说明:
所有的 PVC 都可以在不使用 StorageClass 注解的情况下,直接使用某个动态存储。把一个StorageClass 对象标记为 “default” 就可以了。StorageClass 用注解http://storageclass.beta.kubernetes.io/is-default-class 就可以成为缺省存储。有了缺省的 StorageClass,用户创建 PVC 就不用 storage-class 的注解了,1.4 中新加入的DefaultStorageClass 准入控制器会自动把这个标注指向缺省存储类。PVC 指定特定storageClassName,如fast时, 绑定名称为fast的storageClassPVC中指定storageClassName为“”时, 绑定no class的pv(pv中无class annotation, 或者其值为“”)PVC不指定storageClassName时, DefaultStorageClass admission plugin 开启与否(在apiserver启动时可以指定), 对default class的解析行为是不同的。当DefaultStorageClass admission plugin启用时, 针对没有storageClass annotation的pvc,DefaultStorageClass会分配一个默认的class, 这个默认的class需要用户指定,比如在创建storageclass对象时加入annotation,如 http://storageclass.beta.kubernetes.io/is-default-class: “true” 。如果有多个默认的class, 则pvc会被拒绝创建, 如果用户没有指定默认的class, 则这个DefaultStorageClass admission plugin不会起任何作用。 pvc会找那些no class的pv做绑定。当DefaultStorageClass admission plugin没有启用时, 针对没有storageClass annotation的pvc, 会绑定no class的pv(pv中无class annotation, 或者其值为“”)