Kubernetes PV与PVC
首先我们来了解一下什么是PV和PVC
PV的全称是: PersistentVolume (持久化卷),是对底层的共享存储的一种抽象,PV由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接
PVC的全称是: PersistenVolumeClaim (持久化卷声明),PVC是用户存储的一种声明,PVC和Pod比较类型,Pod是消耗节点,PVC消耗的是PV资源,Pod可以请求CPU的内存,而PVC可以请求特定的存储空间和访问模式。对于真正存储的用户不需要关心底层的存储实现细节,只需要直接使用PVC即可
但是通过PVC请求一定的存储空间也很有可能不足以满足对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求也能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes又为我们引入了一个新的资源对象: StorageClass,通过StorageClass的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据StorageClass的描述就可以非常直观的知道各种存储资源特性了,这样就可以根据应用的特性去申请合适的存储资源了
PV和PVC的生命周期
PV可以看作可用的存储资源,PVC则是对存储资源的需求,PV和PVC的互相关系遵循如下图

1.资源供应 (Provisioning)
Kubernetes支持两种资源的供应模式:静态模式(Staic)和动态模式(Dynamic)。资源供应的结果就是创建好的PV。
静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置
动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种 "类型(Class)"。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及PVC的绑定。PVC可以声明Class为"",说明该PVC禁止使用动态模式
2.资源绑定 (Binding)
在用户定义好PVC后,系统将根据PVC对存储资源的请求 (存储空间和访问模式)在已存在的PV中选择一个满足PVC要求的PV,一旦找到,就将该PV与用户定义的PVC进行绑定,然后用户的应用就可以使用这个PVC了。如果系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合要求的PV。PV一旦绑定在某个PVC上,就被这个PVC独占,不能再与其他PVC进行绑定了。在这种情况下,当PVC申请的存储空间比PV的少时,整个PV的空间都能够为PVC所用,可能会造成资源的浪费。如果资源供应使用的是动态模式,则系统在PVC找到合适的StorageClass后,将会自动创建PV并完成PVC的绑定
3.资源使用 (Using)
Pod 使用volume的定义,将PVC挂载到容器内的某个路径进行使用。volume的类型为persistentVoulumeClaim,在容器应用挂载了一个PVC后,就能被持续独占使用。不过,多个Pod可以挂载同一个PVC,应用程序需要考虑多个实例共同访问一块存储空间的问题
4.资源释放 (Releasing)
当用户对存储资源使用哪个完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能继续使用
5.资源回收 (Reclaiming)
对于PV,管理员可以设定回收策略(Reclaim Policy)用于设置与之绑定的PVC释放资源之后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
1.静态资源下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制

2.动态资源下,通过StorageClass和PVC完成资源动态绑定 (系统自动生成PV,并供Pod使用的存储管理机制)

使用NFS进行演示
首先我们需要安装NFS服务
[root@kubernetes-m mnt]# yum install -y nfs-utils rpcbind
[root@kubernetes-w-1 mnt]# yum install -y nfs-utils rpcbind
[root@kubernetes-w-2 mnt]# yum install -y nfs-utils rpcbind
[root@virtual_cloud ~]# yum install -y nfs-utils rpcbind
[root@virtual_cloud ~]# mkdir /nfs
[root@virtual_cloud ~]# vim /etc/exports
[root@virtual_cloud ~]# cat /etc/exports
/nfs *(rw,no_root_squash,sync)
[root@virtual_cloud ~]# systemctl enable nfs --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
[root@virtual_cloud ~]# exportfs -r
[root@virtual_cloud ~]# showmount -e
Export list for virtual_cloud:
/nfs *
我们nfs server端已经完毕,接下来在所有需要nfs挂载的集群节点安装以下
[root@kubernetes-m mnt]# mkdir /nfs
[root@kubernetes-w-1 mnt]# mkdir /nfs
[root@kubernetes-w-2 mnt]# mkdir /nfs
[root@kubernetes-m mnt]# mount -t nfs 10.0.0.13:/nfs /nfs/
[root@kubernetes-w-1 mnt]# mount -t nfs 10.0.0.13:/nfs /nfs/
[root@kubernetes-w-2 mnt]# mount -t nfs 10.0.0.13:/nfs /nfs/
[root@kubernetes-m mnt]# df
10.0.0.13:/nfs 28289664 3009536 25280128 11% /nfs
[root@kubernetes-w-1 mnt]# df
10.0.0.13:/nfs 28289664 3009536 25280128 11% /nfs
[root@kubernetes-w-2 mnt]# df
10.0.0.13:/nfs 28289664 3009536 25280128 11% /nfs
创建PV
有了我们NFS共享存储,下面就可以来使用PV和PVC。PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息。这里使用nfs类型的后端存储,1g存储空间,访问模式为ReadWriteOnce,回收策略为Recyle,对应文件如下
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs #pv名称
spec:
capacity: #存储能力,一个pv对象都要指定一个存储能力,目前仅支持存储空间的设置
storage: 1Gi #存储空间
accessModes:
- ReadWriteOnce #访问模式
persistentVolumeReclaimPolicy: Recycle #回收策略
nfs: #服务模式 (nfs、ceph、hostpath等)
path: /nfs #共享数据目录挂载点
server: 10.0.0.13 #nfs服务器地址
[root@kubernetes-m mnt]# kubectl create -f pv.yaml
persistentvolume/pv-nfs created
[root@kubernetes-m mnt]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWO Recycle Available 4s
# 参数解释
pv1 名称
1Gi 代表存储空间大小
RWO 访问模式(ReadWriteOnce缩写)
Recycle 回收策略
Available PV状态
PV相关配置说明
[√] Capacity 存储能力
[√] 通过PV的capacity属性来设置存储空间,目前仅支持storage=数据大小,未来可能会加入IOPS、吞吐量等指标配置
[√] AccessModes 访问模式
[√] AccessModes 是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限
[√] ReadWriteOnce (RWO):读写权限,但是只能被单个节点挂载
[√] ReadOnlyMany (ROX):只读权限,可能被多个节点挂载
[√] ReadWriteMany (RWX):读写权限,可以被多个节点挂载
注意:一些PV可能支持多种访问模式,但挂载的时候只可以使用一种访问模式,多种访问模式不会生效
下面是一些常用的Volume插件支持的访问模式(需要根据我们配置的类型进行选择对应的访问模式)

persistentVolumeReclaimPolicy回收策略
[√] Retain (保留) 保留数据,需要管理员手动清理
[√] Recycle (回收) 清除PV中的数据,效果相当于执行删除命令
[√] Delete (删除) 与PV相连的后端存储完成volume的删除操作,常见于云服务商的存储服务
不过需要注意的是,目前只有NFS和HostPath两类支持回收策略,一般设置Retain比较保险
状态
1.Available (可用): 表示可用状态,还未被任何PVC绑定
2.Bound (已绑定):已经绑定到某个PVC
3.Released (已释放):对应的PVC已经删除,但资源还没有被集群收回
4.Failed:PV自动回收失败
创建PVC
前面说过,PV实际上没有创建存储,相当于我们node一样,还需要创建Pod进行消费,接下来我们进行PVC的创建与配置、
#前面我们已经在集群上都安装nfs客户端,并且进行挂载了。下面进行创建pvc
新建pvc同样需要建立一个数据卷声明
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
接下来我们进行创建,pvc的yaml文件基本上和pv相同,这里不过多解释。
在Kubernetes中会自动帮我们查看pv状态为Available并且根据声明pvc容量storage的大小进行筛选匹配,同时还会根据AccessMode进行匹配。如果pvc匹配不到pv会一直处于pending状态。
使用Labels匹配PV与PVC
同时,pv与pvc中间还可以通过label标签进行匹配,配置如下
#记得我们需要修改一下名字,名字是不可以重复的
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
labels: #这里将pv设置一个labels
app: pv-nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /nfs
server: 10.0.0.13
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
selector: ##pvc匹配标签为app=pv-nfs的pv
matchLabels:
app: pv-nfs
##接下来进行创建
[root@kubernetes-m mnt]# kubectl create -f pv-pvc.yaml
persistentvolume/pv-nfs created
persistentvolumeclaim/pvc-nfs created
[root@kubernetes-m mnt]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pv-nfs 10Gi RWO Recycle Bound default/pvc-nfs 8s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc-nfs Bound pv-nfs 10Gi RWO 8s
有一点需要注意,当我们pvc申请的容量小于我们pv的容量是可以进行绑定的,当我们申请pvc的容量大于pv的容量是无法进行绑定的。 这里需要注意
Deployment引用pvc
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
labels:
app: pv-nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /nfs
server: 10.0.0.13
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
selector:
matchLabels:
app: pv-nfs
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts: #挂载,首先添加需要挂载的目录
- name: nginx #挂载点的名称
mountPath: /usr/share/nginx/html #挂载点的路径
volumes: #绑定
- name: nginx
persistentVolumeClaim: #将镜像中的nginx目录挂载到下面名称的pvc中
claimName: pvc-nfs #pvc名称
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: nginx
[root@kubernetes-m mnt]# kubectl create -f pv-pvc-nginx-svc.yaml
persistentvolume/pv-nfs created
persistentvolumeclaim/pvc-nfs created
deployment.apps/nginx created
service/nginx-svc created
[root@kubernetes-m mnt]# kubectl get pv,pvc,pod,svc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pv-nfs 10Gi RWO Recycle Bound default/pvc-nfs 12s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/pvc-nfs Bound pv-nfs 10Gi RWO 12s
NAME READY STATUS RESTARTS AGE
pod/nginx-574576f8dc-57psf 1/1 Running 0 12s
pod/nginx-574576f8dc-59778 1/1 Running 0 12s
pod/nginx-574576f8dc-x7wpw 1/1 Running 0 12s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 100.1.0.1 <none> 443/TCP 13d
service/nginx-svc NodePort 100.1.238.209 <none> 80:32145/TCP 11s
接下来我们直接访问nginx是无法访问的,因为在我们nfs挂载点的目录下面没有文件,所以无法访问
接下来我们到nfs挂载点创建一个index.html
[root@virtual_cloud ~]# cd /nfs/
[root@virtual_cloud nfs]# echo "Is My Nginx" >>index.html
然后我们在进行访问查看\
[root@kubernetes-m mnt]# curl 10.0.0.10:32145
Is My Nginx
由于我们的index.html直接挂在到了/nfs目录下面,如果有很多个pod都使用pvc进行挂载,会造成我们数据目录的文件比较乱
这里我们添加一个subpathsubPath的目的是为了在单一Pod中多次使用同一个volume而设计的。
---
volumeMounts: #挂载,首先添加需要挂载的目录
- name: nginx #挂载点的名称
mountPath: /usr/share/nginx/html #挂载点的路径\
subPath: nginx-pvc
volumes: #绑定
---
当我们更新完pod之后,等pod正常启动。就可以看到在我们nfs存储目录下面单独创建了一个名称为nginx-pvc的目录,这个目录实际上就是我们subpath后面指定的名称
[root@virtual_cloud nfs]# ls
nginx-pvc
这个目录下面也是没有任何文件的,我们需要将原来index.html拷贝过去即可
[root@virtual_cloud nfs]# echo "Is My Nginx - 2" >>nginx-pvc/index.html
[root@kubernetes-m mnt]# curl 10.0.0.10:31190
Is My Nginx - 2
现在我们删除deployment,下面的数据并不会删除。这样使用pv和pvc持久化就完成
如果我们直接删除或者有pod在使用pv或者pvc是无法直接删除的,当我们使用Recycle模式时,删除所有pv和pvc后,数据也会进行删除。所以删除pv和pvc请谨慎操作