Kubernetes对象中的PersistentVolume、PersistentVolumeClaim和StorageClass的概念关系
Kubernetes容器要持久化数据,离不开volume,k8s的volume和Docker原生概念中的volume有一些差别,不过本次不讲这个,本次要明确的是k8s持久化数据用到的几个对象PersistentVolume、PersistentVolumeClaim和StorageClass,首先明确这既然都是k8s对象,就可以通过API来创建的。
k8s的volume支持的类型有很多,例如emptyDir、hostPath、nfs等,这些相对好理解,还有一种就是ersistentVolumeClaim,刚开始接触的时候不太理解这个对象该如何使用,本次主要介绍ersistentVolumeClaim相关的概念和使用方法。
开始先要提一下PersistentVolume(PV)对象,PersistentVolume和Volume一样是群集中的一块存储区域,然而Kubernetes将PersistentVolume抽象成了一种集群资源,类似于集群中的节点(Node)对象,这意味着我们可以使用Kubernetes API来创建PersistentVolume对象。PV与Volume最大的不同是PV拥有着独立于Pod的生命周期。
而PersistentVolumeClaim(PVC)代表了用户对PV资源的请求。用户需要使用PV资源时,只需要创建一个PVC对象(包括指定使用何种存储资源,使用多少GB,以何种模式使用PV等信息),Kubernetes会自动为我们分配我们所需的PV。如果把PersistentVolume类比成集群中的Node,那么PersistentVolumeClaim就相当于集群中的Pod,Kubernetes为Pod分配可用的Node,类似的也可以理解成为PersistentVolumeClaim分配可用的PersistentVolume。
1、静态创建PV对象
可以直接静态创建一个PV对象,作为一个存储供PVC使用,创建PV主要有下面几个参数
accessModes 访问模式有下面三种:
- ReadWriteOnce(RWO):是最基本的方式,可读可写,但只支持被单个 Pod 挂载。
- ReadOnlyMany(ROX):只读模式,可以被多个 Pod 挂载。
- ReadWriteMany(RWX):可读可写,并且可以被被多个 Pod 挂载。
- Retain,不清理,删除PVC时,PV仍然存在并标记为“released”(需要删除时需要手动清理)
- Recycle,删除数据,对卷执行清理(rm -rf / thevolume / *),并使其再次可用于新索引(只有 NFS 和 HostPath 支持)
- Delete,删除存储资源,会从Kubernetes中删除PV对象,以及外部基础结构中的关联存储资产,例如AWS EBS,GCE PD,Azure磁盘或Cinder卷
#静态创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
定义PV时,我们需要指定其底层存储的类型,例如上文中创建的PV,底层使用nfs存储,支持的类型很多,例如awsElasticBlockStore、FC、nfs、RBD、CephFS、Hostpath、StorageOS等等,可以查看官方文档。
可以查看当前集群下创建的PV对象,kubectl get PersistentVolume --all-namespaces
2、静态创建PVC对象
创建PV之后,并没有被使用,如果想使用这个PV就需要创建PVC了,最后在pod中指定使用这个PVC而建立起pod和PV的关系。
#静态创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
#pod使用PVC
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: dockerfile/nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
分析一下上面的代码:
创建PVC时指定了accessModes
= ReadWriteOnce
。这表明这个PVC希望使用accessModes = ReadWriteOnce
的PV。创建PVC时指定了volumeMode
= Filesystem。这表明这个PVC希望使用
volumeMode= Filesystem的PV。
- 创建PVC时指定了
storageClassName: slow
,此配置用于绑定PVC和PV,意思是这个PVC希望使用storageClassName=slow
的PV。我们可以看到最上面创建PV时也包含storageClassName=slow
的配置。 - PVC还可以指定PV必须满足的Label,如加了selector匹配
matchLabels: release: "stable"
。这表明此PVC希望使用Label:release: "stable"
的PV。 - 最后是resources声明,跟pod一样可以声明使用特定数量的资源,
storage: 8Gi
表明此PVC希望使用8G的Volume资源。
通过上面的分析,我们可以看到PVC和PV的绑定,不是简单的通过Label来进行。而是要综合storageClassName,accessModes,matchLabels以及storage来匹配符合条件的PV进行绑定。
3、动态创建PV对象
上面我们通过描述文件静态创建PV对象最终完成和pod的绑定,这种直接通过描述文件创建PV的方式称为静态创建,这样的创建方式有弊端,假如我们创建PV时指定大小为50G,而PVC请求80G的PV,那么此PVC就无法找到合适的PV来绑定。因此实际生产中更多的使用PV的动态创建。
PV的动态创建依赖于StorageClass对象。我们不需要手动创建任何PV,所有的工作都由StorageClass为我们完成,可以查看集群中的StorageClass信息 ,kubectl get StorageClass --all-namespaces
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://192.168.10.100:8080"
restuser: ""
secretNamespace: ""
secretName: ""
reclaimPolicy: Retain
allowVolumeExpansion: true
这个例子使用创建了一个基于glusterfs分布式存储的StorageClass,只有allowVolumeExpansion=teue时,才能扩展PVC,要为PVC请求更大的卷,请编辑PVC对象并指定更大的大小,这会触发底层PersistentVolume的卷的扩展。永远不会创建新的,而是调整现有卷的大小。
StorageClass的定义包含四个部分:
- provisioner:指定 Volume 插件的类型,包括内置插件(如kubernetes.io/glusterfs、kubernetes.io/aws-ebs)和外部插件(如 external-storage 提供的 ceph.com/cephfs)。
- parameters:指定 provisioner 的选项,比如 glusterfs 支持 resturl、restuser 等参数。
- mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.1 等选项。
- reclaimPolicy:指定回收策略,同 PV 的回收策略。
手动创建的PV时,我们指定了storageClassName=slow的配置项,然后Pod定义中也通过指定storageClassName=slow,从而完成绑定。而通过StorageClass实现动态PV时,我们只需要指定StorageClass的metadata.name即可,这个名称非常重要,用户通过名称类请求特定的存储类,储类的对象一旦被创建,name将不能再更改。
回到上文中创建PVC的例子,此时PVC指定了storageClassName=slow。那么Kubernetes会在集群中寻找是否存在metadata.name=slow的StorageClass,如果存在,此StorageClass会自动为此PVC创建一个accessModes = ReadWriteOnce,并且大小为8GB的PV。
或者直接写到一起
#上面的部分省略了
volumeMounts:
- name: data-dir
mountPath: /var/mysql/data
#volumes:
#- name: data-dir
# hostPath:
# path: /opt/mysql volumeClaimTemplates:
- metadata:
#annotations: volume.alpha.kubernetes.io/storage-class: xxxx
creationTimestamp: null
name: data-dir
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: slow
volumeMode: Filesystem
status:
phase: Pending
下面是参考官方的一种比较通用的写法,基于helm的,直接将PVC的代码写到volumes的后面,根据values.yaml中的参数生成是挂载PVC的存储还是其他存储,可以作为以后自己写脚本的参考
volumes:
- name: config
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
- name: probes
configMap:
name: {{ template "redis-ha.fullname" . }}-probes
{{- if .Values.sysctlImage.mountHostSys }}
- name: host-sys
hostPath:
path: /sys
{{- end }}
{{- if .Values.persistentVolume.enabled }}
volumeClaimTemplates:
- metadata:
name: data
annotations:
{{- range $key, $value := .Values.persistentVolume.annotations }}
{{ $key }}: {{ $value }}
{{- end }}
spec:
accessModes:
{{- range .Values.persistentVolume.accessModes }}
- {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolume.size | quote }}
{{- if .Values.persistentVolume.storageClass }}
{{- if (eq "-" .Values.persistentVolume.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistentVolume.storageClass }}"
{{- end }}
{{- end }}
{{- else if .Values.hostPath.path }}
- name: data
hostPath:
path: {{ tpl .Values.hostPath.path .}}
{{- else }}
- name: data
emptyDir: {}
{{- end }}
总结一下整个过程
1)集群管理员预先创建存储类(StorageClass);
2)用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim);
3)存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume);
4)系统读取存储类的信息;
5)系统基于存储类的信息,在后台自动创建PVC需要的PV;
6)用户创建一个使用PVC的Pod;
7)Pod中的应用通过PVC进行数据的持久化;
8)而PVC使用PV进行数据的最终持久化处理。
官方文档 https://kubernetes.io/docs/concepts/storage/persistent-volumes/
Kubernetes对象中的PersistentVolume、PersistentVolumeClaim和StorageClass的概念关系的更多相关文章
- Kubernetes对象
Kubernetes对象 在之前的文章已经讲到了很多Kubernets对象,包括pod,service,deployment等等.Kubernets对象是一种持久化,表示集群状态的实体.它是一种声明式 ...
- Kubernetes对象之Service
系列目录 通过ReplicaSet来创建一组Pod来提供具有高可用性的服务.虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题: Pod IP仅仅是集群内可见的虚拟IP,外部无法访问. ...
- Kubernetes 对象管理的三种方式
Kubernetes 中文文档 1. Kubernetes 对象管理的三种方式对比 Kubernetes 中的对象管理方式,根据对象配置信息的位置不同可以分为两大类: 命令式:对象的参数通过命令指定 ...
- Flexigrid从对象中加载数据
(有问题,在找…………) Flexigrid是用来动态加载数据的一种比较好(老)的Jquery表插件,然后有些时候,我们需要其从本地或者jQuery对象中加载数据,比如有这么个需求,页面显示中有两个表 ...
- js 对象数组根据对象中的属性排序
function createComparisonFunction(propertyName){ return function(object1,object2){ var value1 = obje ...
- javascript,检测对象中是否存在某个属性
检测对象中属性的存在与否可以通过几种方法来判断. 1.使用in关键字. 该方法可以判断对象的自有属性和继承来的属性是否存在. var o={x:1}; "x" in o; //tr ...
- 将filenames里的每个字符串输出到out文件对象中注意行首的缩进
在Linux上用强大的shell脚本应该也可以完成,可是使用Windows的朋友呢?其实象这样一个简单任务用Python这个强大脚本语言只要几条语句就可以搞定了.个大家知道,要完成这样一个任务根本不用 ...
- 将CachedRowSet中的数据转储到对象中
虽然还有很多bug,但凑合能用,就是将CachedRowSet中的数据转换成对象或List.省去了繁琐难看的一系列get/set方法. 先说调用: 注: cachedRowSet是查询的结果集 Stu ...
- window对象中的常见方法
<body><!-- window对象中的常见方法--><script type="text/javascript"> var timeid; ...
随机推荐
- php命令行模式下加载的php.ini文件可能和web模式下加载的php.ini不一致
php命令行模式下加载的php.ini文件可能和web模式下加载的php.ini不一致 命令行下查看加载的php.ini的路径: php -i|grep php.ini web模式下查看: <? ...
- “但行好事 莫问前程 只问耕耘 不问收获 成功不必在我 而功力必不唐捐” 科技袁人·年终盛典——5G是科技时代非常重要的基础设施
中国的科技实力:用数据对比展示当前中国整体科技实力在国际中的发展水平和未来的发展趋势. 主要分为基础研究和应用研究.其中基础研究通过论文数据进行对比展示,应用研究通过发明专利数据. 又分别结合当今中国 ...
- centos(linux)-maven配置
1.确认已经安装jdk 2.下载 3.解压 tar -zxvf apache-maven-xxx(tab补全文件名) 4.配置环境变量 (1)sudo vim /etc/profile (2)在最下面 ...
- 【POJ - 1742】Coins (多重背包)
Coins 直接翻译了 Descriptions 给出硬币面额及每种硬币的个数,求从1到m能凑出面额的个数. Input 多组数据,每组数据前两个数字为n,m.n表示硬币种类数,m为最大面额,之后前 ...
- Vue触发隐藏input file的方法
1.使用input透明覆盖法 将input的z-index设置为1以上的数字并覆盖到需点击的内容上,将input的样式opacity设置为0(即为透明度为0),这样通过绑定在input上的change ...
- C/C++编程
基本的数据类型: 整型 浮点型(单精度.双精度) 在c语言中,所有的变量声明必须在任何执行语句之前(对当前域来说), 否则编译的时候会出现变量是未声明的标识符的错误. main 入口参数:argc 和 ...
- JS中删除数组中的元素方法
删除指定下标数组元素 Array.prototype.del=function(index){ if(isNaN(index)||index>=this.length){ return fals ...
- Django各个文件中常见的模块导入
app01 app01 urls.py from django.conf.urls import url from django.contrib import admin from admins im ...
- 课程设计之C/C++实现用户登录注册
最近的一个课程设计要求的一个用户登录的程序,通常软件网页等的用户登录注册都是涉及到数据库.但像课程设计这种小程序要求的安全度不高就可以用c/c++实现. 首先,我们要清楚用户登录的流程.应该大家对这些 ...
- 局部内部类的final问题
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量就必须是final的(或者只赋值一次) 从Java8开始,只要局部变量事实不变那么final关键字可以省略 为什么需要保证变量为final, ...