k8s基础(13)之scheduler调度器
kube-scheduler是kubernetes系统的核心组件质疑,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将Pod调度到最优的一个工作节点上面去,从而更加的合理、更加充分的利用集群的资源。
调度器流程
scheduler 是Kubernetes的调度器,它的主要作用就是根据特定的调度算法和调度策略将Pod调度到合适的Node节点上去,是一个独立的二进制程序,当我们通过API 或者kubectl工具发送创建Pod的指令后,apiserver会将数据写进ETCD中,同时scheduler监听API Server,通过scheduler内部的匹配规则(预选过程)将合适的Node节点计算出,并进行打分(优选过程)。最后返回给apiserver,并由apiserver 通知kubelet进行创建并将相关信息写进ETCD
调度主要分为以下几个部分
- 预选过程 过滤不满足条件的节点,这个过程称为
Predicates
- 优选过程 对通过的节点按照优先级排序,称之为
Priorities
- 最后从中选择优先级最高的节点
Predicates
阶段首先会遍历全部节点,过滤不满足条件的节点,属于强制性规则,这一阶段输出的所有满足要求的Node将被记录并作为第二阶段的输入,如果所有的节点都不满足条件,那么Pod将会一直处于Pending状态,直到有节点满足条件,在这期间调度器会不断的重试
Priorities
阶段即再次对节点进行筛选,如果有多个节点都满足条件的话,那么系统会根据节点的优先级(priorites)大小对节点进行排序,最后选择优先级最高的节点来部署Pod应用
更详细的流程是这样的:
1.首先,客户端通过 API Server 的 REST API 或者 kubectl 工具创建 Pod 资源 2.API Server 收到用户请求后,存储相关数据到 etcd 数据库中 3.调度器监听 API Server 查看为调度(bind)的 Pod 列表,循环遍历地为每个 Pod 尝试分配节点,这个分配过程就是我们上面提到的两个阶段:
- 预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉
- 优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本分布到不同的主机上,使用最低负载的主机等等策略
4.经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中 5.最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作
其中Predicates过滤有一系列的算法可以使用,我们这里简单列举几个:
- PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源
- PodFitsHost:如果 Pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
- PodFitsHostPorts:节点上已经使用的 port 是否和 Pod 申请的 port 冲突
- PodSelectorMatches:过滤掉和 Pod 指定的 label 不匹配的节点
- NoDiskConflict:已经 mount 的 volume 和 Pod 指定的 volume 不冲突,除非它们都是只读的
- CheckNodeDiskPressure:检查节点磁盘空间是否符合要求
- CheckNodeMemoryPressure:检查节点内存是否够用
除了这些过滤算法之外,还有一些其他的算法,更多更详细的我们可以查看源码文件:k8s.io/kubernetes/pkg/scheduler/algorithm/predicates/predicates.go。
而Priorities优先级是由一系列键值对组成的,键是该优先级的名称,值是它的权重值,同样,我们这里给大家列举几个具有代表性的选项:
- LeastRequestedPriority:通过计算 CPU 和内存的使用率来决定权重,使用率越低权重越高,当然正常肯定也是资源是使用率越低权重越高,能给别的 Pod 运行的可能性就越大
- SelectorSpreadPriority:为了更好的高可用,对同属于一个 Deployment 或者 RC 下面的多个 Pod 副本,尽量调度到多个不同的节点上,当一个 Pod 被调度的时候,会先去查找该 Pod 对应的 controller,然后查看该 controller 下面的已存在的 Pod,运行 Pod 越少的节点权重越高
- ImageLocalityPriority:就是如果在某个节点上已经有要使用的镜像节点了,镜像总大小值越大,权重就越高
- NodeAffinityPriority:这个就是根据节点的亲和性来计算一个权重值,后面我们会详细讲解亲和性的使用方法
自定义调度
上面就是 kube-scheduler 默认调度的基本流程,除了使用默认的调度器之外,我们也可以自定义调度策略。
调度器扩展
kube-scheduler在启动的时候可以通过
--policy-config-file
参数来指定调度策略文件,我们可以根据我们自己的需要来组装Predicates和Priority函数。选择不同的过滤函数和优先级函数、控制优先级函数的权重、调整过滤函数的顺序都会影响调度过程。
下面是官方的 Policy 文件示例:
{
"kind" : "Policy",
"apiVersion" : "v1",
"predicates" : [
{"name" : "PodFitsHostPorts"},
{"name" : "PodFitsResources"},
{"name" : "NoDiskConflict"},
{"name" : "NoVolumeZoneConflict"},
{"name" : "MatchNodeSelector"},
{"name" : "HostName"}
],
"priorities" : [
{"name" : "LeastRequestedPriority", "weight" : 1},
{"name" : "BalancedResourceAllocation", "weight" : 1},
{"name" : "ServiceSpreadingPriority", "weight" : 1},
{"name" : "EqualPriority", "weight" : 1}
]
}
多调度器
如果默认的调度器不满足要求,还可以部署自定义的调度器。并且,在整个集群中还可以同时运行多个调度器实例,通过 podSpec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
schedulerName: my-scheduler # 选择使用自定义调度器 my-scheduler
containers:
- name: nginx
image: nginx:1.10
要开发我们自己的调度器也是比较容易的,比如我们这里的 my-scheduler:
- 首先需要通过指定的 API 获取节点和 Pod
- 然后选择phase=Pending和schedulerName=my-scheduler的pod
- 计算每个 Pod 需要放置的位置之后,调度程序将创建一个Binding对象
- 然后根据我们自定义的调度器的算法计算出最适合的目标节点
优先级调度
与前面所讲的调度优选策略中的优先级(Priorities)不同,前面所讲的优先级指的是节点优先级,而我们这里所说的优先级 pod priority 指的是 Pod 的优先级,高优先级的 Pod 会优先被调度,或者在资源不足低情况牺牲低优先级的 Pod,以便于重要的 Pod 能够得到资源部署。
要定义 Pod 优先级,就需要先定义PriorityClass对象,该对象没有 Namespace 的限制:
apiVersion: v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
其中:
- value为 32 位整数的优先级,该值越大,优先级越高
- globalDefault用于未配置 PriorityClassName 的 Pod,整个集群中应该只有一个PriorityClass将其设置为 true
然后通过在 Pod 的spec.priorityClassName中指定已定义的PriorityClass名称即可:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
另外一个值得注意的是当节点没有足够的资源供调度器调度 Pod,导致 Pod 处于 pending 时,抢占(preemption)逻辑就会被触发。Preemption会尝试从一个节点删除低优先级的 Pod,从而释放资源使高优先级的 Pod 得到节点资源进行部署。