k8s的存储类
更新:HHH   时间:2023-1-7


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

四,五个可移植性建议

  1. 把你的 pvc,和 其它一系列配置放一起, 比如说deployment,configmap
  2. 不要把你的pv放在其它配置里, 因为用户可能没有权限创建pv
  3. 初始化pvc 模版的时候, 提供一个storageclass
  4. 在你的工具软件中,watch那些没有bound的pvc,并呈现给用户
  5. 集群启动的时候启用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, 或者其值为“”)

返回云计算教程...