主讲内容:docker/kubernetes 云原生技术,大数据架构,分布式微服务,自动化测试、运维。
视频地址:ke.qq.com/course/419718
全栈工程师开发手册 (作者:栾鹏)
架构系列文章
K8S的存储系统从基础到高级又大致分为三个层次:普通Volume,Persistent Volume 和动态存储供应(dynamic provisioning)。
单节点Volume
单节点Volume是最简单的普通Volume,它和Docker的存储卷类似,使用的是Pod所在K8S节点的本地目录。具体有两种,一种是 emptyDir,是一个匿名的空目录,由Kubernetes在创建Pod时创建,删除Pod时删除。另外一种是 hostPath,与emptyDir的区别是,它在Pod之外独立存在,由用户指定路径名。这类和节点绑定的存储卷在Pod迁移到其它节点后数据就会丢失,所以只能用于存储临时数据或用于在同一个Pod里的容器之间共享数据。
跨节点存储卷
这种存储卷不和某个具体的K8S节点绑定,而是独立于K8S节点存在的,整个存储集群和K8S集群是两个集群,相互独立。
跨节点的存储卷在Kubernetes上用的比较多,如果已有的存储不能满足要求,还可以开发自己的Volume插件,只需要实现Volume.go 里定义的接口。如果你是一个存储厂商,想要自己的存储支持Kubernetes 上运行的容器,就可以去开发一个自己的Volume插件。普通volume目前支持的各种存储插件及情况如下:
简介
PersistentVolume(pv)和PersistentVolumeClaim(pvc)是k8s提供的两种API资源,用于抽象存储细节。管理员关注于如何通过pv提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。
pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式。
和普通Volume的区别
普通Volume和使用它的Pod之间是一种静态绑定关系,在定义Pod的文件里,同时定义了它使用的Volume。Volume 是Pod的附属品,我们无法单独创建一个Volume,因为它不是一个独立的K8S资源对象。
而Persistent Volume 简称PV是一个K8S资源对象,所以我们可以单独创建一个PV。它不和Pod直接发生关系,而是通过Persistent Volume Claim,简称PVC来实现动态绑定。Pod定义里指定的是PVC,然后PVC会根据Pod的要求去自动绑定合适的PV给Pod使用。
pv和pvc运行原理
PersistentVolumeClaimBinder以单例模式运行在master上,它会监控所有pvc并将它们绑定到与请求资源最接近的足量的pv上。也就是说pvc占用的实际存储资源可能会大于请求的资源。k8s不保证底层存储的高可用,需要提供者负责。
NewPersistentVolumeOrderedIndex用来索引不同访问模式的pv并基于容量排序。PersistentVolumeClaimBinder根据pvc的需求来查找索引。
pv是全局的,pvc可以指定namespace。
默认情况下pv使用的回收策略为Retain,此时如果绑定的pvc删除后,pv将处于“Released”状态,需要手动处理pv或自定义回收脚本。
生命周期
pv和pvc遵循以下生命周期:
pv类型
PV支持的后端存储插件:
绑定的概念
用户根据所需存储空间大小和访问模式创建(或在动态部署中已创建)一个 PersistentVolumeClaim。Kubernetes的Master节点循环监控新产生的PVC,找到与之匹配的PV(如果有的话),并把他们绑定在一起。动态配置时,循环会一直将PV与这个PVC绑定,直到PV完全匹配PVC。避免PVC请求和得到的PV不一致。绑定一旦形成,PersistentVolumeClaim绑定就是独有的,不管是使用何种模式绑定的。
如果找不到匹配的volume,用户请求会一直保持未绑定状态。在匹配的volume可用之后,用户请求将会被绑定。比如,一个配置了许多50Gi PV的集群不会匹配到一个要求100Gi的PVC。 只有在100Gi PV被加到集群之后,这个PVC才可以被绑定。
pv属性
pv拥有以下属性:
PV的访问模式
ReadWriteOnce:是最基本的方式,可读可写,但只支持被单个Pod挂载。
ReadOnlyMany:可以以只读的方式被多个Pod挂载。
ReadWriteMany:这种存储可以以读写的方式被多个Pod共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是NFS。在PVC绑定PV时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。
在CLI下,访问方式被简写为:
PV的使用过程
刚才提到说PV与普通Volume的区别是动态绑定,我们来看一下这个过程是怎样的。
这是PV的生命周期,首先是Provision,即创建PV,这里创建PV有两种方式,静态和动态。
静态,是管理员手动创建一堆PV,组成一个PV池,供PVC来绑定。
动态,是指在现有PV不满足PVC的请求时,可以使用存储分类(StorageClass),描述具体过程为:PV先创建分类,PVC请求已创建的某个类(StorageClass)的资源,这样就达到动态配置的效果。即通过一个叫 Storage Class的对象由存储系统根据PVC的要求自动创建。
一个PV创建完后状态会变成Available,等待被PVC绑定。一旦被PVC邦定,PV的状态会变成Bound,就可以被定义了相应PVC的Pod使用。Pod使用完后会释放PV,PV的状态变成Released。变成Released的PV会根据定义的回收策略做相应的回收工作。有三种回收策略,Retain、Delete 和 Recycle。Retain就是保留现场,K8S什么也不做,等待用户手动去处理PV里的数据,处理完后,再手动删除PV。Delete 策略,K8S会自动删除该PV及里面的数据。Recycle方式,K8S会将PV里的数据删除,然后把PV的状态变成Available,又可以被新的PVC绑定使用。
在实际使用场景里,PV的创建和使用通常不是同一个人。这里有一个典型的应用场景:管理员创建一个PV池,开发人员创建Pod和PVC,PVC里定义了Pod所需存储的大小和访问模式,然后PVC会到PV池里自动匹配最合适的PV给Pod使用。
回收策略
现有回收策略有:
目前只有NFS和HostPath支持回收,AWS EBS, GCE PD和Cinder volumes只支持删除。
卷的状态
卷有四种状态,一个卷必属于其中之一:
前面在介绍PV的生命周期时,提到PV的供给有两种方式,静态和动态。动态卷供给是一个 Kubernetes 独有的功能,这一功能允许按需创建存储卷。在没有这种能力之前,集群管理员需要打电话给他们的云或者存储提供者来创建新的存储卷,成功以后再创建 PersistentVolume对象,才能够在 Kubernetes 中使用。动态卷供给能力让管理员不必进行预先创建存储卷,而是随用户需求进行创建。这一特性在 1.2 版本中处于 α 阶段,在版本 1.4 中提升为 β。这一版本提高了动态卷的弹性和可用性。
其中动态方式是通过StorageClass来完成的,这是一种新的存储供应方式。
使用StorageClass有什么好处呢?除了由存储系统动态创建,节省了管理员的时间,还有一个好处是可以封装不同类型的存储供PVC选用。在StorageClass出现以前,PVC绑定一个PV只能根据两个条件,一个是存储的大小,另一个是访问模式。在StorageClass出现后,等于增加了一个绑定维度。
比如这里就有两个StorageClass,它们都是用谷歌的存储系统,但是一个使用的是普通磁盘,我们把这个StorageClass命名为slow。另一个使用的是SSD,我们把它命名为fast。
在PVC里除了常规的大小、访问模式的要求外,还通过annotation指定了Storage Class的名字为fast,这样这个PVC就会绑定一个SSD,而不会绑定一个普通的磁盘。
例:用户在 PersistentVolumeClaim 中可以包含一个 StorageClass 申请动态提供存储。这一任务需要使用 volume.beta.kubernetes.io/storage-class 注解来完成。这一注解的值必须符合管理员配置的 StorageClass 名称。
要选择 “fast” 存储类,用户需要创建如下的 PVC:
上述报文会提供一个等效于 SSD 的持久盘,当这个 PVC 被删除,这个卷也随之销毁。
缺省行为
所有的 PVC 都可以在不使用 StorageClass 注解的情况下,直接使用某个动态存储。把一个StorageClass 对象标记为 “default” 就可以了。StorageClass 用注解storageclass.beta.kubernetes.io/is-default-class 就可以成为缺省存储。
有了缺省的 StorageClass,用户创建 PVC 就不用 storage-class 的注解了,1.4 中新加入的DefaultStorageClass 准入控制器会自动把这个标注指向缺省存储类。
到这里Kubernetes的整个存储系统就都介绍完了。总结一下,两种存储卷:普通Volume 和Persistent Volume。普通Volume在定义Pod的时候直接定义,Persistent Volume通过Persistent Volume Claim来动态绑定。PV可以手动创建,也可以通过StorageClass来动态创建。
先来创建pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: cloudai-code-pv
namespace: cloudai-2
labels:
alicloud-pvname: pv-nas
spec: # 定义pv属性
capacity: # 容量
storage: 1Gi # 存储容量
storageClassName: nas
accessModes: # 访问模式
- ReadWriteMany # ReadWriteOnce:单个节点读写。ReadOnlyMany:多节点只读。ReadWriteMany:多节点读写。挂载时只能使用一种模式。
flexVolume:
driver: "alicloud/nas" # 这里是固定的
options:
server: "xxxxxxxxxxxxxxxxxxxx" # 这里要问阿里云管理者
path: "/k8s/cloudai2/code"
vers: "4.0"
persistentVolumeReclaimPolicy: Recycle # 回收策略 目前NFS和HostPath支持回收Recycle。 AWS、EBS、GCE、PD和Cinder支持删除Delete。Retain保留所有的数据资源
kind: PersistentVolume
apiVersion: v1
metadata:
name: pv001
labels:
release: stable
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: /data/pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002
labels:
release: stable
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /tmp/data
server: 172.17.0.2
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cloudai-code-pvc
namespace: cloudai-2
spec:
storageClassName: nas
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
selector:
matchLabels:
alicloud-pvname: pv-nas
创建了pvc,我们就可以在pod中将pvc作为目录,挂载到pod内部目录了。多个pod可以挂载同一个pvc目录
pod中将目录挂载带pvc
apiVersion: apps/v1
kind: Deployment
metadata:
name: lp-deployment # 部署的名称,部署负责pod的正常运行个数,不负责pod内分布式处理
namespace: cloudai-2
spec: # 包括一些container,storage,volume以及其他Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性。可在特定Kubernetes API找到完整的Kubernetes Pod的属性。
selector:
matchLabels:
app: lp-pod # 根据标签选择选择部署下一层的容器组,复制保持容器组的正常运行个数
replicas: 1 # 选项定义需要的副本个数,此处可以设置很多属性,例如受此Deployment影响的Pod的选择器等
template: # template其实就是对Pod对象的定义。没有为pod指定名称,会自动在deployment的名称后面加随机字符串
metadata:
labels:
app: lp-pod # 为容器组(pod)打标签,
spec:
volumes:
- name: config-path
persistentVolumeClaim:
claimName: cloudai-code-pvc
imagePullSecrets:
- name: hubsecret
containers:
- name: lp-container # 容器名称
image: xxxxxxxxxxx # 基于的镜像名, 根据镜像创建容器
command: ['sleep','30000']
volumeMounts:
- name: config-path
mountPath: /app
参考:https://www.kubernetes.org.cn/930.html