31、K8S-数据存储之PV、PVC、StorageClass
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
4.3、pv claim占用无法挂载的处理
kubectl patch pv prometheus-data-pv -p '{"spec":{"claimRef": null}}'
5、StorageClass-实践
由于操作的东西比较多,单独开一个小节。请参考:K8S-StorageClass资源-实践:https://www.cnblogs.com/ygbh/p/17319970.html