九、Kubernetes之 PV和PVC
PV(PersistentVolume)与PVC(PersistentVolumeClaim)就是在用户与存储服务之间添加的一个中间层,管理员事先根据PV支持的存储卷插件及适配的存储方案(目标存储系统)细节定义好可以支撑存储卷的底层存储空间,而后由用户通过PVC声明要使用的存储特性来绑定符合条件的最佳PV定义存储卷,从而实现存储系统的使用与管理职能的解耦,大大简化了用户使用存储的方式。 PV和PVC的生命周期由Controller Manager中专用的PV控制器(PV Controller)独立管理,这种机制的存储卷不再依附并受限于Pod对象的生命周期,从而实现了用户和集群管理员的职责相分离,也充分体现出Kubernetes把简单留给用户,把复杂留给自己的管理理念。
1、PV和PVC基础
PV是由集群管理员于全局级别配置的预挂载存储空间,它通过支持的存储卷插件及给定的配置参数关联至某个存储系统上可用数据存储的一段空间,这段存储空间可能是Ceph存储系统上的一个存储映像、一个文件系统(CephFS)或其子目录,也可能是NFS存储系统上的一个导出目录等。PV将存储系统之上的存储空间抽象为Kubernetes系统全局级别的API资源,由集群管理员负责管理和维护。 将PV提供的存储空间用于Pod对象的存储卷时,用户需要事先使用PVC在名称空间级别声明所需要的存储空间大小及访问模式并提交给Kubernetes API Server,接下来由PV控制器负责查找与之匹配的PV资源并完成绑定。随后,用户在Pod资源中使用persistentVolumeClaim类型的存储卷插件指明要使用的PVC对象的名称即可使用其绑定到的PV所指向的存储空间,如图所示。
这种通过管理员手动创建PV来满足PVC需求的静态预配(static provisioning)存在着不少的问题。
第一,集群管理员难以预测出用户的真实需求,很容易导致某些类型的PVC无法匹配到PV而被挂起,直到管理员参与到问题的解决过程中。
第二,那些能够匹配到PV的PVC也很有可能存在资源利用率不佳的状况,例如一个声明使用5G存储空间的PVC绑定到一个20GB的PV之上。
PV和PVC是一对一的关系:一个PVC仅能绑定一个PV,而一个PV在某一时刻也仅可被一个PVC所绑定。为了能够让用户更精细地表达存储需求,PV资源对象的定义支持存储容量、存储类、卷模型和访问模式等属性维度的约束。相应地,PVC资源能够从访问模式、数据源、存储资源容量需求和限制、标签选择器、存储类名称、卷模型和卷名称等多个不同的维度向PV资源发起匹配请求并完成筛选。
2、PV的生命周期
Kubernetes系统与存储相关的组件主要有存储卷插件、存储卷管理器、PV/PVC控制器和AD控制器(Attach/Detach Controller)这4种
▪存储卷插件:Kubernetes存储卷功能的基础设施,是存储任务相关操作的执行方;它是存储相关的扩展接口,用于对接各类存储设备。 ▪存储卷管理器:kubelet内置管理器组件之一,用于在当前节点上执行存储设备的挂载(mount)、卸载(unmount)和格式化(format)等操作;另外,存储卷管理器也可执行节点级别设备的附加(attach)及拆除(detach)操作。 ▪PV控制器:负责PV及PVC的绑定和生命周期管理,并根据需求进行存储卷的预配和删除操作; ▪AD控制器:专用于存储设备的附加和拆除操作的组件,能够将存储设备关联(attach)至目标节点或从目标节点之上剥离(detach)。
除了创建、删除PV对象,以及完成PV和PVC的状态迁移等生命周期管理之外,PV控制器还要负责绑定PVC与PV对象,而且PVC只能在绑定到PV之后方可由Pod作为存储卷使用。创建后未能正确关联到存储设备的PV将处于Pending状态,直到成功关联后转为Available状态。而后一旦该PV被某个PVC请求并成功绑定,其状态也就顺应转为Bound,直到相应的PVC删除后而自动解除绑定,PV才会再次发生状态转换,此时的状态为(Released),随后PV的去向将由其“回收策略”(reclaim policy)所决定,具体如下。
1)Retain(保留):删除PVC后将保留其绑定的PV及存储的数据,但会把该PV置为Released状态,它不可再被其他PVC所绑定,且需要由管理员手动进行后续的回收操作:首先删除PV,接着手动清理其关联的外部存储组件上的数据,最后手动删除该存储组件或者基于该组件重新创建PV。 2)Delete(删除):对于支持该回收策略的卷插件,删除一个PVC将同时删除其绑定的PV资源以及该PV关联的外部存储组件;动态的PV回收策略继承自StorageClass资源,默认为Delete。多数情况下,管理员都需要根据用户的期望修改此默认策略,以免导致数据非计划内的删除。 3)Recycle(回收):对于支持该回收策略的卷插件,删除PVC时,其绑定的PV所关联的外部存储组件上的数据会被清空,随后,该PV将转为Available状态,可再次接受其他PVC的绑定请求。不过,该策略已被废弃。
相应地,创建后的PVC也将处于Pending状态,仅在遇到条件匹配、状态为Available的PV,且PVC请求绑定成功才会转为Bound状态。PV和PVC的状态迁移如图所示。总结起来,PV和PVC的生命周期存在以几个关键阶段。
1)存储预配(provision):存储预配是指为PVC准备PV的途径,Kubernetes支持静态和动态两种PV预配方式,前者是指由管理员以手动方式创建PV的操作,而后者则是由PVC基于StorageClass定义的模板,按需请求创建PV的机制。 2)存储绑定:用户基于一系列存储需求和访问模式定义好PVC后,PV控制器即会为其查找匹配的PV,完成关联后它们二者同时转为已绑定状态,而且动态预配的PV与PVC之间存在强关联关系。无法找到可满足条件的PV的PVC将一直处于Pending状态,直到有符合条件的PV出现并完成绑定为止。 3)存储使用:Pod资源基于persistenVolumeClaim存储卷插件的定义,可将选定的PVC关联为存储卷并用于内部容器应用的数据存取。 4)存储回收:存储卷的使用目标完成之后,删除PVC对象可使得此前绑定的PV资源进入Released状态,并由PV控制器根据PV回收策略对PV作出相应的处置。目前,可用的回收策略有Retaine、Delete和Recycle这3种。
3、静态PV资源
PersistentVolume是隶属于Kubernetes核心API群组中的标准资源类型,它的目标在于通过存储卷插件机制,将支持的外部存储系统上的存储组件定义为可被PVC声明所绑定的资源对象。但PV资源隶属于Kubernetes集群级别,因而它只能由集群管理员进行创建。这种由管理员手动定义和创建的PV被人们习惯地称为静态PV资源。 PV支持的存储卷插件类型是Pod对象支持的存储卷插件类型的一个子集,它仅涵盖Pod支持的网络存储卷类别中的所有存储插件以及local卷插件。除了存储卷插件之外,PersistentVolume资源规范Spec字段主要支持嵌套以下几个通用字段,它们用于定义PV的容量、访问模式和回收策略等属性。
▪capacity <map[string]string>:指定PV的容量;目前,Capacity仅支持存储容量设定,将来应该还可以指定IOPS和吞吐量(throughput)。 ▪accessModes <[]string>:指定当前PV支持的访问模式;存储系统支持的存取能力大体可分为ReadWriteOnce(单路读写)、ReadOnlyMany(多路只读)和ReadWrite-Many(多路读写)3种类型,某个特定的存储系统可能会支持其中的部分或全部的能力。 ▪persistentVolumeReclaimPolicy <string>:PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete。目前,仅NFS和hostPath支持Recycle策略,也仅有部分存储系统支持Delete策略。 ▪volumeMode <string>:该PV的卷模型,用于指定此存储卷被格式化为文件系统使用还是直接使用裸格式的块设备;默认值为Filesystem,仅块设备接口的存储系统支持该功能。 ▪storageClassName <string>:当前PV所属的StorageClass资源的名称,指定的存储类需要事先存在;默认为空值,即不属于任何存储类。 ▪mountOptions <string>:挂载选项组成的列表,例如ro、soft和hard等。 ▪nodeAffinity <Object>:节点亲和性,用于限制能够访问该PV的节点,进而会影响与该PV关联的PVC的Pod的调度结果。
需要注意的是,PV的访问模式用于反映它关联的存储系统所支持的某个或全部存取能力,例如NFS存储系统支持以上3种存取能力,于是NFS PV可以仅支持ReadWriteOnce访问模式。需要注意的是,PV在某个特定时刻仅可基于一种模式进行存取,哪怕它同时支持多种模式。
下面的配置示例来自于pv-nfs-demo.yaml资源清单,它定义了一个使用NFS存储系统的PV资源,它将空间大小限制为5GB,并支持多路的读写操作。
apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-demo spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 nfs: path: "/data/k8s_pv" server: 172.168.32.201
在NFS服务器172.168.32.201上导出/data/k8s_pv目录后.
NFS导出/data/k8s_pv root@harbor:~# mkdir /data/k8s_pv root@harbor:~# exportfs -avr root@harbor:~# cat /etc/exports /data/redis_data *(rw,no_root_squash) /data/k8s_pv *(rw,no_root_squash) root@harbor:~# expo root@harbor:~# showmount -e 172.168.33.201 Export list for 172.168.33.201: /data/k8s_pv * /data/redis_data *
可使用如下命令创建该PV资源
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-nfs-demo.yaml persistentvolume/pv-nfs-demo created [root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Available 24s
若能够正确关联到指定的后端存储,该PV对象的状态将显示为Available,否则其状态为Pending,直至能够正确完成存储资源关联或者被删除。我们同样可使用describe命令来获取PV资源的详细描述信息。
[root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Available 24s root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pv pv-nfs-demo Name: pv-nfs-demo Labels: <none> Annotations: <none> Finalizers: [kubernetes.io/pv-protection] StorageClass: Status: Available Claim: Reclaim Policy: Retain Access Modes: RWX VolumeMode: Filesystem Capacity: 5Gi Node Affinity: <none> Message: Source: Type: NFS (an NFS mount that lasts the lifetime of a pod) Server: 172.168.32.201 Path: /data/k8s_pv ReadOnly: false Events: <none>
2)
apiVersion: v1 kind: PersistentVolume metadata: name: pv-rbd-demo labels: usedof: redisdata spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce rbd: monitors: - 172.168.32.201:6789 - 172.168.32.202:6789 - 172.168.32.203:6789 pool: k8spool image: k8spv user: admin keyring: /etc/ceph/ceph.client.admin.keyring fsType: xfs readOnly: false persistentVolumeReclaimPolicy: Retain
将RBD卷插件内嵌字段相关属性值设定为Ceph存储系统的实际的环境,包括监视器地址、存储池、存储映像、用户名和认证信息(keyring或secretRef)等。测试时,请事先部署好Ceph集群,参考RBD存储卷中设定admin用户账号和Kubernetes集群工作节点的方式,准备好基础环境,并在Ceph集群的管理节点运行如下命令创建用到的存储映像:
#因为挂载rbd的宿主机因内核版本原因不支持rbd的所有特性,只开启部分特性 root@ceph01:~# rbd create k8spv --size 20480 --pool k8spool --image-format 2 --image-feature layering #开启全部特性。注意挂载宿主机的内核版本是否支持rbd的特性功能 #root@ceph01:~# rbd create k8spv --size 20480 --pool k8spool root@ceph01:~# rbd feature disable -p k8spool k8spv object-map fast-diff deep-flatten #在Kubernetes集群的所有节点安装ceph客户端(ceph-common) ①把ceph01上的ceph.repo拷贝到Kubernetes的所有node节点 #master节点 root@ceph01:~# scp /etc/apt/sources.list 172.168.33.207:/etc/apt/sources.list root@ceph01:~# scp /etc/apt/sources.list 172.168.33.208:/etc/apt/sources.list root@ceph01:~# scp /etc/apt/sources.list 172.168.33.209:/etc/apt/sources.list #node节点 root@ceph01:~# scp /etc/apt/sources.list 172.168.33.210:/etc/apt/sources.list root@ceph01:~# scp /etc/apt/sources.list 172.168.33.211:/etc/apt/sources.list root@ceph01:~# scp /etc/apt/sources.list 172.168.33.212:/etc/apt/sources.list ②kubernetes各个node节点安装ceph-common客户端 root@k8s-master01:~# apt update && apt install -y ceph-common root@k8s-master02:~# apt update && apt install -y ceph-common root@k8s-master03:~# apt update && apt install -y ceph-common root@k8s-node01:~# apt update && apt install -y ceph-common root@k8s-node02:~# apt update && apt install -y ceph-common root@k8s-node03:~# apt update && apt install -y ceph-common 5)把ceph中admin的账号密码文件和ceph.conf拷贝到kubernetes的所有node节点上 kubernetes所有的node节点 root@k8s-master01:~# mkdir /etc/ceph root@k8s-master02:~# mkdir /etc/ceph root@k8s-master03:~# mkdir /etc/ceph root@k8s-node01:~# mkdir /etc/ceph root@k8s-node02:~# mkdir /etc/ceph root@k8s-node03:~# mkdir /etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.207:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.208:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.209:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.210:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.211:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.admin.keyring,ceph.conf} 172.168.33.212:/etc/ceph
6)把ceph的hosts解析拷贝到k8s集群的所有节点的hosts中
待所有准备工作就绪后,即可运行如下命令创建示例清单中定义的PV资源pv-rbd-demo:
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-rbd-demo.yaml persistentvolume/pv-rbd-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Available 19m pv-rbd-demo 2Gi RWO Retain Available 3s
同样可以使用describe命令了解pv-rbd-demo的详细描述,若处于Pending状态则需要详细检查存储卷插件的定义是否能吻合存储系统的真实环境。
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pv pv-rbd-demo Name: pv-rbd-demo Labels: usedof=redisdata Annotations: <none> Finalizers: [kubernetes.io/pv-protection] StorageClass: Status: Available Claim: Reclaim Policy: Retain Access Modes: RWO VolumeMode: Filesystem Capacity: 2Gi Node Affinity: <none> Message: Source: Type: RBD (a Rados Block Device mount on the host that shares a pod's lifetime) CephMonitors: [172.168.32.201:6789 172.168.32.202:6789 172.168.32.203:6789] RBDImage: k8spv FSType: xfs RBDPool: k8spool RadosUser: admin Keyring: /etc/ceph/ceph.client.admin.keyring SecretRef: nil ReadOnly: false Events: <none>
使用普通用户client.k8s来部署pv
#如果使用普通用户挂载rbd #1)给pool=k8spool创建一个client的k8s用户,对mon有r权限,对k8spool地址池有rwx权限 root@ceph01:~# ceph auth add client.k8s mon 'allow r' osd 'allow rwx pool=k8spool' added key for client.k8s #2)创建一个名为ceph.client.k8s.keyring的密钥环文件 root@ceph01:~# ceph auth get client.k8s [client.k8s] key = AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== caps mon = "allow r" caps osd = "allow rwx pool=k8spool" exported keyring for client.k8s root@ceph01:~# ceph-authtool --create-keyring ceph.client.k8s.keyring creating ceph.client.k8s.keyring #3)导出client.k8s信息之指定的keyring文件 root@ceph01:~# ceph auth get client.k8s -o ceph.client.k8s.keyring exported keyring for client.k8s root@ceph01:~# cat ceph.client.k8s.keyring [client.k8s] key = AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== caps mon = "allow r" caps osd = "allow rwx pool=k8spool" #4)把ceph.client.k8s.keyring和ceph.conf 拷贝到所有的k8s节点 root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.207:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.208:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.209:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.210:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.211:/etc/ceph root@ceph01:~# scp -r /etc/ceph/{ceph.client.k8s.keyring,ceph.conf} 172.168.33.212:/etc/ceph #5)修改pv-rbd-demo.yaml的用户名和keyring apiVersion: v1 kind: PersistentVolume metadata: name: pv-rbd-demo labels: usedof: redisdata spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce rbd: monitors: - 172.168.32.201:6789 - 172.168.32.202:6789 - 172.168.32.203:6789 pool: k8spool image: k8spv user: k8s keyring: /etc/ceph/ceph.client.k8s.keyring fsType: xfs readOnly: false persistentVolumeReclaimPolicy: Retain #6)运行清单 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-rbd-demo.yaml persistentvolume/pv-rbd-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-rbd-demo 2Gi RWO Retain Available 12s #7)验证 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pv pv-rbd-demo Name: pv-rbd-demo Labels: usedof=redisdata Annotations: <none> Finalizers: [kubernetes.io/pv-protection] StorageClass: Status: Available Claim: Reclaim Policy: Retain Access Modes: RWO VolumeMode: Filesystem Capacity: 2Gi Node Affinity: <none> Message: Source: Type: RBD (a Rados Block Device mount on the host that shares a pod's lifetime) CephMonitors: [172.168.32.201:6789 172.168.32.202:6789 172.168.32.203:6789] RBDImage: k8spv FSType: xfs RBDPool: k8spool RadosUser: k8s Keyring: /etc/ceph/ceph.client.k8s.keyring SecretRef: nil ReadOnly: false Events: <none>
这里以pv-rbd-demo.yaml为例
第一步:更具client.k8s的key来创建pv-secret.yaml
#在ceph上用client.k8s的key值来base64编码 #ceph集群上操作 root@ceph01:~# cat /etc/ceph/ceph.client.k8s.keyring [client.k8s] key = AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== caps mon = "allow r" caps osd = "allow rwx pool=k8spool" root@ceph01:~# echo AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== | base64 QVFBSFdHUmgvZCtKTnhBQUhyenBsd2k4V1dqd1hjL05HZEplQXc9PQo= #根据上面的base64的编码值来创建pv-k8s-secret.yaml清单 #在k8s集群上操作 cat > pv-k8s-secret.yaml << EOF apiVersion: v1 kind: Secret metadata: name: pv-k8s-secret data: #Please note this value is client.k8s base64 encoded. # ceph auth get client.k8s | base64 key: QVFBSFdHUmgvZCtKTnhBQUhyenBsd2k4V1dqd1hjL05HZEplQXc9PQo= EOF #创建pv-rbd-secret-demo.yaml清单 cat > pv-rbd-secret-demo.yaml << EOF apiVersion: v1 kind: PersistentVolume metadata: name: pv-rbd-secret-demo labels: usedof: redisdata spec: capacity: storage: 2Gi accessModes: - ReadWriteOnce rbd: monitors: - 172.168.32.201:6789 - 172.168.32.202:6789 - 172.168.32.203:6789 pool: k8spool image: k8spv user: k8s secretRef: name: pv-k8s-secret fsType: xfs readOnly: false persistentVolumeReclaimPolicy: Retain EOF #运行清单 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-k8s-secret.yaml secret/pv-k8s-secret created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-rbd-secret-demo.yaml persistentvolume/pv-rbd-secret-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-rbd-demo 2Gi RWO Retain Available 15m pv-rbd-secret-demo 2Gi RWO Retain Available 15s #验证 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-rbd-demo 2Gi RWO Retain Available 15m pv-rbd-secret-demo 2Gi RWO Retain Available 15s root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pv pv-rbd-secret-demo Name: pv-rbd-secret-demo Labels: usedof=redisdata Annotations: <none> Finalizers: [kubernetes.io/pv-protection] StorageClass: Status: Available Claim: Reclaim Policy: Retain Access Modes: RWO VolumeMode: Filesystem Capacity: 2Gi Node Affinity: <none> Message: Source: Type: RBD (a Rados Block Device mount on the host that shares a pod's lifetime) CephMonitors: [172.168.32.201:6789 172.168.32.202:6789 172.168.32.203:6789] RBDImage: k8spv FSType: xfs RBDPool: k8spool RadosUser: k8s Keyring: /etc/ceph/keyring SecretRef: &SecretReference{Name:pv-k8s-secret,Namespace:,} ReadOnly: false Events: <none>
4、PVC资源
PersistentVolumeClaim也是Kubernetes系统上标准的API资源类型之一,它位于核心API群组,属于名称空间级别。用户提交新建的PVC资源最初处于Pending状态,由PV控制器找寻最佳匹配的PV并完成二者绑定后,两者都将转入Bound状态,随后Pod对象便可基于persistentVolumeClaim存储卷插件配置使用该PVC对应的持久存储卷。
▪accessModes <[]string>:PVC的访问模式;它同样支持RWO、RWX和ROX这3种模式。 ▪dataSrouces <Object>:用于从指定的数据源恢复该PVC卷,它目前支持的数据源包括一个现存的卷快照对象(snapshot.storage.k8s.io/VolumeSnapshot)、一个既有的PVC对象(PersistentVolumeClaim)或一个既有的用于数据转存的自定义资源对象(resource/object)。 ▪resources <Object>:声明使用的存储空间的最小值和最大值;目前,PVC的资源限定仅支持空间大小一个维度。 ▪selector <Object>:筛选PV时额外使用的标签选择器(matchLabels)或匹配条件表达式(matchExpressions ▪storageClassName <string>:该PVC资源隶属的存储类资源名称;指定了存储类资源的PVC仅能在同一个存储类下筛选PV资源,否则就只能从所有不具有存储类的PV中进行筛选。 ▪volumeMode <string>:卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认值为Filesystem。 ▪volumeName <string>:直接指定要绑定的PV资源的名称。
下面通过匹配前一节中创建的PV资源的两个具体示例来说明PVC资源的配置方法,两个PV资源目前的状态如下所示,它仅截取了命令结果中的一部分。
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Available 11s pv-rbd-demo 5Gi RWX Retain Available 3s
下面的配置清单(pvc-nfs-001.yaml)定义了一个名为pvc-nfs-001的PVC资源示例,它仅定义了期望的存储空间范围、访问模式和卷模式以筛选集群上的PV资源。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs-001 namespace: default spec: accessModes: - ReadWriteMany volumeMode: Filesystem resources: requests: storage: 3Gi limits: storage: 10Gi
显然,此前创建的两个PV资源中,pv-nfs-demo能够完全满足该PVC的筛选条件,因而创建示例清单中的资源后,它能够迅速绑定至PV之上,如下面的创建和资源查看命令结果所示。
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pvc-nfs-001.yaml persistentvolumeclaim/pvc-nfs-001 created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pvc -n default NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs-001 Bound pv-nfs-demo 5Gi RWX 21s root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Bound default/pvc-nfs-001 3m56s pv-rbd-demo 2Gi RWX Retain Available 3m48s
被PVC资源pvc-demo-0001绑定的PV资源pv-nfs-demo的状态也将从Available转为Bound
集群上的PV资源数量很多时,用户可通过指定多维度的过滤条件来缩小PV资源的筛选范围,以获取到最佳匹配
2)
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-rbd-002 namespace: default spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 2Gi limits: storage: 5Gi selector: matchLabels: usedof: redisdata
配置清单中的资源PVC/pvc-rbd-002特地为绑定此前创建的资源PV/pv-rbd-demo而创建,其筛选条件可由该PV完全满足,因而创建配置清单中的PVC/pvc-rbd-002资源后会即刻绑定于PV/pv-rbd-demo之上,如下面命令的结果所示。
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pvc-rbd-002.yaml persistentvolumeclaim/pvc-rbd-002 created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs-001 Bound pv-nfs-demo 5Gi RWX 22m pvc-rbd-002 Bound pv-rbd-demo 5Gi RWX 4s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Bound default/pvc-nfs-001 26m pv-rbd-demo 5Gi RWX Retain Bound default/pvc-rbd-002 8m24s
3)删除pvc并手动清理数据并恢复pv
以pvc-rbd-002为例
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl delete pvc pvc-rbd-002 persistentvolumeclaim "pvc-rbd-002" deleted root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-nfs-001 Bound pv-nfs-demo 5Gi RWX 24m root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Bound default/pvc-nfs-001 28m pv-rbd-demo 5Gi RWX Retain Released default/pvc-rbd-002 9m28s #pv-rbd-demo为Released状态无法被其他pvc所绑定 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl delete pv pv-rbd-demo persistentvolume "pv-rbd-demo" deleted #删除pv-rbd-demo并重建该pv root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pv-rbd-demo.yaml persistentvolume/pv-rbd-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Bound default/pvc-nfs-001 30m pv-rbd-demo 5Gi RWX Retain Available 5s #pv-rbd-demo的状态为Available,可以被其他pvc所绑定
PVC资源隶属名称空间级别,它仅可被同一名称空间中的Pod对象通过persistentVolumeClaim插件所引用并作为存储卷使用,该存储卷插件可嵌套使用如下两个字段。
▪claimName:要调用的PVC存储卷的名称,PVC卷要与Pod在同一名称空间中。
▪readOnly:是否强制将存储卷挂载为只读模式,默认为false。
下面的配置清单(volumes-pvc-demo.yaml)定义了一个Pod资源,该配置清单中直接使用RBD存储的Pod资源改为了调用指定的PVC存储卷。
apiVersion: v1 kind: Pod metadata: name: volumes-pvc-demo namespace: default spec: containers: - name: redis image: redis:alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-rbd-vol volumes: - name: redis-rbd-vol persistentVolumeClaim: claimName: pvc-rbd-0002 #PVC的名称,必须与pod在同一个名称空间下。
运行清单
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f volumes-pvc-demo.yaml pod/volumes-pvc-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pod NAME READY STATUS RESTARTS AGE volumes-pvc-demo 1/1 Running 0 28s root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pod volumes-pvc-demo ...... Volumes: redis-rbd-vol: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: pvc-rbd-002 ReadOnly: false ......
存储类也是Kubernetes系统上的API资源类型之一,它位于storage.k8s.io群组中。存储类通常由集群管理员为管理PV资源之便而按需创建的存储资源类别(逻辑组),例如可将存储系统按照其性能高低或者综合服务质量级别分类、依照备份策略分类,甚至直接按管理员自定义的标准分类等。存储类也是PVC筛选PV时的过滤条件之一,这意味着PVC仅能在其隶属的存储类之下找寻匹配的PV资源。不过,Kubernetes系统自身无法理解“类别”到底意味着什么,它仅仅把存储类中的信息当作PV资源的特性描述使用。
1) StorageClass介绍
StorageClass资源的期望状态直接与apiVersion、kind和metadata定义在同一级别而无须嵌套在spec字段中,它支持使用的字段包括如下几个。
▪allowVolumeExpansion <boolean>:是否支持存储卷空间扩展功能。 ▪allowedTopologies <[]Object>:定义可以动态配置存储卷的节点拓扑,仅启用了卷调度功能的服务器才会用到该字段;每个卷插件都有自己支持的拓扑规范,空的拓扑选择器表示无拓扑限制。 ▪provisioner <string>:必选字段,用于指定存储服务方(provisioner,或称为预配器),存储类要基于该字段值来判定要使用的存储插件,以便适配到目标存储系统;Kubernetes内置支持许多的provisioner,它们的名字都以kubernetes.io/为前缀,例如kubernetes.io/glusterfs等。 ▪parameters <map[string]string>:定义连接至指定的provisioner类别下的某特定存储时需要使用的各相关参数;不同provisioner的可用参数各不相同。 ▪reclaimPolicy <string>:由当前存储类动态创建的PV资源的默认回收策略,可用值为Delete(默认)和Retain两个;但那些静态PV的回收策略则取决于它们自身的定义。 ▪volumeBindingMode <string>:定义如何为PVC完成预配和绑定,默认值为Volume-BindingImmediate;该字段仅在启用了存储卷调度功能时才能生效。 ▪mountOptions <[]string>:由当前类动态创建的PV资源的默认挂载选项列表。
下面是一个定义在storageclass-rbd-demo.yaml配置文件中的StorageClass资源清单,它定义了一个以Ceph存储系统的RBD接口为后端存储的StorageClass资源storage-ceph-rbd,因此,其存储预配标识为kubernetes.io/rbd。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: storage-ceph-rbd annotations: storageclass.kubernetes.io/is-default-class: "true" #设置为默认存储类 provisioner: kubernetes.io/rbd parameters: monitors: 172.168.32.201:6789,172.168.32.202:6789,172.168.32.203:6789 adminId: admin adminSecretName: admin-storage-secret adminSecretNamespace: default pool: k8spool userId: k8s userSecretName: k8s-storage-secret fsType: ext4 imageFormat: "2" imageFeatures: "layering" reclaimPolicy: Retain
不同的provisioner的parameters字段中可嵌套使用的字段各有不同,上面示例中Ceph RBD存储服务可使用的各字段及意义如下。
▪monitors <string>:Ceph存储系统的监视器访问接口,多个套接字间以逗号分隔。 ▪adminId <string>:有权限在指定的存储池中创建image的管理员用户名,默认为admin。 ▪adminSecretName <string>:存储有管理员账号认证密钥的Secret资源名称。 ▪adminSecretNamespace <string>:管理员账号相关的Secret资源所在的名称空间。 ▪pool <string>:Ceph存储系统的RBD存储池名称,默认为rbd。 ▪userId <string>:用于映射RBD镜像的Ceph用户账号,默认同adminId字段。 ▪userSecretName <string>:存储有用户账号认证密钥的Secret资源名称。 ▪userSecretNamespace <string>:用户账号相关的Secret资源所在的名称空间。 ▪fsType <string>:存储映像格式化的文件系统类型,默认为ext4。 ▪imageFormat <string>:存储映像的格式,其可用值仅有“1”和“2”,默认值为“2”。 ▪imageFeatures <string>:“2”格式的存储映像支持的特性,目前仅支持layering,默认为空值,并且不支持任何功能。
2)ceph-rbd的动态预配
第一步:创建admin和k8s的secret
与Pod或PV资源上的RBD卷插件配置格式不同的是,StorageClass上的RBD供给者参数不支持使用keyring直接认证到Ceph,它仅能引用Secret资源中存储的认证密钥完成认证操作。因而,我们需要先将Ceph用户admin和kube的认证密钥分别创建为Secret资源对象。
1)在Ceph管理节点上分别获取admin的认证密钥,并经行base64编码,创建admin-secret.yaml ceph集群对client.admin的key经行base64编码 root@ceph01:~# cat /etc/ceph/ceph.client.admin.keyring [client.admin] key = AQCNxVlhDfpPBBAALJ1Bn6KOGXKug1GIaBXYVA== caps mds = "allow *" caps mgr = "allow *" caps mon = "allow *" caps osd = "allow *" root@ceph01:~# echo AQCNxVlhDfpPBBAALJ1Bn6KOGXKug1GIaBXYVA== | base64 QVFDTnhWbGhEZnBQQkJBQUxKMUJuNktPR1hLdWcxR0lhQlhZVkE9PQo= #k8s集群创建admin-storage-secret.yaml apiVersion: v1 kind: Secret metadata: name: admin-storage-secret data: #Please note this value is client.k8s base64 encoded. # ceph auth get client.admin | base64 key: QVFDTnhWbGhEZnBQQkJBQUxKMUJuNktPR1hLdWcxR0lhQlhZVkE9PQo= 2)在Ceph管理节点上分别获取k8s的认证密钥,并经行base64编码,创建k8s-storage-secret.yaml root@ceph01:~# cat /etc/ceph/ceph.client.k8s.keyring [client.k8s] key = AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== caps mon = "allow r" caps osd = "allow rwx pool=k8spool" root@ceph01:~# echo AQAHWGRh/d+JNxAAHrzplwi8WWjwXc/NGdJeAw== | base64 QVFBSFdHUmgvZCtKTnhBQUhyenBsd2k4V1dqd1hjL05HZEplQXc9PQo= #k8s集群创建k8s-storage-secret.yaml apiVersion: v1 kind: Secret metadata: name: k8s-storage-secret data: #Please note this value is client.k8s base64 encoded. # ceph auth get client.k8s | base64 key: QVFBSFdHUmgvZCtKTnhBQUhyenBsd2k4V1dqd1hjL05HZEplQXc9PQo= 2)在Kubernetes集群管理客户端上使用kubectl命令创建为Secret资源 root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f admin-storage-secret.yaml secret/admin-storage-secret created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f k8s-storage-secret.yaml secret/k8s-storage-secret created
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: storage-ceph-rbd annotations: storageclass.kubernetes.io/is-default-class: "true" #设置为默认存储类 provisioner: kubernetes.io/rbd reclaimPolicy: Retain parameters: monitors: 172.168.32.201:6789,172.168.32.202:6789,172.168.32.203:6789 adminId: admin adminSecretName: admin-storage-secret adminSecretNamespace: default pool: k8spool userId: k8s userSecretName: k8s-storage-secret fsType: ext4 imageFormat: "2" imageFeatures: "layering" #1,storageclass.beta.kubernetes.io/is-default-class ##如果设置为true,则为默认的storageclasss。pvc申请存储,如果没有指定storageclass,则从默认的storageclass申请。 #2,adminId:ceph客户端ID,用于在ceph 池中创建映像。默认是 “admin”。 #3,userId:ceph客户端ID,用于映射rbd镜像。默认与adminId相同。 #4,imageFormat:ceph rbd镜像格式,“1” 或者 “2”。默认值是 “1”。 #5,imageFeatures:这个参数是可选的,只能在你将imageFormat设置为 “2” 才使用。 目前支持的功能只是layering。默认是 “",没有功能打开。
待相关Secret资源准备完成后,将示例清单中的StorageClass资源创建在集群上,即可由PVC或PV资源将其作为存储类。
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f storageclass-rbd-demo.yaml storageclass.storage.k8s.io/storage-ceph-rbd created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get sc storage-ceph-rbd NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE storage-ceph-rbd (default) kubernetes.io/rbd Retain Immediate false 7s
第三步:创建pvc的模板
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-dyn-rbd-demo namespace: default spec: accessModes: ["ReadWriteOnce"] volumeMode: Filesystem resources: requests: storage: 3Gi storageClassName: storage-ceph-rbd
运行该清单
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl apply -f pvc-dyn-rbd-demo.yaml persistentvolumeclaim/pvc-dyn-rbd-demo created root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-dyn-rbd-demo Bound pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e 3Gi RWO storage-ceph-rbd 4s root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e 3Gi RWO Retain Bound default/pvc-dyn-rbd-demo storage-ceph-rbd 9s
storageclass会自动在ceph中创建pv
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pvc pvc-dyn-rbd-demo Name: pvc-dyn-rbd-demo Namespace: default StorageClass: storage-ceph-rbd Status: Bound Volume: pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e #pv信息 Labels: <none> Annotations: pv.kubernetes.io/bind-completed: yes pv.kubernetes.io/bound-by-controller: yes volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/rbd Finalizers: [kubernetes.io/pvc-protection] Capacity: 3Gi Access Modes: RWO VolumeMode: Filesystem Used By: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ProvisioningSucceeded 27m persistentvolume-controller Successfully provisioned volume pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e using kubernetes.io/rbd
再通过pv的信息来确定是ceph集群中的那个rbd镜像
root@k8s-master01:/apps/k8s-yaml/pv_pvc# kubectl describe pv pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e Name: pvc-e6f9c06a-58d6-47ee-8ab1-f2fa5ccc8b4e Labels: <none> Annotations: kubernetes.io/createdby: rbd-dynamic-provisioner pv.kubernetes.io/bound-by-controller: yes pv.kubernetes.io/provisioned-by: kubernetes.io/rbd Finalizers: [kubernetes.io/pv-protection] StorageClass: storage-ceph-rbd Status: Bound Claim: default/pvc-dyn-rbd-demo Reclaim Policy: Retain Access Modes: RWO VolumeMode: Filesystem Capacity: 3Gi Node Affinity: <none> Message: Source: Type: RBD (a Rados Block Device mount on the host that shares a pod's lifetime) CephMonitors: [172.168.32.201:6789 172.168.32.202:6789 172.168.32.203:6789] RBDImage: kubernetes-dynamic-pvc-c8d6a084-d5e3-40dd-8dee-a08e715431d7 #RBD镜像信息 FSType: ext4 RBDPool: k8spool RadosUser: k8s Keyring: /etc/ceph/keyring SecretRef: &SecretReference{Name:k8s-storage-secret,Namespace:,} ReadOnly: false Events: <none>
查看ceph中的rbd
root@ceph01:~# rbd ls k8spool k8spv k8srbd kubernetes-dynamic-pvc-55511c43-4d7a-416f-a885-609ba252a904 kubernetes-dynamic-pvc-94fff9aa-7d75-48f5-8c7f-339b1e0927ce kubernetes-dynamic-pvc-c8d6a084-d5e3-40dd-8dee-a08e715431d7 #kubectl describe pv kubernetes-dynamic-pvc-cd037207-04ec-4b89-a56a-d852deac21f8 kubernetes-dynamic-pvc-e620a2c1-9c30-4aad-a144-b677a223470a
7、总结
PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像节点是集群资源一样。 PV是容量插件,如Volumes,但其生命周期独立于使用PV的任何单个pod。 此API对象捕获存储实现的详细信息,包括NFS,iSCSI或特定于云提供程序的存储系统
2) 什么是pvc
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读/写或多次只读)。
3) pv和pvc的联系
pv和pvc的生命周期
PV是群集中的资源。 PVC是对这些资源的请求,并且还充当对资源的索赔检查。 PV和PVC之间的相互作用遵循以下生命周期:
(1)pv的供应方式
可以通过两种方式配置PV:静态或动态。
静态的
集群管理员创建了许多PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用。
动态的
当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,群集可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses:PVC必须请求存储类,管理员必须已创建并配置该类,以便进行动态配置。
(2)绑定
用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态
(3)使用
a)需要找一个存储服务器,把它划分成多个存储空间;
b)k8s管理员可以把这些存储空间定义成多个pv;
c)在pod中使用pvc类型的存储卷之前需要先创建pvc,通过定义需要使用的pv的大小和对应的访问模式,找到合适的pv;
d)pvc被创建之后,就可以当成存储卷来使用了,我们在定义pod时就可以使用这个pvc的存储卷
e)pvc和pv它们是一一对应的关系,pv如果被被pvc绑定了,就不能被其他pvc使用了;
f)我们在创建pvc的时候,应该确保和底下的pv能绑定,如果没有合适的pv,那么pvc就会处于pending状态。
(4)回收策略
当我们创建pod时如果使用pvc做为存储卷,那么它会和pv绑定,当删除pod,pvc和pv绑定就会解除,解除之后和pvc绑定的pv卷里的数据需要怎么处理,目前,卷可以保留,回收或删除
Retain
Recycle (不推荐使用,1.15可能被废弃了)
Delete
· Retain
当删除pvc的时候,pv仍然存在,处于released状态,但是它不能被其他pvc绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略,管理员能够通过下面的步骤手工回收存储卷:
1)删除PV:在PV被删除后,在外部设施中相关的存储资产仍然还在;
2)手工删除遗留在外部存储中的数据;
3)手工删除存储资产,如果需要重用这些存储资产,则需要创建新的PV。
· Delete
删除pvc时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产,例如AWS EBS, 或者Cinder存储卷。
数据卷的缺点: 1、当有多个存储服务器时,数据卷不利于应用者使用 2、所有存储服务器信息暴露给应用者,安全性降低 3、数据卷存储这块配置更具有专业性
数据持久卷的问题: Q1:pv与pvc是什么关系? A:一对一关系 Q2:pv与pvc怎么匹配的? A:默认使用容量和访问模式进行匹配 Q3:pv的容量能限制实际的存储容量? A:取决于后端存储 Q4:容量匹配策略 A:如果申请的容量,pv没有正好的,会分配接近最大的那个pv,如果都满足不了,pod处于pending
配置回收策略: persistentVolumeReclaimPolicy: Recycle
4) 访问模式
PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为 对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器 上以只读的方式导出。每个 PV 卷都会获得自身的访问模式集合,描述的是 特定 PV 卷的能力。
访问模式有:
-
ReadWriteOnce -- 卷可以被一个节点以读写方式挂载;
-
ReadOnlyMany -- 卷可以被多个节点以只读方式挂载;
-
ReadWriteMany -- 卷可以被多个节点以读写方式挂载。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
-
RWO - ReadWriteOnce
-
ROX - ReadOnlyMany
-
RWX - ReadWriteMany
重要提醒! 每个卷只能同一时刻只能以一种访问模式挂载,即使该卷能够支持 多种访问模式。例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式 挂载。
5) STATUS(状态)
一个PV 的生命周期中,可能会处于4中不同的阶段: •Available(可用):表示可用状态,还未被任何PVC 绑定 •Bound(已绑定):表示PV 已经被PVC 绑定 •Released(已释放):PVC 被删除,但是资源还未被集群重新声明 •Failed(失败):表示该PV 的自动回收失败
6) pod、pvc和pv的关系
PersistentVolume(PV)是指由集群管理员配置提供的某存储系统上的一段存储空间,它是对底层共享存储的抽象,将共享存储作为一种可由用户申请使用的资源,实现了“存储消费”机制。通过存储插件机制,PV支持使用多种网络存储系统或云端存储等多种后端存储系统,例如,前面使用的NFS、RBD和Cinder等。PV是集群级别的资源,不属于任何名称空间,用户对PV资源的使用需要通过PersistentVolumeClaim(PVC)提出的使用申请(或称为声明)来完成绑定,是PV资源的消费者,它向PV申请特定大小的空间及访问模式(如rw或ro),从而创建出PVC存储卷,而后再由Pod资源通过PersistentVolumeClaim存储卷关联使用
8.1 pv的动态供给介绍
存储类(storage class)是Kubernetes资源类型的一种,它是由管理员为管理PV之便而按需创建的类别(逻辑组),例如可按存储系统的性能高低分类,或者根据其综合服务质量级别进行分类、依照备份策略分类,甚至直接按管理员自定义的标准进行分类等。不过,Kubernetes自身无法理解“类别”到底意味着什么,它仅仅是将这些当作PV的特性描述。
存储类的好处之一便是支持PV的动态创建。用户用到持久性存储时,需要通过创建PVC来绑定匹配的PV,此类操作需求量较大,或者当管理员手动创建的PV无法满足PVC的所有需求时,系统按PVC的需求标准动态创建适配的PV会为存储管理带来极大的灵活性。 存储类对象的名称至关重要,它是用户调用的标识。创建存储类对象时,除了名称之外,还需要为其定义三个关键字段:provisioner、parameter和reclaimPolicy。
动态PV供给的启用,需要事先由管理员创建至少一个存储类,不同的Provisoner的创建方法各有不同。另外,并非所有的存储卷插件都由Kubernetes内建支持PV动态供给功能。
K8s默认不支持NFS动态供给,需要单独部署社区开发的插件。 项目地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
下载https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy中的class.ymal,rbac.ymal及deployment.yaml
[root@k8s-master01 nfs-subdir-external-provisioner]# pwd /apps/nfs-subdir-external-provisioner [root@k8s-master01 nfs-subdir-external-provisioner]# ll total 12 -rw-r--r-- 1 root root 255 Apr 18 21:33 class.yaml #创建存储类 -rw-r--r-- 1 root root 1066 Apr 18 21:34 deployment.yaml #部署插件,需修改里面NFS服务器地址与共享目录及pod镜像地址 -rw-r--r-- 1 root root 1819 Apr 18 21:33 rbac.yaml #授权访问apiserver [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl apply -f class.yaml storageclass.storage.k8s.io/managed-nfs-storage created [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl apply -f deployment.yaml deployment.apps/nfs-client-provisioner created You have new mail in /var/spool/mail/root [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl apply -f rbac.yaml serviceaccount/nfs-client-provisioner created clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl get sc,pod -o wide NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE storageclass.storage.k8s.io/managed-nfs-storage k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 12m NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/nfs-client-provisioner-64c54cf68d-fvn2j 1/1 Running 0 48s 10.244.58.245 k8s-node02 <none> <none>
2、创建pvc时指定存储类名称,并指定空间大小
[root@k8s-master01 apps]# vim pvc-sc.yml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-claim spec: #storageclass的名称 storageClassName: "managed-nfs-storage" #访问模式 accessModes: - ReadWriteMany #资源大小 resources: requests: storage: 1Gi [root@k8s-master01 apps]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4 1Gi RWX managed-nfs-storage 36s #已经动态绑定了pv
3、配置pod并调用storageclass
[root@k8s-master01 apps]# vim pod-sc.yml apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-pod image: nginx volumeMounts: - name: nfs-pvc mountPath: "/usr/share/nginx/html" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-claim #调用pvc [root@k8s-master01 apps]# kubectl apply -f pod-sc.yml pod/test-pod created [root@k8s-master01 apps]# kubectl get pod NAME READY STATUS RESTARTS AGE nfs-client-provisioner-64c54cf68d-fvn2j 1/1 Running 0 11m test-pod 1/1 Running 0 28s [root@k8s-master01 apps]# kubectl get pod test-pod 。。。。。。 Mounts: /usr/share/nginx/html from nfs-pvc (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-4zscw (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: nfs-pvc: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: test-claim ReadOnly: false 。。。。。。 #pvc已经被pod挂载 #在harbor:192.168.32.41上查看nfs服务器,发现已经创建了pv目录 [root@harbor ~]# cd /ifs/kubernetes/default-test-claim-pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4/ [root@harbor default-test-claim-pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4]#ll total 0 [root@harbor default-test-claim-pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4]# #进入pod [root@k8s-master01 apps]# kubectl exec -it test-pod -- bash root@test-pod:/# ls /usr/share/nginx/html/ root@test-pod:/# #在nfs上创建aa.log文件 [root@harbor default-test-claim-pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4]# touch aa.log [root@harbor default-test-claim-pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4]# ll total 0 -rw-r--r-- 1 root root 0 Apr 18 22:03 aa.log #在pod中查看 root@test-pod:/# ls /usr/share/nginx/html/ aa.log #pod已经有在nfs上创建的文件aa.log
4、删除pod
[root@k8s-master01 apps]# kubectl delete -f pod-sc.yml pod "test-pod" deleted [root@k8s-master01 apps]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-387ffb9f-d471-4310-9cef-3bb27d2775e4 1Gi RWX managed-nfs-storage 13m #删除了pod,不会影响pvc
5、删除pvc
[root@k8s-master01 apps]# kubectl delete -f pvc-sc.yml persistentvolumeclaim "test-claim" deleted [root@k8s-master01 apps]# kubectl get pvc No resources found in default namespace. #在nfs上查看 [root@harbor ~]# ll /ifs/kubernetes/ total 0 #可以发现,nfs上的pv目录随这pvc的删除也被删除 #为了数据的安全会修改class.yaml中 archiveOnDelete参数,设置为"true",当pvc被删除后,nfs上的pv目录会有备份,数据会保留 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: managed-nfs-storage provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME' parameters: archiveOnDelete: "true" [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl delete -f class.yaml storageclass.storage.k8s.io "managed-nfs-storage" deleted [root@k8s-master01 nfs-subdir-external-provisioner]# kubectl apply -f class.yaml storageclass.storage.k8s.io/managed-nfs-storage created #重新创建pvc后,再删除PVC [root@k8s-master01 apps]# kubectl apply -f pvc-sc.yml persistentvolumeclaim/test-claim created [root@k8s-master01 apps]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-8b5e4ae4-a3a2-4377-a87d-a49171bae36c 1Gi RWX managed-nfs-storage 4s #nfs上有pv目录 [root@harbor ~]# ll /ifs/kubernetes/default-test-claim-pvc-8b5e4ae4-a3a2-4377-a87d-a49171bae36c/ total 0 #删除pvc [root@k8s-master01 apps]# kubectl delete -f pvc-sc.yml persistentvolumeclaim "test-claim" deleted [root@k8s-master01 apps]# kubectl get pvc No resources found in default namespace. #nfs上会有pv的备份 [root@harbor ~]# ll /ifs/kubernetes/archived-default-test-claim-pvc-8b5e4ae4-a3a2-4377-a87d-a49171bae36c/ total 0