4.2 K8s存储之 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可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。
Delete删除策略:表示删除pvc时,pv也会一起删除,同事也删除pv所指向的实际存储空间。
Retain保留策略:表示删除pvc时,pv不会一起删除,而是变成Released状态,等待管理员手动清理。
Recycle回收策略:将执行清除操作,之后可以被新的pvc使用,需要插件支持。
- Available – 资源尚未被claim使用
- Bound – 已经绑定到某个pvc上
- Released – 对应的pvc已经被删除,卷处于释放状态,但是资源还没有被集群回收
- Failed – 自动回收失败
不同情况下,PV和PVC的状态变化如下:
| 操作 | PV状态 | PVC状态 |
|---|---|---|
| 1.创建 PV | Available | - |
| 2.创建PVC | Available | Pending |
| 3.过几秒钟 | Bound | Bound |
| 4.删除PV | -/Terminating | Lost/Bound |
| 5.重新创建PV | Bound | Bound |
| 6.删除PVC | Released | - |
| 7.后端存储不可用 | Failed | - |
| 8.删除PV的claimRef | Available | - |
- ReadWriteOnce – 卷可以被一个节点以读写方式挂载;
- ReadOnlyMany – 卷可以被多个节点以只读方式挂载;
- ReadWriteMany – 卷可以被多个节点以读写方式挂载。
上述中的节点是指的k8s的node,而不是pod。
重要提醒! 每个卷只能同一时刻只能以一种访问模式挂载,即使该卷能够支持 多种访问模式。例如,一个Glusterfs卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式 挂载。
访问模式只是能力描述,并不是强制执行的,对于没有按pvc声明的方式使用pv,存储提供者应该负责访问时的运行错误。例如如果设置pvc的访问模式为
ReadOnlyMany,pod挂载后依然可写,如果需要真正的不可写,申请pvc是需要指定readOnly: true参数

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

[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>
[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/的子目录下进行挂载了,而直接挂载到映射到容器中。