,给大家带来 PersistentVolume(PV) & PersistentVolumeClaim(PVC) 的讲解。

    现在讲Volume里面在生产中用的最多的PersistentVolume(持久卷,简称PV)和 PersistentVolumeClaim(持久卷消费,简称PVC),通常在企业中,Volume是由存储系统的管理员来维护,他们来提供pv,pv具有持久性,生命周期独立于Pod;Pod则是由应用的开发人员来维护,如果要进行一卷挂载,那么就写一个pvc来消费pv就可以了,K8s会查找并提供满足条件的pv。

    有了pvc,我们在K8s进行卷挂载就只需要考虑要多少容量了,而不用关心真正的空间是用什么存储系统做的等一些底层细节信息,pv这些只有存储管理员才应用去关心它。

    K8s支持多种类型的pv,我们这里就以生产中常用的NFS来作演示(在云上的话就用NAS),生产中如果对存储要求不是太高的话,建议就用NFS,这样出问题也比较容易解决,如果有性能需求,可以看看rook的ceph,以及Rancher的Longhorn,这些我都在生产中用过,如果有需求的同学可以在评论区留言,我会单独做课程来讲解。

    开始部署NFS-SERVER

    # 我们这里在10.0.1.201上安装(在生产中,大家要提供作好NFS-SERVER环境的规划)
    # yum -y install nfs-utils
    
    # 创建NFS挂载目录
    # mkdir /nfs_dir
    # chown nobody.nobody /nfs_dir
    
    # 修改NFS-SERVER配置
    # echo '/nfs_dir *(rw,sync,no_root_squash)' > /etc/exports
    
    # 重启服务
    # systemctl restart rpcbind.service
    # systemctl restart nfs-utils.service 
    # systemctl restart nfs-server.service 
    
    # 增加NFS-SERVER开机自启动
    # systemctl enable nfs-server.service 
    Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
    
    # 验证NFS-SERVER是否能正常访问
    # showmount -e 10.0.1.201                 
    Export list for 10.0.1.201:
    /nfs_dir *

    创建基于NFS的PV

    首先在NFS-SERVER的挂载目录里面创建一个目录

    # mkdir /nfs_dir/pv1

    接着准备好pv的yaml配置,保存为pv1.yaml

    # cat pv1.yaml 
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv1
      labels:
        type: test-claim    # 这里建议打上一个独有的标签,方便在多个pv的时候方便提供pvc选择挂载
    spec:
      capacity:
        storage: 1Gi     # <----------  1
      accessModes:
        - ReadWriteOnce     # <----------  2
      persistentVolumeReclaimPolicy: Recycle     # <----------  3
      storageClassName: nfs     # <----------  4
      nfs:
        path: /nfs_dir/pv1     # <----------  5
        server: 10.0.1.201
    1. capacity 指定 PV 的容量为 1G。
    2. accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有: ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。 ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。 ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。
    3. persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有: Retain – 需要管理员手工回收。 Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。 Delete – 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
    4. storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。
    5. 指定 PV 在 NFS 服务器上对应的目录,这里注意,我测试的时候,需要手动先创建好这个目录并授权好,不然后面挂载会提示目录不存在 mkdir /nfsdata/pv1 && chown -R nobody.nogroup /nfsdata 。

    创建这个pv

    # kubectl apply -f pv1.yaml 
    persistentvolume/pv1 created
    
    # STATUS 为 Available,表示 pv1 就绪,可以被 PVC 申请
    # kubectl get pv
    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
    pv1    1Gi        RWO            Recycle          Available           nfs                     4m45s

    接着准备PVC的yaml,保存为pvc1.yaml

    # cat pvc1.yaml 
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: pvc1
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
      storageClassName: nfs
      selector:
        matchLabels:
          type: test-claim

    创建这个pvc

    # kubectl apply -f pvc1.yaml          
    persistentvolumeclaim/pvc1 created
    
    # 看下pvc的STATUS为Bound代表成功挂载到pv了
    # kubectl get pvc           
    NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc1   Bound    pv1      1Gi        RWO            nfs            2s
    
    # 这个时候再看下pv,STATUS也是Bound了,同时CLAIM提示被default/pvc1消费
    # kubectl get pv
    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
    pv1    1Gi        RWO            Recycle          Bound    default/pvc1   nfs  

    下面我们准备pod服务来挂载这个pvc,这里就以上面最开始演示用的nginx的deployment的yaml配置来作修改

    # cat nginx.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: nginx
      name: nginx
    spec:
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        app: nginx
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx
      name: nginx
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - image: nginx
            name: nginx
            volumeMounts:    # 我们这里将nginx容器默认的页面目录挂载
              - name: html-files
                mountPath: "/usr/share/nginx/html"
          volumes:
            - name: html-files
              persistentVolumeClaim:  # 卷类型使用pvc,同时下面名称处填先创建好的pvc1
                claimName: pvc1

    更新配置

    # kubectl apply -f nginx.yaml 
    service/nginx unchanged
    deployment.apps/nginx configured
    
    # 我们看到新pod已经在创建了
    # kubectl get pod
    NAME                     READY   STATUS              RESTARTS   AGE
    nginx-569546db98-4nmmg   0/1     ContainerCreating   0          5s
    nginx-f89759699-6vgr8    1/1     Running             1          23h
    web-5bf769fdfc-44p7h     2/2     Running             0          113m
    
    # 我们这里直接用svc地址测试一下
    # kubectl get svc
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    kubernetes   ClusterIP   10.68.0.1       <none>        443/TCP   23h
    nginx        ClusterIP   10.68.238.54    <none>        80/TCP    23h
    web          ClusterIP   10.68.229.231   <none>        80/TCP    6h27m
    
    # 咦,这里为什么是显示403了呢,注意,卷挂载后会把当前已经存在这个目录的文件给覆盖掉,这个和传统机器上的磁盘目录挂载道理是一样的
    [root@node-1 ~]# curl 10.68.238.54
    <html>
    <head><title>403 Forbidden</title></head>
    <body>
    <center><h1>403 Forbidden</h1></center>
    <hr><center>nginx/1.19.5</center>
    </body>
    </html>
    
    # 我们来自己创建一个index.html页面
    # echo 'hello, world!' > /nfs_dir/pv1/index.html
    
    # 再请求下看看,已经正常了
    # curl 10.68.238.54                             
    hello, world!
    
    # 我们来手动删除这个nginx的pod,看下容器内的修改是否是持久的呢?
    # kubectl delete pod nginx-569546db98-4nmmg 
    pod "nginx-569546db98-4nmmg" deleted
    
    # 等待一会,等新的pod被创建好
    # kubectl get pod
    NAME                     READY   STATUS    RESTARTS   AGE
    nginx-569546db98-99qpq   1/1     Running   0          45s
    
    # 再测试一下,可以看到,容器内的修改现在已经被持久化了
    # curl 10.68.238.54        
    hello, world!
    
    # 后面我们再想修改有两种方式,一个是exec进到pod内进行修改,还有一个是直接修改挂载在NFS目录下的文件
    # echo 111 > /nfs_dir/pv1/index.html
    # curl 10.68.238.54  
    111

    下面讲下如何回收PVC以及PV

    # 这里删除时会一直卡着,我们按ctrl+c看看怎么回事
    # kubectl delete pvc pvc1 
    persistentvolumeclaim "pvc1" deleted
    ^C
    
    # 看下pvc发现STATUS是Terminating删除中的状态,我分析是因为服务pod还在占用这个pvc使用中
    # kubectl get pvc
    NAME   STATUS        VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc1   Terminating   pv1      1Gi        RWO            nfs            21m
    
    # 先删除这个pod
    # kubectl delete pod nginx-569546db98-99qpq 
    pod "nginx-569546db98-99qpq" deleted
    
    # 再看先删除的pvc已经没有了
    # kubectl get pvc
    No resources found in default namespace.
    
    # 根据先前创建pv时的数据回收策略为Recycle – 清除 PV 中的数据,这时果然先创建的index.html已经被删除了,在生产中要尤其注意这里的模式,注意及时备份数据,注意及时备份数据,注意及时备份数据
    # ll /nfs_dir/pv1/
    total 0
    
    # 虽然此时pv是可以再次被pvc来消费的,但根据生产的经验,建议在删除pvc时,也同时把它消费的pv一并删除,然后再重启创建都是可以的
    文档更新时间: 2021-07-28 16:51   作者:李延召