赞
踩
为了能够让pod在运行期间使用磁盘资源,kubernetes为pod定义了volume属性,和pod拥有相同的生命周期,pod创建时创建 ,pod删除时删除,volume本身并不是一个独立的kubernetes资源对象,而是pod的一个属性,volume可以理解为不同的存储类型,常见的可以支持的类型包括:
对于卷(volume)的使用,一般分两步:
一个简单的例子:
apiVersion: v1 kind: Pod metadata: name: fortune spec: containers: - image: luksa/fortune name: html-generator volumeMounts: //在html卷下挂载一个路径 - name: html mountPath: /var/htdocs - image: nginx:alphine name: web-server volumeMounts: //在html卷下挂载一个路径 - name: html mountPath: /usr/share/nginx/html readOnly: true volumes: //一个类型为empty的volume,名字叫html - name: html emptyDir: {} medium: Memory //可以指定卷的类型,使用内存而不是磁盘,提高效率
emptyDir是最简单的卷类型,其他卷都是基于empty进行构建的,举个例子,类型为git-repo的卷类型 ,其yaml如下:
volumes: //一个类型为empty的volume,名字叫html
- name: html
gitRepo: {}
repository: xxxxxxxxx
reverison: master
directory: .
每次pod在删除创建的到时候都会从git repo拉取最新的文件,按照sidecar的逻辑来看,应该将git-repo更新的逻辑放在用一个pod的不同容器中,减少对住应用程序的影响。
以上两种volume都属于临时性存储,当pod删除时,卷的内容和卷本身都会删除,hostPath则不会,在pod删除后,新创建的pod仍然能够访问上一个pod修改后的数据,volume和volume的内容都得到了保存。只不过在使用hostPath的时候要注意连续两次的pod都必须在同一台主机上,不然会出现找不到上一个pod卷的问题。
和volume一样,用以提供给pod进行数据持久化使用,只不过persistVolume和volume的区别在于persistVolume在pod删除后仍然能够在下一个pod启动后继续使用。本身和其存储的内容不会丢失,其实一个用hostPath类型volume挂载的目录已经能够实现数据的不丢失,但是受限于节点的限制,因此为了实现前后两次pod能够使用同一份数据,需要提供NAS类型的存储能力作为volume。
举个例子:NFS类型的volume
volumes: //一个类型为empty的volume,名字叫html
- name: html
nfs:
server: xxx.xx.xx.xx
path: /xxxx/xxxx
通过声明一个类型为NFS的volume,pod在重启后可以仍然使用相同server、相同的path,可以实现读取上一个pod的volume的数据。这样做有一些弊端,比如如果NFS的地址发生了改变,name需要对所有涉及的pod进行全量的重启,导致断服,所以一种比较好的方式是将的volume存储提供服务与pod的配置分离开,当底层发生变化时,不影响上层的服务。
结合两个目标:
使用NFS的 volume和Persist volume是两个概念,NFS的volume是卷的一种类型,支持持久化存储;Persist volume是Kubernetes的一种资源对象,在pod的使用逻辑上存在一定的区别,两种流程(流程1无PV/PVC,只有volume):
1、使用NFS的volume:用户创建pod,pod内定义了基于NFS的volume,其中包括server的ip和NFS的path,pod将自己的路径挂载在该volume上。
2、使用NFS类型的PV,用户创建pod,pod定义了pod关联的 PVC;用户定义PVC,PVC传递给api server,kubernetes基于PVC的描述,寻找或创建合适的PV(基于NFS进行创建),并将PVC绑定到对应的PV上;
与卷(volume)不同,持久卷(PV)/持久卷声明(PVC)都是Kubernetes的资源对象,kubernetes可以对PV/PVC进行管理,举一个PVC的例子:
apiVersion: v1
kind: PersistentVolume
metadata:
name: xxxxx-pv
spec:
capacity:
storage: 5G
accessModes:
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimRolicy: Retain
gcePersistentDisk:
pdName: volumePD
fsType: ext4
如果在pod中要引用,则需要:
spec:
volumes:
- name: volumePD-data
gcePersistentDisk:
pdName: volumePD
fsType: ext4
pod在使用PV时的步骤可以理解为以下:
经过如上操作,当pod删除后,PV仍然存在,当pod删除后下次创建,仍然能绑定到相同的PV,此时的PV如果发生改变(比如NFS的地址发生改变),pod不需要重启,这是使用PV优于直接创建volume挂载NFS的好处
使用PV解决了开发者自己生命volume带来的底层存储和上层应用的强耦合,那么每次开发者使用磁盘时,都得自己创建自己的PV吗?一个集群内的不同开发者可能会使用不同的PV,每种PV都有自己的定义方式,这对开发者来说提高了开发和学习的成本,所以引入PVC(persistentVolumeClaim)的概念,来解决这个问题。
看一个persistentVolumeClaim的例子:
aoiVersion:
kind: persistentVolumeClaim
metadata:
name: my-pvc
spec:
resources:
requests:
storage: 1G
accessModes:
- ReadWriteOnce
storageClassName: ""//主要通过这个来隔离开发者与底层存储细节,开发者只需要关心resources的内容,申请过程由storageClass来完成,如果不填,那么PVC会默认的从已有的PV中寻找能够和resource匹配的对象来进行绑定,如果找不到就悲剧了
在pod中引用PVC时,如下:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- image: myImage
name: imageName
volumeMounts:
- name: my-volume-data
mountPath: /data
volumes:
- name: my-volume-data
persistenVolumeClaim:
claimName: my-pvc
其实和最开始我们使用volume自己挂NFS、emptyDir啥的很像,只是volumes的创建(绑定)是通过PVC来完成的,除此之外,不需要单独创建PV,但需要的单独创建PVC,pod绑定到PVC,由PVC来想办法创建或绑定到对应的PV。
当我们创建并使用一个PVC后,如果persistentVolumeClaimPolicy如果是Retain,PVC对应的PV会持久的绑定,如果手贱在pod删除后,把PVC删除了,那么再次创建的pod mount的PVC会一直pending,无法获得对应的PV(PV对应的状态时Released)
Retain的意思就是希望在PVC删除的情况下仍然保留PV的数据,如果还想用之前的PVC,那么只能手动删除对应的PV,PVC还提供delete、recycle类型的PVC,当配置为delete或recycle后,PVC创建或挂在的PV可以被不同的PVC反复使用,不会一直保留上一次的数据。但是不是所有PVC的持久化存储class都支持,需要具体去调研
截止目前我们对PVC的使用,已经将开发者对PV具体存储细节隔离开了,但是仅仅按照上述方式,那么PV的创建仍然需要一个管理员来完成,管理员完成PV的创建后,其他开发者通过定义自己的PVC(resources),获得对应的磁盘资源,如果希望持久获得就配置Retain,如果希望共享使用,就配置delete/recycle,当我们使用storageClass这个kubernetes资源对象时,就可以解放团队中的PV管理员角色了。
一个storageClass的例子:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: my-storage-class
provisioner: xxxxxx
paramters:
type: xxxx
zone: xxxx
StorageClass定义了用哪个置备程序来创建或绑定持久卷,StorageClass是kubernetes的一个资源对象,我们甚至可以自己定义自己的置备程序,例如创建一个myStorageProvider作为pod在kubernetes,定期通过api server获取当前有哪些PVC使用了自己,确定后通过自己的逻辑为对应的pod创建PV并实现PV-PVC的绑定过程,当然这是猜测,后续如果这部分源码才能肯定。
之前的例子中仅仅使用了PVC,但storageClass字段对应是空,kubernetes会从已有的满足条件的PV中寻找合适的进行绑定,如果没有则不会创建新的,这部分逻辑其实是有默认定义的:一个默认的StorageClass,会定义如果在PVC中没有对应的StorageClass,那么就会通过该置备程序来实现PV的创建或绑定。
回看关于pod挂载磁盘的过程,目前可以看到从简单粗暴到逐步合理使用kubernetes特性:
L1:我的一个pod内多个容器想共享一部分数据,怎么破?创建一个volume,pod内的每个container都volumeMount上就好了
L2:pod重启了,数据都丢了,怎么破?创建volume的时候,别用emptyDir类型,用hostPath将数据存储在主机上就好了
L3:一个节点坏了,被删掉了,在之前node上的数据就再也找不到了(在hostPath上),创建volume的时候,用NFS这种远程的存储就好了
L4:NFS节点坏了,换了个集群,ip全变了,pod上再也找不到之前的数据,怎么破?不要再创建volume了,创建一个PV(persistentVolume)这样 的kubernetes资源,PV的类型写为NFS,当NFS的服务ip变更时,修改PV中的NFS server地址就好了
L5:
用户A使用时,自己创建了PV-A
用户B使用时,也需要PV,但PV创建比较麻烦,照抄了PV-A
用户C使用时,也需要PV,但存储类型和A/B不同,所以得自己去研究一下自己需要用到的存储类型PV应该如何创建
使用PVC,A/B/C三个用户只需要定义自己对资源的需求(resource字段定义),同时指定自己所用的存储底层支持的StorageClass,这样在开发者界面看到的就是“所需资源+置备程序Class名称”
L6:市面上没有合适自己产品的PVC置备程序,用户想自己定义一个,用户可以搭建自己的置备程序并按照标准接入kubernetes;其他用户在使用自研的存储置备程序时(自研的置备程序在安装时会创建对应的StorageClass),直接在PVC中指定自研置备程序的storageClassName,同时还可以修改kubernetes默认的置备程序(当用户的PVC中的StorageClass没有定义时生效)
当前项目产品使用的置备程序是rook,回头可以就rook简单写一下流程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。