31、K8S-数据存储之PV、PVC

发布时间 2023-03-27 15:50:52作者: 小粉优化大师

1、基础知识

1.1、流程梳理

1.1.1、PV-Persistent Volume

之前我们提到的Volume可以提供多种类型的资源存储(可持久或不持久),但是它定义在Pod上的,是属于"
源对象"的一部分。工作中的存储资源一般都是独立的,这就是资源对象Persistent Volume(PV),是由管
理员设置的存储,它是群集的一部分,PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。。
Persistent Volume 跟Volume类似,区别就是:
   PV就是Kubernetes集群中的网络存储,不属于Node、Pod等资源,但可以被他们访问。
 PV是独立的网络存储资源对象,有自己的生命周期,支持很多种volume类型

1.1.2、PVC-Persistent Volume Claim

Persistent Volume Claim(PVC) 是一个网络存储服务的请求。PVC和PV与Pod之间的关系。
   Pod能够申请特定的CPU和MEM资源,但是Pod只能通过PVC到PV上请求一块独立大小的网络存储空间,
而PVC 可以动态的根据用户请求去申请PV资源,不仅仅涉及到存储空间,还有对应资源的访问模式,对于真
正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。

1.1.3、pod、pv、pvc关联关系

注意事项:
      pod、pvc、pv 必须在同一个命名空间。
前提:
     存储管理员配置各种类型的PV对象、
用户需要存储资源的时候:
1、用户根据资源需求创建PVC,由PVC自动匹配(权限、容量)合适的PV对象。
2、在pod内部通过PVC将pv绑定到当前的空间,进行使用。
3、如果用户不使用存储资源的话,解绑pvc和pod即可。

1.2、PV、PVC的生命周期图

1.2.1、流程细节说明

1、用户创建了一个包含 PVC 的 Pod,该 PVC 要求使用动态存储卷;
2、Scheduler 根据 Pod 配置、节点状态、PV 配置等信息,把 Pod 调度到一个合适的 Worker 节点上;
3、PV 控制器 watch 到该 Pod 使用的 PVC 处于 Pending 状态,于是调用 Volume Plugin(intree)创建存储卷,并创建 PV 对象(out-of-tree 由 External Provisioner 来处理);
4、AD 控制器发现 Pod 和 PVC 处于待挂接状态,于是调用 Volume Plugin 挂接存储设备到目标Worker 节点上
5、在 Worker 节点上,Kubelet 中的 Volume Manager 等待存储设备挂接完成,并通过 Volume 
Plugin 将设备挂载到全局目录: **/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PVname]**(以 iscsi 为例);
6、Kubelet 通过 Docker 启动 Pod 的 Containers,用 bind mount 方式将已挂载到本地全局目录的卷映射到容器中。

1.3、SC-StorageClass

1.3.1、需求

对于我们学习到的 PV 和 PVC 的使用方法而言,整个过程是比较繁琐的,不仅仅需要自己定义PV和PVC还需
要将其与Pod进行关联,而且对于PV和PVC的适配我们也要做好前提规划,而生产环境中,这种繁琐的事情是
有悖于我们使用kubernetes的原则的,而且这种方式在很大程度上并不能满足我们的需求,,而且不同的应
用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,比如我们有一个应用需要对存储的
并发度要求比较高,而另外一个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型的应用简单
的来使用静态的 PV 就很不合适了,这种情况下我们就需要用到动态 PV。

1.3.2、简介

Kubernetes 为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可
以将存储资源定义为某种类型的资源,比如存储质量、快速存储、慢速存储等,为了满足不同用户的多种多样
的需求,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
所以,StorageClass提供了一种资源使用的描述方式,使得管理员能够描述提供的存储的服务质量和等级,进而做出不同级别的存储服务和后端策略。

1.4、PV、PVC属性解析

1.4.1、PV-属性

PV的配置方法
静态: 集群管理员预制创建一些 PV。它们带有可供群集用户使用的实际存储的细节。
动态: 集群尝试根据用户请求动态地创建卷。此配置基于 StorageClasses:PVC 必须请求存储类,并且管 理员必须创建并配置该类才能进行动态创建。声明该类为
"" 可以有效地禁用其动态配置。
PV的属性信息 PV 作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息
kubectl explain pv.spec capacity 定义pv使用多少资源,仅限于空间的设定 accessModes   访问模式存储类型 每种存储类型的样式的属性名称都是专有的。 persistentVolumeReclaimPolicy 资源回收策略,主要三种Retain、Delete、Recycle

1.4.2、PVC-属性

它与所有空间都能使用的pv不一样,pvc是属于名称空间级别的资源对象,也就是说只有特定的资源才能使用
 kubectl explain pods.spec.volumes.persistentVolumeClaim
   claimName 定义pvc的名称
   readOnly 设定pvc是否只读
kubectl explain pvc.spec
  accessModes 访问模式 *
  resources 资源限制 *
  selector   标签选择器
  storageClassName 动态存储名称
  volumeMode 后端存储卷的模式
  volumeName 指定卷(pv)的名称

1.5、PV生命周期状态图

状态            解析
Availabled      空闲状态,表示pv没有被其他对象使用
Bound           绑定状态,表示pv已经被其他对象使用
Released        未回收状态,表示pvc已经被删除了,但是资源没有被回收
Faild           资源回收失败

注意:这个过程是单向过程,不能逆向。

1.6、AccessModes属性介绍

AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括

类型                    解析
ReadWriteOnce(RWO)    单节点读写
ReadOnlyMany(ROX)     多节点只读
ReadWriteMany(RWX)    多节点读写
ReadWriteOncePod(RWOP)  单pod读写

注意:
不同的后端存储支持不同的访问模式,所以要根据后端存储类型来设置访问模式。
一些 PV 可能支持多种访问模式,但是在挂载的时候只能使用一种访问模式,多种访问模式是不会生效的

1.7、PV - Retain、Recycle、Delete三种资源回收策略

当pod结束 volume 后可以回收资源对象删除PVC,而绑定关系就不存在了,当绑定关系不存在后这个PV需
要怎么处理,而PersistentVolume 的回收策略告诉集群在存储卷声明释放后应如何处理该卷。目前,
volume 的处理策略有保留、回收或删除。

类型        解析
Retain      保留存储空间数据,一般推荐使用此项,但是数据的删除需要人工干预
Recycle     清空存储空间
Delete      相关的存储实例一并删除。

注意:
    目前,只有 NFS 和 HostPath 支持 Recycle 策略,其他支持 Delete 策略

2、PV & PVC实践

2.1、PV-实践

2.1.1、需求

PV对象可以有很多常见的类型:本地磁盘、NFS、分布式文件系统...我们接下来就以常见的NFS类型创建一个3G大小的存储资源对象
因为我们有一个现成的nfs环境,所以我们就创建一个NFS类型的pv(使用hostPath方式也可以)

2.1.2、定义资源配置清单

cat > storage-pv.yml<<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-test
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /nfs-data
    server: 192.168.10.33
EOF

# 注意:
虽然我们在创建pv的时候没有指定回收策略,而其策略自动帮我们配置了Retain

2.1.3、应用资源配置清单

master1 ]# kubectl apply -f storage-pv.yml 
persistentvolume/pv-test created

master1 ]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-test   3Gi        RWO            Retain           Available                                   3s

master1 ]# kubectl describe pv pv-test 
Name:            pv-test
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    
Status:          Available
Claim:           
Reclaim Policy:  Retain
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        3Gi
Node Affinity:   <none>
Message:         
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.10.33
    Path:      /nfs-data
    ReadOnly:  false
Events:        <none>

2.1.4、配置清单属性解析

如果该pv的使用方式多的话,可以这么写:accessModes: ["ReadWriteMany","ReadWriteOnce"]

如果nfs提供的目录空间个数多的话,我们这里可以同样的方式写多个。
 
Pv的资源对象类型:PersistentVolume
存储空间大小设定:capacity

存储空间的访问模式:accessModes
    ReadWriteOnce —— 该volume只能被单个节点以读写的方式映射
    ReadOnlyMany —— 该volume可以被多个节点以只读方式映射
    ReadWriteMany —— 该volume只能被多个节点以读写的方式映射

可以指定回收策略:persistentVolumeReclaimPolicy: Recycle

2.2、PVC-实践

2.2.1、定义资源配置清单

cat >storage-pvc.yml<<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
EOF

2.2.2、应用资源配置清单

master1 ]# kubectl apply -f storage-pvc.yml 
persistentvolumeclaim/pvc-test created

master1 ]# kubectl get pvc
NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-test   Bound    pv-test   3Gi        RWO                           4s

# 一旦我们启动pvc之后,他会自动去搜寻合适的可用的pv,然后绑定在一起,否则的话,pvc找不到对应
的pv资源的话,他的状态会一直处于pending。
master1 ]# kubectl describe pvc pvc-test 
Name:          pvc-test
Namespace:     default
StorageClass:  
Status:        Bound
Volume:        pv-test
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      3Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Used By:       <none>
Events:        <none>

3、应用实践

3.1、Nginx目录PVC挂载-实践

3.1.1、定义资源配置清单

cat >nginx-pvc.yml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  volumes:
    - name: nginx-volume
      persistentVolumeClaim:
        claimName: pvc-test
  containers:
    - name: nginx-pv
      image: 192.168.10.33:80/k8s/my_nginx:v1
      volumeMounts:
      - name: nginx-volume
        mountPath: "/usr/share/nginx/html"
EOF

3.1.2、属性解析

spec.volumes 是针对pod资源申请的存储资源来说的,我们这里使用的主要是pvc的方式。
spec.containers.volumeMounts 是针对pod资源对申请到的存储资源的具体使用信息。

这里将本地的nfs目录挂载到

3.1.3、应用资源配置清单

master1 ]# kubectl apply -f nginx-pvc.yml 
pod/nginx-pod created

master1 ]# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
nginx-pod   1/1     Running   0          22s

master1 ]# kubectl describe pod nginx-pod 
...
    Mounts:
      /usr/share/nginx/html from nginx-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-tj96q (ro)
...
Volumes:
  nginx-volume:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  pvc-test
    ReadOnly:   false
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  86s   default-scheduler  Successfully assigned default/nginx-pod to node1
  Normal  Pulled     85s   kubelet            Container image "192.168.10.33:80/k8s/my_nginx:v1" already present on machine
  Normal  Created    85s   kubelet            Created container nginx-pv
  Normal  Started    85s   kubelet            Started container nginx-pv
  
# 设置nginx默认主页
register ~]# echo "Hello Kubernetes PV" > /nfs-data/index.html

# 测试是否正常访问
master1 ]# curl  10.244.3.230
Hello Kubernetes PV

3.2、subPath-实践

3.2.1、需求

当前的nginx首页存放在/nfs-data的一级目录中,但是生产中,一个nfs肯定是对多个应用来使用的,所以
我们需要定制app的子目录首页,但是如果我们采用定制pv的方式,有些太繁琐了,有没有什么办法解决这种场景问题呢?

3.2.2、属性解析

这个功能其实可以直接在容器内部的属性直接实现,而无需对pv和pvc进行变动,这个属性是:
 kubectl explain deployment.spec.template.spec.containers.volumeMounts
 subPath     <string>
 属性解析:
    subPath的属性主要的目的是设置数据卷的源地址,
    不设置的话,表示是源地址的根目录
    设置的话,表示是源地址的子目录

3.2.3、定义资源配置清单

cat >nginx-pvc-subdir.yml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  volumes:
    - name: nginx-volume
      persistentVolumeClaim:
        claimName: pvc-test
  containers:
    - name: nginx-pv
      image: 192.168.10.33:80/k8s/my_nginx:v1
      volumeMounts:
      - name: nginx-volume
        mountPath: "/usr/share/nginx/html"
        subPath: web1
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-flask
spec:
  volumes:
    - name: nginx-volume
      persistentVolumeClaim:
        claimName: pvc-test
  containers:
    - name: nginx-flask
      image: 192.168.10.33:80/k8s/my_nginx:v1
      volumeMounts:
      - name: nginx-volume
        mountPath: "/usr/share/nginx/html"
        subPath: web2
EOF

3.2.4、应用资源配置清单

master1 ]# kubectl apply -f nginx-pvc-subdir.yml 
pod/nginx-pod created
pod/nginx-flask created

master1 storage]# kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
nginx-flask   1/1     Running   0          2m    10.244.3.232   node1   <none>           <none>
nginx-pod     1/1     Running   0          2m    10.244.3.231   node1   <none>           <none>


# 往NFS写入测试的默认主页
register ~]# echo "web1 home">/nfs-data/web1/index.html
register ~]# echo "web2 home">/nfs-data/web2/index.html

# 访问测试
master1 ]# curl 10.244.3.232
web2 home

master1 ]# curl 10.244.3.231
web1 home

3.3、多PV、PVC-实践

3.3.1、NFS创建目录

register nfs-data]# mkdir redis00{1..3}

3.3.2、定义资源配置清单-创建多个pv

cat >pv-nfs-mul.yml<<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-001
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path:  "/nfs-data/redis001"
    server: 192.168.10.33
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-002
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path:  "/nfs-data/redis002"
    server: 192.168.10.33
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-003
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path:  "/nfs-data/redis003"
    server: 192.168.10.33
EOF

3.3.3、应用pv资源配置清单

master1 ]# kubectl apply -f pv-nfs-mul.yml 
persistentvolume/pv-nfs-001 created
persistentvolume/pv-nfs-002 created
persistentvolume/pv-nfs-003 created

master1 ]# kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   REASON   AGE
pv-nfs-001   5Gi        RWX            Retain           Available                                              3s
pv-nfs-002   5Gi        ROX            Retain           Available                                              3s
pv-nfs-003   1Gi        RWO            Retain           Available                                              3s
pv-test      3Gi        RWO            Retain           Bound       default/pvc-test                           177m

3.3.4、定义资源配置清单-创建PVC

cat >pvc-mul.yml<<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-demo-0001
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  volumeMode: Filesystem
  resources:
    requests:
      storage: 3Gi
    limits:
      storage: 10Gi
EOF

3.3.5、应用资源配置清单

master1 ]# kubectl apply -f pvc-mul.yml 
persistentvolumeclaim/pvc-demo-0001 created

master1 ]# kubectl get pvc
NAME            STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-demo-0001   Bound    pv-nfs-001   5Gi        RWX                           2s
pvc-test        Bound    pv-test      3Gi        RWO                           97m

master1 ]# kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS   REASON   AGE
pv-nfs-001   5Gi        RWX            Retain           Bound       default/pvc-demo-0001                           4m34s
pv-nfs-002   5Gi        ROX            Retain           Available                                                   4m34s
pv-nfs-003   1Gi        RWO            Retain           Available                                                   4m34s
pv-test      3Gi        RWO            Retain           Bound       default/pvc-test                                3h1m

4、PV、PVC资源释放

4.1、删除pvc

# 在删除pvc的时候,应该先删除pvc被应用的pod资源
kubectl delete -f nginx-pvc.yml 
kubectl delete -f storage-pvc.yml 

master1 ]# kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS   REASON   AGE
pv-nfs-001   5Gi        RWX            Retain           Bound       default/pvc-demo-0001                           10m
pv-nfs-002   5Gi        ROX            Retain           Available                                                   10m
pv-nfs-003   1Gi        RWO            Retain           Available                                                   10m
pv-test      3Gi        RWO            Retain           Released    default/pvc-test                                3h7m
# 当移除pvc后,pv资源就被释放了,pv的状态变成了 Released。

4.2、强制删除

4.2.1、需求

生产中,对于存储资源的释放,最好按照流程来,即先清空应用,然后在清空pvc,但是生产中,经常遇
到应用资源意外终止或者其他情况,导致我们的pvc资源没有使用,而且也没有清空,我们推荐有多种方式,
最常用的一种方式就是,在所有的应用pod中增加一个prestop的钩子函数,从而让我们的应用资源合理的清空。
而对于特殊的异常情况,我们还有另外一种策略,即强制清空--一般不推荐使用。

4.2.2、正常删除

master1 ]# kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS   REASON   AGE
pv-nfs-001   5Gi        RWX            Retain           Bound       default/pvc-demo-0001                           13m
pv-nfs-002   5Gi        ROX            Retain           Available                                                   13m
pv-nfs-003   1Gi        RWO            Retain           Available                                                   13m
pv-test      3Gi        RWO            Retain           Released    default/pvc-test                                3h10m

master1 ]# kubectl delete pv pv-nfs-002

4.2.3、强制删除

master1 ]# kubectl delete pv pv-nfs-003 --grace-period=0 --force=true