使用NFS提供存储,此时就要求用户会搭建NFS系统,并且会在yaml配置nfs。由于kubernetes支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes引入PV和PVC两种资源对象。
PV (Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
PVC (Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC其实就是用户向kubernetes系统发出的一种资源需求申请。
使用了PV和PVC之后,工作可以得到进一步的细分:
存储:存储工程师维护
PV:kubernetes管理员维护
PVC: kubernetes用户维护
PV是存储资源的抽象,PV资源是属于集群资源,跨namespace(不受命名空间隔离)
PV资源清单文件:
apiVersion: v1
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: #访问模式
storageClassName: #存储类别
persistentVolumeReclaimPolicy: #回收策略
PV的关键配置参数说明:
存储类型
底层实际存储的类型,kubernetes支持多种存储类型(nfs、cifs、glusterfs等),每种存储类型的配置都有所差异
存储能力(capacity)
目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置
访问模式(accessModes)
用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
ReadWriteOnce (RWO) :读写权限,但是只能被单个节点挂载
ReadOnlyMany (ROX) :只读权限,可以被多个节点挂载
ReadWriteMany (RWX) :读写权限,可以被多个节点挂载
需要注意的是,底层不同的存储类型可能支持的访问模式不同
回收策略(persistentVolumeReclaimPolicy)
当PV不再被使用了之后,对其的处理方式。目前支持三种策略:
Retain (保留) 保留数据,需要管理员手工清理数据
Recycle (回收) 清除PV中的数据,效果相当于执行rm -rf /thevolume/*
Delete (删除) 与PV相连的后端存储完成volume的删除操作,当然这常见于云服务商的存储服务
需要注意的是,底层不同的存储类型可能支持的回收策略不同
存储类别
PV可以通过storageClassName参数指定一个存储类别
具有特定类别的PV只能与请求了该类别的PVC进行绑定
未设定类别的PV则只能与不请求任何类别的PVC进行绑定
状态(status)
一个PV的生命周期中,可能会处于4种不同的阶段:
Available(可用):表示可用状态,还未被任何PVC绑定
Bound(已绑定):表示PV已经被PVC绑定
Released(已释放):表示PVC被删除,但是资源还未被集群重新声明
Failed(失败):表示该PV的自动回收失败
【例 】
使用NFS作为存储,来演示PV的使用,创建3个PV,对应NFS中的3个暴露的路径。
准备NFS环境
# 创建目录
[root@nfs ~]# mkdir -p /root/data/{pv1,pv2,pv3}
# 配置共享目录及权限
[root@nfs ~]# vim /etc/exports
/root/data/pv1 192.168.126.0/24(rw,no_root_squash)
/root/data/pv2 192.168.126.0/24(rw,no_root_squash)
/root/data/pv3 192.168.126.0/24(rw,no_root_squash)
# 重启服务
[root@nfs ~]# systemctl restart nfs
# 编辑PV资源清单文件
[root@k8s-master ~]# vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi # 存储能力,1G
accessModes:
- ReadWriteMany # 访问模式,读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy: Retain # 回收策略,保留
nfs: # 存储类型,nfs
path: /root/data/pv1 # 路径
server: 192.168.126.12 # nfs服务器IP
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.126.12
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.126.12
# 创建
[root@k8s-master ~]# kubectl create -f pv.yaml
persistentvolume/pv1 created
persistentvolume/pv2 created
persistentvolume/pv3 created
# 查看
[root@k8s-master ~]# kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
pv1 1Gi RWX Retain Available 10s Filesystem
pv2 2Gi RWX Retain Available 10s Filesystem
pv3 3Gi RWX Retain Available 10s Filesystem
PVC是对PV资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。PVC会受到namespace隔离与PV不同
PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了
PVC资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc # 资源名
namespace: test # 所属命名空间
spec: # 以下是对PV资源的需求信息
accessModes: # 访问模式
selector: # 采用标签对PV选择
storageClassName: # 存储类别
resources: #请求空间
requests:
storage: 5Gi
PVC的关键配置参数说明:
访问模式(accessModes)
用于描述用户应用对存储资源的访问权限
选择条件(selector)
通过Label Selector的设置,可使PVC对于系统中己存在的PV进行筛选关联
存储类别(storageClassName)
PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出
资源请求(Resources )
描述对存储资源的请求
【例 】
创建PVC(前提是在上面实验的基础上)
# 编辑PVC资源清单,申请PV
[root@k8s-master ~]# vim pvc.yaml
# 这里申请的是上面创建好的PV资源,所以PVC在申请时必须是和已有的PV的配置相对应(比如访问模式,回收策略要一致,申请空间必须小于等于已有的PV),否则无法匹配上PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
# 创建
[root@k8s-master ~]# kubectl create -f pvc.yaml
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created
persistentvolumeclaim/pvc3 created
# 查看
[root@k8s-master ~]# kubectl get pvc -n test
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc1 Bound pv1 1Gi RWX 8s
pvc2 Bound pv2 2Gi RWX 8s
pvc3 Bound pv3 3Gi RWX 8s
# 状态均为绑定,说明所有PVC匹配PV均成功(比如如果我们有一个PVC申请空间是5G,那么该PVC便无法匹配和绑定上PV,状态会一直处于Pending状态,无法绑定成功,因为上面创建的三个PV的大小分别是1G、2G、3G)
# 同时查看PV状态
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWX Retain Bound test/pvc1 27m
pv2 2Gi RWX Retain Bound test/pvc2 27m
pv3 3Gi RWX Retain Bound test/pvc3 27m。CLAIM
# 只要被PVC匹配上并绑定,那么PV的状态也会为绑定状态,CLAIM 字段说明了PV被绑定的到哪个命名空间的哪个PVC上
# PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了
创建Pod,通过PVC引用使用PV
# 编辑pod资源清单文件
[root@k8s-master ~]# vim pods.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: test
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c", "while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/ # 指定该PV所挂载到容器内部的路径
volumes:
- name: volume
persistentVolumeClaim: # 声明要使用的PVC
claimName: pvc1
readOnly: false # 将强制VolumeMounts中的ReadOnly设置。默认值为false,表示对该pv为可读可写权限,true表示只有只读权限。
# 最后上面输出到/root/out.txt文件中的内容会同时持久化到nfs对应的共享目录中。因为上面是将容器中的/root/目录通过PVC引用挂载到PV上,
# 而PV又是建立在nfs存储之上,也就是最后的数据会持久化到PV所声明的nfs服务器的对应共享目录下。
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: test
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c", "while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false
# 最后上面输出到/root/out.txt文件中的内容会同时持久化到nfs对应的共享目录中。因为上面是将容器中的/root/目录通过PVC引用挂载到PV上,
# 而PV又是建立在nfs存储之上,也就是最后的数据会持久化到PV所声明的nfs服务器的对应共享目录下。
---
apiVersion: v1
kind: Pod
metadata:
name: pod3
namespace: test
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c", "while true;do echo pod3 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc3
readOnly: false
# 最后上面输出到/root/out.txt文件中的内容会同时持久化到nfs对应的共享目录中。因为上面是将容器中的/root/目录通过PVC引用挂载到PV上,
# 而PV又是建立在nfs存储之上,也就是最后的数据会持久化到PV所声明的nfs服务器的对应共享目录下。
# 创建
[root@k8s-master ~]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 9s 10.244.2.216 k8s-node02 <none> <none>
pod2 1/1 Running 0 9s 10.244.2.215 k8s-node02 <none> <none>
pod3 1/1 Running 0 9s 10.244.1.180 k8s-node01 <none> <none>
# 此时查看nfs服务器对应的共享目录下的文件是否生成
[root@nfs ~]# cat /root/data/pv1/out.txt
pod1
pod1
[root@nfs ~]# cat /root/data/pv2/out.txt
pod2
pod2
[root@nfs ~]# cat /root/data/pv3/out.txt
pod3
pod3
# 可以看到生成对应的文件并且内容也是对的
# 可以看到容器内挂载目录下的文件和nfs共享目录下的文件是一致的,而且内容也是实时更新的
[root@k8s-master ~]# kubectl exec -it pod1 -n test -- cat /root/out.txt
pod1
pod1
pod1
[root@k8s-master ~]# kubectl exec -it pod2 -n test -- cat /root/out.txt
pod2
pod2
pod2
[root@k8s-master ~]# kubectl exec -it pod3 -n test -- cat /root/out.txt
pod3
pod3
pod3
# 删除pod
[root@k8s-master ~]# kubectl delete -f pods.yaml
pod "pod1" deleted
pod "pod2" deleted
pod "pod3" deleted
# 删除pvc
[root@k8s-master ~]# kubectl delete -f pvc.yaml
persistentvolumeclaim "pvc1" deleted
persistentvolumeclaim "pvc2" deleted
persistentvolumeclaim "pvc3" deleted
# 此时查看pv状态
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWX Retain Released test/pvc1 65m
pv2 2Gi RWX Retain Released test/pvc2 65m
pv3 3Gi RWX Retain Released test/pvc3 65m
# 状态变为 Released ,表示PVC被删除,但是PV资源还未被集群重新声明该如何处理
其实作为用户而言只需要完成PVC和Pod的创建即可使用存储,用户不需要具体关心PV和底层的存储是什么。因为对于PV和底层的存储只需要相关专业人员设计和创建部署好即可。
小结
创建PV资源,通过PVC申请使用PV资源,将PVC与PV进行绑定,最后Pod通过引用PVC来使用PV资源(也就是pod最后间接的使用到底层的存储资源)
即在pod中声明要使用的PVC,然后将容器的目录挂载到PVC所指定的PV上,最后挂载到PV上的容器内的目录下的所有文件会同时持久化到nfs对应的共享目录中。因为容器内的被挂载目录是通过PVC引用挂载到PV上,而PV又是建立在nfs存储之上,也就是最后的数据会持久化到PV所声明的nfs服务器的对应共享目录下。
PVC和PV是一 一对应的, PV和PVC之间的相互作用遵循以下生命周期:
资源供应:管理员手动创建底层存储和PV,状态处于Available
资源绑定:用户创建PVC, kubernetes负责根据PVC的声明去寻找PV,并绑定,状态为Bound
在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的
PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了
资源使用:用户可在pod中像volume一样使用pvc
Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。
资源释放:用户删除pvc来释放pv
当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为”已释放(Released)“,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。
资源回收:kubernetes根据pv设置的回收策略进行资源的回收
对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收(处于Available状态),才能供新的PVC绑定和使用