关于PV、PVC、StorageClass ,这篇文章讲的不错:https://www.cnblogs.com/rexcheny/p/10925464.html
容器的设计理念就是一次性,也就是容器销毁后容器里的所有数据都会销毁,所以需要将容器里面需要保留的数据挂载到持久性存储中,这里就涉及到三个概念:PV、PVC、StorageClass 。
HostPath
当使用docker创建container的时候,一般都是加参数 -v 挂载宿主机的目录到container里面,k8s 也可以实现该功能,先讲解一下 挂载到宿主机目录的方法。
创建一个deployment资源配置文件,挂载到宿主机目录:
[root@ylserver10686071 ~]# cat volumes001.yml apiVersion: apps/v1 kind: Deployment metadata: name: volumes001 namespace: prod spec: replicas: 1 selector: matchLabels: k8s-app: volumes001 template: metadata: labels: k8s-app: volumes001 spec: containers: - name: nginx image: nginx:1.21 volumeMounts: - mountPath: /usr/share/nginx/html/ name: html volumes: - name: html hostPath: path: /data/nginx/html/ type: DirectoryOrCreate
- volumeMounts 挂载到container 指定目录的相关配置,根据name来匹配volumes对象的下的name,据此来找到挂载对象
- volumes 声明挂载对象
- hostPath 存储类型
- type: DirectoryOrCreate 如果宿主机路径不存在则创造该路径,当值为Directory 是,宿主机必须有该目录,否则会导致pod创建失败
创建deployment资源,查看pod在哪台Node上运行:
[root@ylserver10686071 ~]# kubectl apply -f volumes001.yml deployment.apps/volumes001 created [root@ylserver10686071 ~]# kubectl get pods -n prod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volumes001-66767f866f-rc5qk 1/1 Running 0 21s 10.233.72.59 ylserver10686073 <none> <none> [root@ylserver10686071 ~]#
在ylserver10686071 Node上查看目录是否创建,然后到Pod运行的节点上查看目录是否创建:
[root@ylserver10686071 ~]# ll /data/nginx/html/ ls: cannot access /data/nginx/html/: No such file or directory [root@ylserver10686071 ~]# [root@ylserver10686073 ~]# ll /data/nginx/html/ total 0 [root@ylserver10686073 ~]#
给挂载目录创建文件,验证是否挂载到Pod里面:
[root@ylserver10686073 ~]# echo "Hello K8S" > /data/nginx/html/index.html [root@ylserver10686073 ~]# curl http://10.233.72.59/index.html Hello K8S [root@ylserver10686073 ~]#
PV
上面的实验中宿主机挂载的目录只有在 Pod 运行的 Node 上才会创建,换言之,Pod要挂载的目录必须跟Node做绑定,这会增加运维的难度,也失去k8s的故障转移特性。
针对这个问题,可以使用存储券解决,这里就要引入一个概念:PV。
PV全称叫做Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的,这个通常都是有运维或者数据存储工程师来定义。本节使用NFS来作为存储端,NFS搭建这里不做讲解。
先创建一个PV资源配置文件:
[root@ylserver10686071 ~]# cat pv001.yml apiVersion: v1 kind: PersistentVolume ###PV资源不属于任何命名空间,属于集群级别的 ###kubectl api-resources --namespaced=true 命令可以查看哪些资源属于命名空间 metadata: name: pv001 labels: ###Label可以不定义 name: pv001 storetype: nfs spec: ###定义PV资源规格 storageClassName: normal accessModes: ###设置访问模型 - ReadWriteMany - ReadWriteOnce - ReadOnlyMany capacity: ###设置存储空间大小 storage: 500Mi persistentVolumeReclaimPolicy: Retain ###回收策略 nfs: path: /data/nfs/k8s/ server: 10.68.60.193 [root@ylserver10686071 ~]#
accessModes 有3种属性值:
- ReadWriteMany 多路读写,卷能被集群多个节点挂载并读写
- ReadWriteOnce 单路读写,卷只能被单一集群节点挂载读写
- ReadOnlyMany 多路只读,卷能被多个集群节点挂载且只能读
persistentVolumeReclaimPolicy 回收策略也有3种属性值:
- Retain
当删除与之绑定的PVC时候,这个PV被标记为released(PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV上,但是该PV不可用,需要手动来处理这些数据并删除该PV
这种方式是最常用的,可以避免误删pvc或者pv而造成数据的丢失
- Delete 删除存储资源,AWS EBS, GCE PD, Azure Disk, and Cinder volumes支持这种方式
- Recycle 这个在1.14版本中以及被废弃,取而代之的是推荐使用动态存储供给策略,它的功能是当删除与该PV关联的PVC时,自动删除该PV中的所有数据
创建完PV后,PV会有几种状态:
- Available(可用) 块空闲资源还没有被任何声明绑定
- Bound(已绑定) 卷已经被声明绑定
- Released(已释放) 声明被删除,但是资源还未被集群重新声明
- Failed(失败) 该卷的自动回收失败
创建PV资源,并查看PV信息:
[root@ylserver10686071 ~]# kubectl apply -f pv001.yml persistentvolume/pv001 created [root@ylserver10686071 ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 500Mi RWO,ROX,RWX Retain Available normal 6s
PVC
PV只是定义了一个存储卷实体,还需要一层抽象的接口使其与POD关联,这层抽象的接口就是PVC,全称 Persistent Volume Claim,也就是持久化存储声明。开发人员使用这个来描述该容器需要一个什么存储。
创建一个PVC资源配置文件:
[root@ylserver10686071 ~]# cat pvc001.yml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc001 namespace: prod ###PVC资源属于命名空间级别 labels: ###Label可以不定义 name: pvc001 storetype: nfs capacity: 500Mi spec: storageClassName: normal accessModes: ###PVC也需要定义访问模式,不过它的模式一定是和现有PV相同或者是它的子集,否则匹配不到PV - ReadWriteMany resources: ###定义资源要求PV满足这个PVC的要求才会被匹配到 requests: storage: 500Mi # 定义要求有多大空间
创建PVC资源,查看PVC资源和PV资源绑定情况,可以看到PV和PVC已经实现绑定:
[root@ylserver10686071 ~]# kubectl apply -f pvc001.yml persistentvolumeclaim/pvc001 created [root@ylserver10686071 ~]# kubectl get pvc -n prod NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc001 Bound pv001 500Mi RWO,ROX,RWX normal 15s [root@ylserver10686071 ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 500Mi RWO,ROX,RWX Retain Bound prod/pvc001 normal 62m [root@ylserver10686071 ~]#
PVC是如何跟PVC绑定的呢,有以下几个原则:
- PV和PVC中的spec关键字段要匹配,比如存储(storage)大小
- PVC的访问模式一定是和现有PV相同或者是它的子集,否则匹配不到PV
- PV和PVC中的 StorageClass Name字段必须一致,StorageClass后面会讲到
- Label 标签在这里只做描述作用,跟 PV 和 PVC 的绑定没有任何关系
看到这里,回想总结一下就会发现 k8s 里面会通过定义一层抽象概念来管理实体或者连接实体,类似于 Pod 和 Container , PVC 和 PV ;对象和对象的匹配设计原理也是一直,例如 deployment匹配replicaset通过matchLabels ,PV 和 PVC通过 StorageClass Name以及 resources等,即对象与对象之间通过匹配关系进行绑定。
更新上面的deployment资源配置文件,使其使用创建好的PVC资源:
[root@ylserver10686071 ~]# cat volumes001.yml apiVersion: apps/v1 kind: Deployment metadata: name: volumes001 namespace: prod ###要和指定的PVC同一个命名空间 spec: replicas: 1 selector: matchLabels: k8s-app: volumes001 template: metadata: labels: k8s-app: volumes001 spec: containers: - name: nginx image: nginx:1.21 volumeMounts: ###container的挂载声明没有改变 - mountPath: /usr/share/nginx/html/ name: html volumes: ###依然使用volumes声明挂载卷 - name: html persistentVolumeClaim: ###指定PVC claimName: pvc001
更新资源deployment资源配置文件,查看pod关于volumes相关信息:
[root@ylserver10686071 ~]# kubectl apply -f volumes001.yml deployment.apps/volumes001 configured [root@ylserver10686071 ~]# kubectl describe pod volumes001 -n prod|grep -5 Volumes Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: html: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: pvc001 ReadOnly: false default-token-lx75g: [root@ylserver10686071 ~]#
写一个文件到NFS挂载目录中,测试一下效果:
[root@ylserver106860193 ~]# echo "K8S PV" > /data/nfs/k8s/index.html [root@ylserver10686071 ~]# kubectl get pods -n prod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volumes001-55f5bb9585-nx9xd 1/1 Running 0 11m 10.233.72.60 ylserver10686073 <none> <none> [root@ylserver10686071 ~]# curl http://10.233.72.60 K8S PV [root@ylserver10686071 ~]#