九、Kubernetes之 PV和PVC

通过网络存储卷及使用示例可知,用户必须要清晰了解用到的网络存储系统的访问细节才能完成存储卷相关的配置任务,例如RBD存储卷插件配置中的监视器(monitor)、存储池(pool)、存储映像(image)和密钥环(keyring)等来自于Ceph存储系统中的概念,这就要求用户对该类存储系统有着一定的了解才能够顺利使用。这与Kubernetes向用户和开发隐藏底层架构的目标有所背离,最好对存储资源的使用也能像计算资源一样,用户和开发人员既无须了解Pod资源究竟运行在哪个节点,也不用了解存储系统是什么设备、位于何处以及如何访问。

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所指向的存储空间,如图所示。

尽管PVC及PV将存储资源管理与使用的职责分离至用户和集群管理员两类不同的人群之上,简化了用户对存储资源的使用机制,但也对二者之间的协同能力提出了要求。管理员需要精心预测和规划集群用户的存储使用需求,提前创建出多种规格的PV,以便于在用户声明PVC后能够由PV控制器在集群中找寻到合适的甚至是最佳匹配的PV进行绑定。

这种通过管理员手动创建PV来满足PVC需求的静态预配(static provisioning)存在着不少的问题。

第一,集群管理员难以预测出用户的真实需求,很容易导致某些类型的PVC无法匹配到PV而被挂起,直到管理员参与到问题的解决过程中。
第二,那些能够匹配到PV的PVC也很有可能存在资源利用率不佳的状况,例如一个声明使用5G存储空间的PVC绑定到一个20GB的PV之上。

解决方案是一种称为动态预配、按需创建PV的机制。集群管理员要做的仅是事先借助存储类(StorageClass)的API资源创建出一到多个“PV模板”,并在模板中定义好基于某个存储系统创建PV所依赖的存储组件(例如Ceph RBD存储映像或CephfFS文件系统等)时需要用到的配置参数。创建PVC时,用户需要为其指定要使用PV模板(StorageClass资源),而后PV控制器会自动连接相应存储类上定义的目标存储系统的管理接口,请求创建匹配该PVC需求的存储组件,并将该存储组件创建为Kubernetes集群上可由该PVC绑定的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)。

这4个组件中,存储卷插件是其他3个组件的基础库,换句话说,PV控制器、AD控制器和存储卷管理器均构建于存储卷插件之上,以提供不同维度管理功能的接口,具体的实现逻辑均由存储卷插件完成。

除了创建、删除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种。

处于绑定状态的PVC删除后,相应的PV将转为Released状态,之后的处理机制依赖于其回收策略。而处于绑定状态的PV将会导致相应的PVC转为Lost状态,而无法再由Pod正常使用,除非PVC再绑定至其他Available状态的PV之上,但应用是否能正常运行,则取决于对此前数据的依赖度。另一方面,为了避免使用中的存储卷被移除而导致数据丢失,Kubernetes自1.9版引入了“PVC保护机制”,其目的在于,用户删除了仍被某Pod对象使用中的PVC时,Kubernetes不会立即移除该PVC,而是会推迟到它不再被任何Pod对象使用后方才真正执行删除操作。处于保护阶段的PVC资源的status字段值为Termination,并且其Finalizers字段值中包含有kubernetes.io/pvc-protection。

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在某个特定时刻仅可基于一种模式进行存取,哪怕它同时支持多种模式。

1) NFS 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) 使用keyring文件来创建ceph-rbd-pv

下面是另一个PV资源的配置清单(pv-rbd-demo.yaml),它使用了RBD存储后端,空间大小等同于指定的RBD存储映像的大小(这里为2GB),并限定支持的访问模式为RWO,回收策略为Retain。除此之外,该PV资源还拥有一个名为usedof的资源标签,该标签可被PVC的标签选择器作为筛选PV资源的标准之一。

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>

3)使用Secret来创建ceph-rbd-pv

这里以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对应的持久存储卷。 定义PVC时,用户可通过访问模式(accessModes)、数据源(dataSource)、存储资源空间需求和限制(resources)、存储类、标签选择器、卷模型和卷名称等匹配标准来筛选集群上的PV资源,其中,resources和accessModes是最重要的筛选标准。PVC的Spec字段的可嵌套字段有如下几个。

▪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

1) NFS PV示例

下面的配置清单(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)ceph-rbd PVC示例

下面这个定义在pvc-rbd-0002.yaml中的配置清单定义了一个PVC资源,除了期望的访问模式、卷模型和存储空间容量边界之外,它使用了标签选择器来匹配PV资源的标签。

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

删除一个PVC将导致其绑定的PV资源转入Released状态,并由相应的回收策略完成资源回收。反过来,直接删除一个仍由某PVC绑定的PV资源,会由PVC保护机制延迟该删除操作至相关的PVC资源被删除。

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所绑定

5、在Pod中使用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
......

6、存储类StorageClass

存储类也是Kubernetes系统上的API资源类型之一,它位于storage.k8s.io群组中。存储类通常由集群管理员为管理PV资源之便而按需创建的存储资源类别(逻辑组),例如可将存储系统按照其性能高低或者综合服务质量级别分类、依照备份策略分类,甚至直接按管理员自定义的标准分类等。存储类也是PVC筛选PV时的过滤条件之一,这意味着PVC仅能在其隶属的存储类之下找寻匹配的PV资源。不过,Kubernetes系统自身无法理解“类别”到底意味着什么,它仅仅把存储类中的信息当作PV资源的特性描述使用。

 

存储类的最重要功能之一便是对PV资源动态预配机制的支持,它可被视作动态PV资源的创建模板,能够让集群管理员从维护PVC和PV资源之间的耦合关系的束缚中解脱出来。需要用到具有持久化功能的存储卷资源时,用户只需要向满足其存储特性要求的存储类声明一个PVC资源,存储类将会根据该声明创建恰好匹配其需求的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,默认为空值,并且不支持任何功能。

提示 存储类接入其他存储系统时使用的参数请参考https://kubernetes.io/docs/concepts/storage/storage-classes/

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

第二步:创建storageclass

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的模板

pvc-dyn-rbd.yaml

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

查看pvc来确定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

3) 各存储插件对动态预配方式的支持状况

 

7、总结

1) 什么是pv

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、NFS的动态预配(StorageClass)

8.1 pv的动态供给介绍

存储类(storage class)是Kubernetes资源类型的一种,它是由管理员为管理PV之便而按需创建的类别(逻辑组),例如可按存储系统的性能高低分类,或者根据其综合服务质量级别进行分类、依照备份策略分类,甚至直接按管理员自定义的标准进行分类等。不过,Kubernetes自身无法理解“类别”到底意味着什么,它仅仅是将这些当作PV的特性描述。

存储类的好处之一便是支持PV的动态创建。用户用到持久性存储时,需要通过创建PVC来绑定匹配的PV,此类操作需求量较大,或者当管理员手动创建的PV无法满足PVC的所有需求时,系统按PVC的需求标准动态创建适配的PV会为存储管理带来极大的灵活性。 存储类对象的名称至关重要,它是用户调用的标识。创建存储类对象时,除了名称之外,还需要为其定义三个关键字段:provisioner、parameter和reclaimPolicy。

 

StorageClass Spec中的字段是定义存储类时最重要的字段,其包含以下五个可用字段。·provisioner(供给方):即提供了存储资源的存储系统,存储类要依赖Provisioner来判定要使用的存储插件以便适配到目标存储系统。Kubernetes内建有多种供给方(Provisioner),这些供给方的名字都以“kubernetes.io”为前缀。另外,它还支持用户依据Kubernetes规范自定义Provisioner。·parameters(参数):存储类使用参数描述要关联到的存储卷,不过,不同的Provisioner可用的参数各不相同。·reclaimPolicy:为当前存储类动态创建的PV指定回收策略,可用值为Delete(默认)和Retain;不过,那些由管理员手工创建的PV的回收策略则取决于它们自身的定义。·volumeBindingMode:定义如何为PVC完成供给和绑定,默认值为“VolumeBinding Immediate”;此选项仅在启用了存储卷调度功能时才能生效。·mountOptions:由当前类动态创建的PV的挂载选项列表。

动态PV供给的启用,需要事先由管理员创建至少一个存储类,不同的Provisoner的创建方法各有不同。另外,并非所有的存储卷插件都由Kubernetes内建支持PV动态供给功能。

任何支持PV动态供给的存储系统都可以在定义为存储类后由PVC动态申请使用,这对于难以事先预估使用到的存储空间大小及存储卷数量的使用场景尤为有用,例如由StatefulSet控制器管理Pod对象时,存储卷是必备资源,且随着规模的变动,存储卷的数量也会随之变动。 另外,用户也可以使用云端存储提供的PV动态供给机制,如AWS EBS、AzureDisk、Cinder或GCEPersistentDisk等,将Kubernetes部署于IaaS云端时,此种存储方式使用的较多。各类云存储动态供给的具体使用方式请参考相关的使用手册。

 

8.2 配置nfs的动态供给

 

1、部署NFS动态供给插件

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
I have a dream so I study hard!!!
原文地址:https://www.cnblogs.com/yaokaka/p/15400151.html