十、k8s入门系列----PV、PVC、StorageClass

  关于PV、PVC、StorageClass ,这篇文章讲的不错:https://www.cnblogs.com/rexcheny/p/10925464.html

  容器的设计理念就是一次性,也就是容器销毁后容器里的所有数据都会销毁,所以需要将容器里面需要保留的数据挂载到持久性存储中,这里就涉及到三个概念:PV、PVC、StorageClass 。

  HostPath

  当使用docker创建container的时候,一般都是加参数 -v 挂载宿主机的目录到container里面,k8s 也可以实现该功能,先讲解一下 挂载到宿主机目录的方法。 

  创建一个deployment资源配置文件,挂载到宿主机目录:

[root@ylserver10686071 ~]# cat volumes001.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: volumes001
  namespace: prod
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: volumes001
  template:
    metadata:
      labels:
        k8s-app: volumes001
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        volumeMounts:
        - mountPath: /usr/share/nginx/html/
          name: html
      volumes:
      - name: html
        hostPath:
          path: /data/nginx/html/
          type: DirectoryOrCreate
  • volumeMounts 挂载到container 指定目录的相关配置,根据name来匹配volumes对象的下的name,据此来找到挂载对象
  • volumes              声明挂载对象
  • hostPath             存储类型
  • type: DirectoryOrCreate  如果宿主机路径不存在则创造该路径,当值为Directory 是,宿主机必须有该目录,否则会导致pod创建失败

 

  创建deployment资源,查看pod在哪台Node上运行:

[root@ylserver10686071 ~]# kubectl apply -f volumes001.yml 
deployment.apps/volumes001 created
[root@ylserver10686071 ~]# kubectl get pods -n prod -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP             NODE               NOMINATED NODE   READINESS GATES
volumes001-66767f866f-rc5qk   1/1     Running   0          21s   10.233.72.59   ylserver10686073   <none>           <none>
[root@ylserver10686071 ~]# 

  在ylserver10686071 Node上查看目录是否创建,然后到Pod运行的节点上查看目录是否创建:

[root@ylserver10686071 ~]# ll /data/nginx/html/
ls: cannot access /data/nginx/html/: No such file or directory
[root@ylserver10686071 ~]# 
[root@ylserver10686073 ~]# ll /data/nginx/html/
total 0
[root@ylserver10686073 ~]# 

  给挂载目录创建文件,验证是否挂载到Pod里面:

[root@ylserver10686073 ~]# echo "Hello K8S" > /data/nginx/html/index.html
[root@ylserver10686073 ~]# curl http://10.233.72.59/index.html
Hello K8S
[root@ylserver10686073 ~]# 

  

   PV

  上面的实验中宿主机挂载的目录只有在 Pod 运行的 Node 上才会创建,换言之,Pod要挂载的目录必须跟Node做绑定,这会增加运维的难度,也失去k8s的故障转移特性。

  针对这个问题,可以使用存储券解决,这里就要引入一个概念:PV。

  PV全称叫做Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的,这个通常都是有运维或者数据存储工程师来定义。本节使用NFS来作为存储端,NFS搭建这里不做讲解。

  先创建一个PV资源配置文件:

[root@ylserver10686071 ~]# cat pv001.yml 
apiVersion: v1
kind: PersistentVolume  ###PV资源不属于任何命名空间,属于集群级别的
                        ###kubectl api-resources --namespaced=true 命令可以查看哪些资源属于命名空间              
metadata:  
  name: pv001  
  labels:   ###Label可以不定义        
    name: pv001
    storetype: nfs
spec:    ###定义PV资源规格
  storageClassName: normal
  accessModes:  ###设置访问模型
    - ReadWriteMany
    - ReadWriteOnce
    - ReadOnlyMany
  capacity:  ###设置存储空间大小
    storage: 500Mi
  persistentVolumeReclaimPolicy: Retain  ###回收策略
  nfs:  
    path: /data/nfs/k8s/
    server: 10.68.60.193
[root@ylserver10686071 ~]# 

  

  accessModes 有3种属性值:

  • ReadWriteMany     多路读写,卷能被集群多个节点挂载并读写
  • ReadWriteOnce     单路读写,卷只能被单一集群节点挂载读写
  • ReadOnlyMany      多路只读,卷能被多个集群节点挂载且只能读

 

  persistentVolumeReclaimPolicy 回收策略也有3种属性值:

  • Retain

     当删除与之绑定的PVC时候,这个PV被标记为released(PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV上,但是该PV不可用,需要手动来处理这些数据并删除该PV

   这种方式是最常用的,可以避免误删pvc或者pv而造成数据的丢失

  • Delete       删除存储资源,AWS EBS, GCE PD, Azure Disk, and Cinder volumes支持这种方式
  • Recycle     这个在1.14版本中以及被废弃,取而代之的是推荐使用动态存储供给策略,它的功能是当删除与该PV关联的PVC时,自动删除该PV中的所有数据

   

  创建完PV后,PV会有几种状态:

  • Available(可用)  块空闲资源还没有被任何声明绑定
  • Bound(已绑定)  卷已经被声明绑定
  • Released(已释放) 声明被删除,但是资源还未被集群重新声明
  • Failed(失败)  该卷的自动回收失败

   

  创建PV资源,并查看PV信息:

[root@ylserver10686071 ~]# kubectl apply -f pv001.yml 
persistentvolume/pv001 created
[root@ylserver10686071 ~]# kubectl get pv 
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   500Mi      RWO,ROX,RWX    Retain           Available           normal                  6s

   

  PVC

  PV只是定义了一个存储卷实体,还需要一层抽象的接口使其与POD关联,这层抽象的接口就是PVC,全称 Persistent Volume Claim,也就是持久化存储声明。开发人员使用这个来描述该容器需要一个什么存储。

  创建一个PVC资源配置文件:

[root@ylserver10686071 ~]# cat pvc001.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc001
  namespace: prod   ###PVC资源属于命名空间级别
  labels:       ###Label可以不定义
    name: pvc001
    storetype: nfs
    capacity: 500Mi
spec:
  storageClassName: normal
  accessModes:    ###PVC也需要定义访问模式,不过它的模式一定是和现有PV相同或者是它的子集,否则匹配不到PV
  - ReadWriteMany
  resources:   ###定义资源要求PV满足这个PVC的要求才会被匹配到
    requests:
      storage: 500Mi  # 定义要求有多大空间

  创建PVC资源,查看PVC资源和PV资源绑定情况,可以看到PV和PVC已经实现绑定:

[root@ylserver10686071 ~]# kubectl apply -f pvc001.yml 
persistentvolumeclaim/pvc001 created
[root@ylserver10686071 ~]# kubectl get pvc -n prod
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc001   Bound    pv001    500Mi      RWO,ROX,RWX    normal         15s
[root@ylserver10686071 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 500Mi RWO,ROX,RWX Retain Bound prod/pvc001 normal 62m
[root@ylserver10686071 ~]#

  

  PVC是如何跟PVC绑定的呢,有以下几个原则:

  • PV和PVC中的spec关键字段要匹配,比如存储(storage)大小
  • PVC的访问模式一定是和现有PV相同或者是它的子集,否则匹配不到PV
  • PV和PVC中的 StorageClass  Name字段必须一致,StorageClass后面会讲到
  • Label 标签在这里只做描述作用,跟 PV 和 PVC 的绑定没有任何关系

 

  看到这里,回想总结一下就会发现 k8s 里面会通过定义一层抽象概念来管理实体或者连接实体,类似于 Pod 和 Container , PVC 和 PV ;对象和对象的匹配设计原理也是一直,例如 deployment匹配replicaset通过matchLabels ,PV 和 PVC通过 StorageClass  Name以及 resources等,即对象与对象之间通过匹配关系进行绑定。

 

  更新上面的deployment资源配置文件,使其使用创建好的PVC资源:

[root@ylserver10686071 ~]# cat volumes001.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: volumes001
  namespace: prod   ###要和指定的PVC同一个命名空间
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: volumes001
  template:
    metadata:
      labels:
        k8s-app: volumes001
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        volumeMounts:   ###container的挂载声明没有改变
        - mountPath: /usr/share/nginx/html/
          name: html
      volumes:     ###依然使用volumes声明挂载卷
      - name: html
        persistentVolumeClaim:   ###指定PVC
          claimName: pvc001

  更新资源deployment资源配置文件,查看pod关于volumes相关信息:

[root@ylserver10686071 ~]# kubectl apply -f volumes001.yml 
deployment.apps/volumes001 configured
[root@ylserver10686071 ~]# kubectl describe pod volumes001 -n prod|grep -5 Volumes
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  html:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  pvc001
    ReadOnly:   false
  default-token-lx75g:
[root@ylserver10686071 ~]# 

  写一个文件到NFS挂载目录中,测试一下效果:

[root@ylserver106860193 ~]# echo "K8S PV" > /data/nfs/k8s/index.html
[root@ylserver10686071 ~]# kubectl get pods -n prod -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP             NODE               NOMINATED NODE   READINESS GATES
volumes001-55f5bb9585-nx9xd   1/1     Running   0          11m   10.233.72.60   ylserver10686073   <none>           <none>
[root@ylserver10686071 ~]# curl http://10.233.72.60
K8S PV
[root@ylserver10686071 ~]# 

  给原来的Node打上Taint,重启deployment资源,查看Pod在其他Node上运行时,原来的挂载文件是否还存在:

[root@ylserver10686071 ~]# kubectl taint node ylserver10686073 web=nginx:NoSchedule
node/ylserver10686073 tainted
[root@ylserver10686071 ~]# kubectl rollout restart deployment volumes001 -n prod
deployment.apps/volumes001 restarted
[root@ylserver10686071 ~]# kubectl get pods -n prod -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP             NODE               NOMINATED NODE   READINESS GATES
volumes001-7fcd68c5b8-hcwcf   1/1     Running   0          30s   10.233.67.54   ylserver10686072   <none>           <none>
[root@ylserver10686071 ~]# 

  验证一下,可以看到挂载的文件依然存在:

[root@ylserver10686071 ~]# curl http://10.233.67.54
K8S PV
[root@ylserver10686071 ~]# 

  PV回收时需要删除PVC,删除PVC需要先删除关联的Pod,验证一下:

[root@ylserver10686071 ~]# kubectl delete deployments volumes001  -n prod 
deployment.apps "volumes001" deleted
[root@ylserver10686071 ~]# kubectl delete pvc pvc001 -n prod
persistentvolumeclaim "pvc001" deleted
[root@ylserver10686071 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM         STORAGECLASS   REASON   AGE
pv001   500Mi      RWO,ROX,RWX    Retain           Released   prod/pvc001   normal                  18m
[root@ylserver10686071 ~]# 

  可以看到删除pvc后,pv处于Released状态,此时pv只能删除重新创建才能继续使用,验证一下:

[root@ylserver10686071 ~]# kubectl apply -f pvc001.yml 
persistentvolumeclaim/pvc001 created
[root@ylserver10686071 ~]# kubectl get pvc -n prod
NAME     STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc001   Pending                                      normal         17s
[root@ylserver10686071 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM         STORAGECLASS   REASON   AGE
pv001   500Mi      RWO,ROX,RWX    Retain           Released   prod/pvc001   normal                  19m

  删除PV,因为回收策略是Retain,所以NFS端的数据依然存在,验证一下:

[root@ylserver10686071 ~]# kubectl delete pv pv001
persistentvolume "pv001" deleted
[root@ylserver106860193 ~]# cat /data/nfs/k8s/index.html 
K8S PV
[root@ylserver106860193 ~]# 

  重新创建PV,PVC就可以继续绑定:

[root@ylserver10686071 ~]# kubectl apply -f pv001.yml 
persistentvolume/pv001 created
[root@ylserver10686071 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM         STORAGECLASS   REASON   AGE
pv001   500Mi      RWO,ROX,RWX    Retain           Bound    prod/pvc001   normal                  18s
[root@ylserver10686071 ~]# kubectl get pvc -n prod
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc001   Bound    pv001    500Mi      RWO,ROX,RWX    normal         4m11s
[root@ylserver10686071 ~]# 

  

  StorageClass

  PV是运维人员创建的,开发操作PVC,一个PV只能被一个PVC绑定,如果这些PV都需要运维手动来处理将会是一件十分繁琐的事情,所以就有了动态供给概念,也就是Dynamic Provisioning。而我们上面的创建的PV都是静态供给方式,也就是Static Provisioning。而动态供给的关键就是StorageClass,它的作用就是创建PV模板。

  创建StorageClass里面需要定义PV属性比如存储类型、大小等;另外创建这种PV需要用到存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass,则会为其自动创建PV并进行绑定。

  Kubernetes本身支持的动态PV创建不包括nfs,所以需要使用额外插件实现。nfs-client  

  GitHub的部署里面使用的镜像地址国内无法下载,这里修改了一下,用国内的镜像地址,这里使用helm部署nfs插件,先下载 chart文件: https://www.mediafire.com/file/pzi7skcwdm0v64a/nfs-subdir-external-provisioner.tar.gz/file

  下载文件后,解压到 /opt 目录下:

tar -zxvf nfs-subdir-external-provisioner.tar.gz  -C /opt/

  使用helm部署 nfs-client插件:

[root@ylserver10686071 ~]# cd /opt/nfs-subdir-external-provisioner/
[root@ylserver10686071 nfs-subdir-external-provisioner]# helm install nfs-subdir-external-provisioner --namespace  kube-system .   --set nfs.server=10.68.60.193 --set nfs.path=/data/nfs/k8s/
NAME: nfs-subdir-external-provisioner
LAST DEPLOYED: Fri Jul 30 20:36:05 2021
NAMESPACE: prod
STATUS: deployed
REVISION: 1
TEST SUITE: None
[root@ylserver10686071 nfs-subdir-external-provisioner]# 

  查看已经创建的 StorageClass,StorageClass属于集群级别:

[root@ylserver10686071 nfs-subdir-external-provisioner]# kubectl get storageclass
NAME         PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-client   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   48s
[root@ylserver10686071 nfs-subdir-external-provisioner]# 

  创建PVC资源配置文件,使用nfs-client StorageClass:

[root@ylserver10686071 ~]# cat pvc002.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc002
  namespace: default
  labels: 
    name: pvc002
    storetype: nfs
    capacity: 300Mi
spec:
  storageClassName: nfs-client
  accessModes:  
  - ReadWriteMany
  resources: 
    requests:
      storage: 300Mi  
[root@ylserver10686071 ~]# 

  创建PVC资源,可以看到PV已经自动创建:

[root@ylserver10686071 ~]# kubectl apply -f  pvc002.yml 
persistentvolumeclaim/pvc002 created
[root@ylserver10686071 ~]# kubectl get pvc 
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc002   Bound    pvc-13f05a23-0fce-429f-9803-db4ec3dd6465   300Mi      RWX            nfs-client     5m
[root@ylserver10686071 ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
pvc-13f05a23-0fce-429f-9803-db4ec3dd6465   300Mi      RWX            Delete           Bound    default/pvc002   nfs-client              5m3s
[root@ylserver10686071 ~]# 

  NFS服务端可以看到创建的PV对应存储目录:

[root@ylserver106860193 ~]# ll /data/nfs/k8s/
total 4
drwxrwxrwx 2 root root 6 Jul 31 03:17 default-pvc002-pvc-13f05a23-0fce-429f-9803-db4ec3dd6465
-rw-r--r-- 1 root root 7 Jul 30 03:32 index.html
[root@ylserver106860193 ~]# 

  现在往PV对应的目录写入文件,然后删除PVC,看NFS端PV对应的存储目录是否存储:

[root@ylserver106860193 ~]# echo "StorageClass" > /data/nfs/k8s/default-pvc002-pvc-13f05a23-0fce-429f-9803-db4ec3dd6465/index.html
[root@ylserver106860193 ~]# 

  开始删除PVC,PV也会自动删除

[root@ylserver10686071 ~]# kubectl delete pvc pvc002
persistentvolumeclaim "pvc002" deleted
[root@ylserver10686071 ~]# kubectl get pvc
No resources found in default namespace.
[root@ylserver10686071 ~]# kubectl get pv
No resources found
[root@ylserver10686071 ~]# 

  查看NFS服务端目录,可以看到文件依然保留,目录在原有名称上添加了archived-:

[root@ylserver106860193 ~]# cat  /data/nfs/k8s/archived-default-pvc002-pvc-13f05a23-0fce-429f-9803-db4ec3dd6465/index.html 
StorageClass
[root@ylserver106860193 ~]# 

  其实跟StorageClass回收策略,涉及两个参数,文件 /opt/nfs-subdir-external-provisioner/values.yaml里可以修改,然后helm update 即可:

archiveOnDelete: true
reclaimPolicy: Delete
  • archiveOnDelete    当设置为 true 时,在删除PVC后,会对 PV 对应的存储目录进行备份
  • reclaimPolicy          回收策略,上面有讲解过,Delete策略就是删除PVC时自动删除绑定的PV

   

  StatefulSet StorageClass

  当使用 StatefulSet 控制器创建Pod的时候,可以在 StatefulSet 配置文件里面直接声明 PV的创建,创建一个StatefulSet  资源配置文件:

[root@ylserver10686071 ~]# cat volumes002.yml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: volumes002
  namespace: prod
spec:
  serviceName: volumes002-svc
  selector:
    matchLabels:
      k8s-app: volumes002
  replicas: 3
  template:
    metadata:
      labels:
        k8s-app: volumes002
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        volumeMounts:
        - mountPath: /usr/share/nginx/html/
          name: html
  volumeClaimTemplates:   ###这里声明PV资源 
  - metadata:   ###这里声明具体PV信息,数组类型,可以声明多个
      name: html
      annotations:
        volume.beta.kubernetes.io/storage-class: nfs-client
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 200Mi

  创建 StatefulSet  资源,查看PV创建信息:

[root@ylserver10686071 ~]# kubectl apply -f volumes002.yml 
statefulset.apps/volumes002 created
[root@ylserver10686071 ~]# kubectl get pvc -n prod
NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
html-volumes002-0   Bound    pvc-216d73b5-b4b6-4103-a163-f67c235b8cda   200Mi      RWX            nfs-client     38s
html-volumes002-1   Bound    pvc-17a767a7-282a-4fc4-8050-72ddc5829d86   200Mi      RWX            nfs-client     22s
html-volumes002-2   Bound    pvc-40409054-f6a8-46f8-9c0a-96979de34249   200Mi      RWX            nfs-client     5s
[root@ylserver10686071 ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pvc-17a767a7-282a-4fc4-8050-72ddc5829d86   200Mi      RWX            Delete           Bound    prod/html-volumes002-1   nfs-client              26s
pvc-216d73b5-b4b6-4103-a163-f67c235b8cda   200Mi      RWX            Delete           Bound    prod/html-volumes002-0   nfs-client              42s
pvc-40409054-f6a8-46f8-9c0a-96979de34249   200Mi      RWX            Delete           Bound    prod/html-volumes002-2   nfs-client              9s
[root@ylserver10686071 ~]# 

  

  Local PV

  PV的后端存储也支持本地宿主机目录,类似于hostPath,跟hostPath 同样存在一个问题,通常先创建PV,然后创建PVC,这时候如果两者匹配那么系统会自动进行绑定,哪怕是动态PV创建,也是先调度POD到任意一个节点,然后根据PVC来进行创建PV然后进行绑定最后挂载到POD中,可是本地持久化存储有一个问题就是这种PV必须要先准备好,而且不一定集群所有节点都有这种PV,如果POD随意调度肯定不行,如何保证POD一定会被调度到有PV的节点上呢?这时候就需要在PV中声明节点亲和,且POD被调度的时候还要考虑卷的分布情况。

  编写PV资源配置文件:

[root@ylserver10686071 ~]# cat pv002.yml 
apiVersion: v1
kind: PersistentVolume
metadata:  
  name: pv002
  labels: 
    name: pv002
    storetype: Local 
spec:  
  storageClassName: local-storage
  accessModes:  
    - ReadWriteMany
    - ReadWriteOnce
    - ReadOnlyMany
  capacity: 
    storage: 5000Mi
  persistentVolumeReclaimPolicy: Retain
  local:   ### local类型
    path: /data/pv002   ###宿主机目录
  nodeAffinity:   ###节点亲和性设置
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: database
          operator: In
          values:
          - mysql

 

  编写存储类配置文件:

[root@ylserver10686071 ~]# cat storageclass002.yml 
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

  这里的volumeBindingMode: WaitForFirstConsumer很关键,意思就是延迟绑定,当有符合PVC要求的PV不立即绑定。因为POD使用PVC,而绑定之后,POD被调度到其他节点,显然其他节点很有可能没有那个PV所以POD就挂起了,另外就算该节点有合适的PV,而POD被设置成不能运行在该节点,这时候就没法了,延迟绑定的好处是,POD的调度要参考卷的分布。当开始调度POD的时候看看它要求的LPV在哪里,然后就调度到该节点,然后进行PVC的绑定,最后在挂载到POD中,这样就保证了POD所在的节点就一定是LPV所在的节点。所以让PVC延迟绑定,就是等到使用这个PVC的POD出现在调度器上之后(真正被调度之前),然后根据综合评估再来绑定这个PVC。

 

  编写PVC配置文件:

[root@ylserver10686071 ~]# cat pvc002.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc002
  namespace: prod
  labels: 
    name: pvc002
    storetype: local
    capacity: 5000Mi
spec:
  storageClassName: local-storage
  accessModes:  
  - ReadWriteOnce
  resources: 
    requests:
      storage: 5000Mi

  创建PV、StorageClass、PVC资源,查看PVC绑定情况:

[root@ylserver10686071 ~]# kubectl apply -f pv002.yml 
persistentvolume/pv002 created
[root@ylserver10686071 ~]# kubectl apply -f storageclass002.yml 
storageclass.storage.k8s.io/local-storage created
[root@ylserver10686071 ~]# kubectl apply -f pvc002.yml 
persistentvolumeclaim/pvc002 created
[root@ylserver10686071 ~]# kubectl get pvc -n prod|grep pvc002
pvc002              Pending                                                                        local-storage   17s

  可以看到PVC处于Pending状态,这也就是延迟绑定,因为此时还没有POD。

 

  创建 StatefulSet 资源配置文件:

[root@ylserver10686071 ~]# cat volumes003.yml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: volumes003
  namespace: prod
spec:
  serviceName: volumes003-svc
  replicas: 1
  selector:
    matchLabels:
      k8s-app: volumes003
  template:
    metadata:
      labels:
        k8s-app: volumes003
    spec:
      containers:
      - name: mysql
        env:
        - name: "MYSQL_DATABASE"
          value: "admin"
        - name: "MYSQL_USER"
          value: "admin"
        - name: "MYSQL_ROOT_PASSWORD"
          value: "123456"
        - name: "MYSQL_PASSWORD"
          value: "123456"
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 3306
            name: mysql
            protocol: TCP
        volumeMounts:
          - mountPath: /var/lib/mysql
            name: mysql-data
      volumes:
      - name: mysql-data
        persistentVolumeClaim:
          claimName: pvc002

  先创建挂载目录(目录必须先手动创建),接着给Node打上标签,然后创建StatefulSet,查看Pod运行情况:

[root@ylserver10686073 ~]# mkdir /data/pv002
[root@ylserver10686071 ~]# kubectl label node ylserver10686073 database=mysql
node/ylserver10686073 labeled
[root@ylserver10686071 ~]# kubectl apply -f  volumes003.yml
statefulset.apps/volumes003 created
[root@ylserver10686071 ~]# kubectl get pods -n prod -o wide|grep volumes003
volumes003-0   1/1     Running   0          23s   10.233.72.71   ylserver10686073   <none>           <none>
[root@ylserver10686071 ~]# 

  Pod运行的 Node 节点上查看挂载目录内容:

[root@ylserver10686073 ~]# ll /data/pv002/
total 188484
-rw-r-----. 1 polkitd input       56 Jul 31 17:31 auto.cnf
-rw-------. 1 polkitd input     1676 Jul 31 17:31 ca-key.pem
-rw-r--r--. 1 polkitd input     1112 Jul 31 17:31 ca.pem
-rw-r--r--. 1 polkitd input     1112 Jul 31 17:31 client-cert.pem
-rw-------. 1 polkitd input     1680 Jul 31 17:31 client-key.pem
-rw-r-----. 1 polkitd input      436 Jul 31 17:31 ib_buffer_pool
-rw-r-----. 1 polkitd input 79691776 Jul 31 17:31 ibdata1
-rw-r-----. 1 polkitd input 50331648 Jul 31 17:31 ib_logfile0
-rw-r-----. 1 polkitd input 50331648 Jul 31 17:30 ib_logfile1
-rw-r-----. 1 polkitd input 12582912 Jul 31 17:31 ibtmp1
drwxr-x---. 2 polkitd input     4096 Jul 31 17:31 mysql
drwxr-x---. 2 polkitd input     8192 Jul 31 17:31 performance_schema
-rw-------. 1 polkitd input     1676 Jul 31 17:31 private_key.pem
-rw-r--r--. 1 polkitd input      452 Jul 31 17:31 public_key.pem
-rw-r--r--. 1 polkitd input     1112 Jul 31 17:31 server-cert.pem
-rw-------. 1 polkitd input     1680 Jul 31 17:31 server-key.pem
drwxr-x---. 2 polkitd input     8192 Jul 31 17:31 sys
[root@ylserver10686073 ~]# 

  这个POD被调度到Node ylserver10686073上,因为PV就在Node ylserver10686073上,这时候删除这个POD,然后在重建该POD,那么依然会被调度到Node ylserver10686073上。

  

  总结一下:

  1. 根据 Container 一次性原则,Container销毁时,里面的数据也会销毁,所以需要保存的数据需要挂载到持久化存储中;
  2. Container 保存数据数据可以选择直接挂载宿主机目录,即 挂载卷类型为 hostPath,或者使用 PVC 卷进行挂载;
  3. PVC 是一层抽象接口,绑定的 PV 实体存储后端类型可以根据需求自定义,可以是本地宿主机目录, NFS,AWS EBS, GCE PD, Azure Disk, and Cinder volumes等等;
  4. PVC 绑定 PV 是根据 storage 大小、accessModes类型、以及相同的 storageClassName来绑定,其中 storage 大小、accessModes类型 必须是 PV的子集;
  5. StatefulSet 类型的资源可以在模板里面定义PVC资源,而不需要单独配置PVC文件;
  6. StorageClass 动态持久化存储可以由PVC资源自动生成PV资源,减少了维护成本
posted @ 2021-07-29 21:02  梦君子  阅读(626)  评论(1编辑  收藏  举报