优先级和抢占机制,解决的是Pod调度失败时该怎么办的问题。
正常情况下,当一个Pod调度失败后,它就会被暂时“搁置”起来,直到Pod被更新,或者集群状态发生变化,调度器才会对这个Pod进行重新调度。
前一篇文章提到过 Kubernetes调度器 维护着一个优先级队列,当Pod拥有了优先级之后,高优先级的Pod就可能会比低优先级的Pod提前出队。
优先级的定义需要用到 PriorityClass,具体的用法见下图:
说明:
globalDefault
被设置为true,那就意味着这个PriorityClass的值会成为系统的默认值globalDefault
被设置为false,那么对于没有声明使用该PriorityClass的Pod来说,其优先级是0PriorityAdmissionController
就会自动为这个Pod设置 spec.priority
字段preemptionPolicy
为NeverKubernetes调度器为了实现抢占算法用到了两个队列:
当某个Pod调度失败后,会被放进unschedulableQ队列,这次失败事件会触发抢占流程,可以分为两大步骤:
Predicates
(不清楚是啥的出门右转看 Kubernetes调度器)的失败是不能通过抢占来解决的nominatedNodeName
字段设置为被抢占Pod所在的Node的名字,并不会立刻被调度到被抢占的Node上,说白了,就是把抢占者交给下一个调度周期再处理InterPodAntiAffinity
规则约束容器化一个应用比较麻烦的是对其“状态”的管理,接下来,我们一起看看最常见的存储状态。
说明:
PVC
描述的是Pod想要使用的持久化存储的属性,比如存储的大小、读写权限等PV
描述的是一个具体的Volume的属性,比如Volume的类型、挂载目录、远程存储服务器地址等StorageClass
的作用是充当PV的模板,并且只有StorageClass相同的PV和PVC才可以绑定在一起;StorageClass的另一个重要作用是指定PV的Provisioner(存储插件),如果存储插件支持 Dynamic Provisioning
,那么Kubernetes就可以自动创建需要的PV为了进行以下DEMO,可以使用 RAM Disk
来模拟本地磁盘:
mkdir -p /mnt/disks/vol
mount -t tmpfs -o size=100m vol /mnt/disks/vol
看完上面的过程,可以发现 local PV 并非通过 hostPath
加 nodeAffinity
来实现的。
持久化Volume分为两阶段,是靠独立于kubelet主控制循环(Kubelet Sync Loop)之外的两个控制循环来实现的:
Attach
,为宿主机挂载远程磁盘
Volume Controller
负责维护的,这个控制循环叫作 AttachDetachController
,运行在Master节点上的Mount
,将磁盘设备格式化并挂载到Volume宿主机目录
VolumeManagerReconciler
,是kubelet组件的一部分,是一个独立于kubelet主循环的Goroutine经过上面两阶段处理,就得到了一个可以持久化的Volume宿主机目录。接下来,kubelet只要把这个Volume目录通过CRI里的Mounts参数传递给Docker,然后就可以为Pod里的容器挂载这个持久化的Volume了,相当于执行了如下所示的命令:
docker run -v <宿主机挂载目录>:<容器内的目标目录> xxx镜像 ...
仅仅依靠Attach阶段和Mount阶段是无法实现 Dynamic Provisioning
这类相对复杂的功能,这个时候就需要有 Container Storage Interface(CSI)这样更完善、更编程友好的插件方式。
简单来说,CSI插件体系的核心设计思想,就是把Provision阶段以及Kubernetes的一部分存储管理功能从主干代码里剥离出来,做成几个单独的组件,这些组件会通过 Watch API
监听Kubernetes里与存储相关的事件变化,比如PVC的创建,来执行具体的存储管理动作。
上图最右侧的部分,就是需要我们编写代码来实现的CSI插件。一个CSI插件只有一个二进制文件,它会以 gRPC
的方式对外提供三个服务:
CSI Volume
(对应Kubernetes里的PV)的管理接口,比如:
除了上面三个服务,这套存储插件体系多了三个独立的 External Components,对应的正是从Kubernetes项目里面剥离出来的那部分存储管理功能:
新版本由 Node Driver Registrar 替代
VolumeAttachment
对象的变化注意,Volume的Mount阶段并不属于 External Components 的职责。
综上所述,CSI插件体系包含Provision、Attach和Mount三个阶段。其中,Provision相当于创建磁盘,至于Attach和Mount与原来的区别如下:
VolumeManagerReconciler
控制循环需要进行Mount操作时,它实际上会直接向 CSI Node 服务发起调用 NodePublishVolume
方法的请求AttachDetachController
需要进行Attach操作时,它实际上会创建一个VolumeAttachment对象,从而触发 External Attacher 调用 CSI Controller 服务的 ControllerPublishVolume
方法DaemonSet
在每个Node上都启动一个CSI插件,为kubelet提供一对一的 CSI Node 服务
sidecar
的方式运行StatefulSet
在任意一个Node上再启动一个CSI插件,为 External Components 提供 CSI Controller 服务
CSI插件相对来说比较复杂,可能需要多看几遍才能消化理解!