ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

4.2 K8s存储之 Pv& Pvc

PV&PVC

概述

​ PersistentVolume(pv)和PersistentVolumeClaim(pvc)是k8s提供的两种API资源,用于抽象存储细节,支持众多的后端存储实现,例如GCEPersistentDisk、AWSElasticBlockStore、NFS、iSCSI、RBD (Ceph Block Device)、Glusterfs、AzureFile、AzureDisk、CephFS、cinder、FC、FlexVolume、Flocker、PhotonPersistentDisk、Quobyte、VsphereVolume、HostPath(single node testing only)……等。

​ 存储管理员提供存储基础设施,k8s管理员关注于如何基于存储设施通过pv提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。 pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式,这就可以通过Provision -> Claim 的方式,来对存储资源进行控制。

​ pv和pvc都是k8s的标准资源对象。pv与pvc是一一对应关系。一旦pvc与pv绑定后,此时该pvc就相当于一个存储卷

pvc在创建时,必须有满足条件的pv存在,否则pvc的状态会一直处在peding。

pv的回收策略(RECLAIM POLICY)

pv可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。

  • Delete删除策略:表示删除pvc时,pv也会一起删除,同事也删除pv所指向的实际存储空间。

  • Retain保留策略:表示删除pvc时,pv不会一起删除,而是变成Released状态,等待管理员手动清理。

  • Recycle回收策略:将执行清除操作,之后可以被新的pvc使用,需要插件支持。

pv的状态(STATUS)

  • Available – 资源尚未被claim使用
  • Bound – 已经绑定到某个pvc上
  • Released – 对应的pvc已经被删除,卷处于释放状态,但是资源还没有被集群回收
  • Failed – 自动回收失败

不同情况下,PV和PVC的状态变化如下:

操作PV状态PVC状态
1.创建 PVAvailable-
2.创建PVCAvailablePending
3.过几秒钟BoundBound
4.删除PV-/TerminatingLost/Bound
5.重新创建PVBoundBound
6.删除PVCReleased-
7.后端存储不可用Failed-
8.删除PV的claimRefAvailable-

pv访问权限accessModes

  • ReadWriteOnce – 卷可以被一个节点以读写方式挂载;
  • ReadOnlyMany – 卷可以被多个节点以只读方式挂载;
  • ReadWriteMany – 卷可以被多个节点以读写方式挂载。

上述中的节点是指的k8s的node,而不是pod。

重要提醒! 每个卷只能同一时刻只能以一种访问模式挂载,即使该卷能够支持 多种访问模式。例如,一个Glusterfs卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式 挂载。

访问模式只是能力描述,并不是强制执行的,对于没有按pvc声明的方式使用pv,存储提供者应该负责访问时的运行错误。例如如果设置pvc的访问模式为ReadOnlyMany ,pod挂载后依然可写,如果需要真正的不可写,申请pvc是需要指定 readOnly: true 参数

1535363942777

上图所示:pod与pvc处于同一个名称空间

1535363980041

示例

定义pv:

[root@k8smaster volumes]# cat pv-demo.yaml 
apiVersion: v1
kind: PersistentVolume  #pv的定义与volume在pod中的定义一致
metadata: 
  name: pv001  #pv属于集群级别,不属于任何名称空间,不能定义在名称空间中
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: 192.168.5.241 
  accessModes:
  - ReadWriteMany
  - ReadOnlyMany
  - ReadWriteOnce
  capacity:
    storage: 2Gi #这个是pv申请的大小,但是应用向其中写入数据是没有大小限制的。因为底层支持无限大小。
---
apiVersion: v1
kind: PersistentVolume
metadata: 
  name: pv002  #定义第2个pv
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: 192.168.5.241 
  accessModes:
  - ReadWriteMany
  - ReadOnlyMany
  - ReadWriteOnce
  capacity:
    storage: 5Gi 
---
apiVersion: v1
kind: PersistentVolume
metadata: 
  name: pv003  #定义第3个pv
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: 192.168.5.241 
  accessModes:
  - ReadOnlyMany
  - ReadWriteOnce
  capacity:
    storage: 10Gi 

如何查看pv使用的后端真实存储路径?

# kubectl describe pv pvc-66eb62f9-7d55-4dfb-8030-5b608aecf33a                                        
Name:            pvc-66eb62f9-7d55-4dfb-8030-5b608aecf33a
Labels:          <none>
Annotations:     Description: Gluster-Internal: Dynamically provisioned PV
                 gluster.kubernetes.io/heketi-volume-id: 46e2930863ab05053ec3cbbc49120e7d
                 gluster.org/type: file
                 kubernetes.io/createdby: heketi-dynamic-provisioner
                 pv.beta.kubernetes.io/gid: 2001
                 pv.kubernetes.io/bound-by-controller: yes
                 pv.kubernetes.io/provisioned-by: kubernetes.io/glusterfs
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    glusterfs-storage
Status:          Bound
Claim:           default/pvc-cc-main-2
Reclaim Policy:  Delete
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:         
Source:
    Type:                Glusterfs (a Glusterfs mount on the host that shares a pod's lifetime)
    EndpointsName:       glusterfs-dynamic-66eb62f9-7d55-4dfb-8030-5b608aecf33a
    EndpointsNamespace:  0xc000450bb0
    Path:                vol_46e2930863ab05053ec3cbbc49120e7d  #pv使用的基础设施存储路径
    ReadOnly:            false
Events:                  <none>

定义pvc,并在pod中使用pvc:

[root@k8smaster volumes]# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default  #pvc需要与使用pvc的pod在同一个命名空间(注意:pvc是集群级别的资源)
spec:
  accessModes:  #该accessModes必须是某个pv的子集,才能匹配
  - ReadWriteMany
  resources:  #pvc在创建时,自动找到满足条件的pv进行绑定,否则pvc一直处于peding状态
    requests:
      storage: 2Gi  #最少资源限制,pv的容器必须大于等于该值才能匹配
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp-vol-pvc
  namespace: default
spec:
  containers:
  - name: myapp-pod-vol-pvc
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html  #要挂载的volume名称
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    persistentVolumeClaim:   #定义pvc类型的存储卷
      claimName: mypvc
      #readOnly: true

远程存储实现挂载原理

以docker运行时、远程存储为cephfs为例,Pod使用cephfs类型的远程存储。首先cephfs存储会挂载到Pod所在节点的/var/lib/kubelet/pods/${PODID}/volumes/下的一个子目录上,然后docker会使用-v参数将宿主机上的目录映射到容器中。所以hostpath类型的volume,就不会再在/var/lib/kubelet/pods/${PODID}/volumes/的子目录下进行挂载了,而直接挂载到映射到容器中。