Kubernetes资源调度1--k8s资源调度

研究一下Kubernetes的资源调度器的实现原理以及大神们的改进。

k8s的基本架构如下:

Scheduler调度器做为Kubernetes三大核心组件之一, 承载着整个集群资源的调度功能,其根据特定调度算法和策略,将Pod调度到最优工作节点上,从而更合理与充分的利用集群计算资源。其作用是根据特定的调度算法和策略将Pod调度到指定的计算节点(Node)上,其做为单独的程序运行,启动之后会一直监听API Server,获取PodSpec.NodeName为空的Pod,对每个Pod都会创建一个绑定。默认情况下,k8s的调度器采用扩散策略,将同一集群内部的pod对象调度到不同的Node节点,以保证资源的均衡利用。

首先用户通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到 
Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即 APIServer 用 的方式发送“POST”请求,即创建 Pod 的请求。
 APIServer 接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统 Scheduler 就会定时去监控 APIServer
通过 APIServer 得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知APIServer,APIServer会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node=””)便会立即触发调度流程进行调度。

Kubernetes的调度器以插件化形式实现的,调度器的源码位于kubernetes/plugin/中

kubernetes/pkg/scheduler/ 
`-- scheduler               //调度相关的具体实现              
|-- algorithm
|   |-- predicates       //节点筛选策略
|   `-- priorities         //节点打分策略
|       `-- util
|-- algorithmprovider
|   `-- defaults          //定义默认的调度器  

Scheduler创建和运行的过程, 对应的代码在kubernetes/pkg/scheduler//scheduler.go。调度器是Kubernetes容器集群管理系统中加载并运行的调度程序,负责收集、统计分析容器集群管理系统中所有Node的资源使用情况,然后以此为依据将新建的Pod发送到优先级最高的可用Node上去建立。 

具体的调度过程,一般如下:

  1. 首先,客户端通过API Server的REST API/kubectl/helm创建pod/service/deployment/job等,支持类型主要为JSON/YAML/helm tgz。
  2. 接下来,API Server收到用户请求,存储到相关数据到etcd。
  3. 调度器通过API Server查看未调度(bind)的Pod列表,循环遍历地为每个Pod分配节点,尝试为Pod分配节点。调度过程分为2个阶段:
    • 第一阶段:预选过程,过滤节点,调度器用一组规则过滤掉不符合要求的主机。比如Pod指定了所需要的资源量,那么可用资源比Pod需要的资源量少的主机会被过滤掉。
    • 第二阶段:优选过程,节点优先级打分,对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把容一个Replication Controller的副本分布到不同的主机上,使用最低负载的主机等。
  4. 选择主机:选择打分最高的节点,进行binding操作,结果存储到etcd中。
  5. 所选节点对于的kubelet根据调度结果执行Pod创建操作。

预选过程:  源码位置kubernetes/pkg/scheduler/algorithm/predicates/predicates.go

predicatesOrdering = []string{CheckNodeConditionPred, CheckNodeUnschedulablePred,
		GeneralPred, HostNamePred, PodFitsHostPortsPred,
		MatchNodeSelectorPred, PodFitsResourcesPred, NoDiskConflictPred,
		PodToleratesNodeTaintsPred, PodToleratesNodeNoExecuteTaintsPred, CheckNodeLabelPresencePred,
		CheckServiceAffinityPred, MaxEBSVolumeCountPred, MaxGCEPDVolumeCountPred, MaxCSIVolumeCountPred,
		MaxAzureDiskVolumeCountPred, CheckVolumeBindingPred, NoVolumeZoneConflictPred,
		CheckNodeMemoryPressurePred, CheckNodePIDPressurePred, CheckNodeDiskPressurePred, MatchInterPodAffinityPred}

主要包括主机的目录,端口,cpu内存资源等条件进行物理主机的筛选,筛选出可以进行调度的物理机节点,然后进行优选环节,通过某种策略进行可用节点的评分,最终选出最优节点。

优选过程: 源码位置 kubernetes/pkg/scheduler/algorithm/priorities/

用一组优先级函数处理每一个通过预选的节点,每一个优先级函数会返回一个0-10的分数,分数越高表示节点越优, 同时每一个函数也会对应一个表示权重的值。最终主机的得分用以下公式计算得出:
               finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + … + (weightn * priorityFuncn)

优先级策略:

LeastRequestedPriority:节点的优先级就由节点空闲资源与节点总容量的比值,即由(总容量-节点上Pod的容量总和-新Pod的容量)/总容量)来决定。CPU和内存具有相同权重,资源空闲比越高的节点得分越高。

                 cpu((capacity – sum(requested)) * 10 / capacity) + memory((capacity – sum(requested)) * 10 / capacity) / 2

BalancedResourceAllocation:CPU和内存使用率越接近的节点权重越高,该策略不能单独使用,必须和LeastRequestedPriority组合使用,尽量选择在部署Pod后各项资源更均衡的机器。如果请求的资源(CPU或者内存)大于节点的capacity,那么该节点永远不会被调度到。

InterPodAffinityPriority:通过迭代 weightedPodAffinityTerm 的元素计算和,并且如果对该节点满足相应的PodAffinityTerm,则将 “weight” 加到和中,具有最高和的节点是最优选的。 `

SelectorSpreadPriority:为了更好的容灾,对同属于一个service、replication controller或者replica的多个Pod副本,尽量调度到多个不同的节点上。如果指定了区域,调度器则会尽量把Pod分散在不同区域的不同节点上。当一个Pod的被调度时,会先查找Pod对于的service或者replication controller,然后查找service或replication controller中已存在的Pod,运行Pod越少的节点的得分越高。

NodeAffinityPriority:亲和性机制。Node Selectors(调度时将pod限定在指定节点上),支持多种操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限于对节点labels的精确匹配。另外支持两种类型的选择器,一种是“hard(requiredDuringSchedulingIgnoredDuringExecution)”选择器,它保证所选的主机必须满足所有Pod对主机的规则要求。这种选择器更像是之前的nodeselector,在nodeselector的基础上增加了更合适的表现语法。另一种是“soft(preferresDuringSchedulingIgnoredDuringExecution)”选择器,它作为对调度器的提示,调度器会尽量但不保证满足NodeSelector的所有要求。

NodePreferAvoidPodsPriority(权重1W):如果 节点的 Anotation 没有设置 key-value:scheduler. alpha.kubernetes.io/ preferAvoidPods = "...",则节点对该 policy 的得分就是10分,加上权重10000,那么该node对该policy的得分至少10W分。如果Node的Anotation设置了,scheduler.alpha.kubernetes.io/preferAvoidPods = "..." ,如果该 pod 对应的 Controller 是 ReplicationController 或 ReplicaSet,则该 node 对该 policy 的得分就是0分。

TaintTolerationPriority : 使用 Pod 中 tolerationList 与 节点 Taint 进行匹配,配对成功的项越多,则得分越低。

ImageLocalityPriority:根据Node上是否存在一个pod的容器运行所需镜像大小对优先级打分,分值为0-10。遍历全部Node,如果某个Node上pod容器所需的镜像一个都不存在,分值为0;如果Node上存在Pod容器部分所需镜像,则根据这些镜像的大小来决定分值,镜像越大,分值就越高;如果Node上存在pod所需全部镜像,分值为10。

EqualPriority : EqualPriority 是一个优先级函数,它给予所有节点相等权重。

MostRequestedPriority : 在 ClusterAutoscalerProvider 中,替换 LeastRequestedPriority,给使用多资源的节点,更高的优先级。计算公式为:(cpu(10 sum(requested) / capacity) + memory(10 sum(requested) / capacity)) / 2

自定义调度:

kubernetes/pkg/scheduler/algorithmprovider/defaults/defaults.go

自定义预选以及优选策略,scheduler在启动的时候可以通过 --policy-config-file参数可以指定调度策略文件。

"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}
    ],
"hardPodAffinitySymmetricWeight" : 10 

自定义Priority和Predicate

自定义Priority的实现过程

1.接口位置:kubernetes/pkg/scheduler/algorithm/types.go

// PriorityFunction is a function that computes scores for all nodes.
// DEPRECATED
// Use Map-Reduce pattern for priority functions.
type PriorityFunction func(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error)

2.在kubernetes/pkg/scheduler/algorithm/predicates/predicates.go文件中编写对象是实现上述接口

3.注册自定义过滤函数   kubernetes/pkg/scheduler/algorithmprovider/defaults/defaults.go

4.--policy-config-file将自定义Priority加载进来即可

自定义调度器:

利用Pod的spec.schedulername字段来指定调度器,默认为default

#!/bin/bash
SERVER='localhost:8001'
while true;
do
for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')
;
do
    NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
    NUMNODES=${#NODES[@]}
    CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
    curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"
: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
    echo "Assigned $PODNAME to $CHOSEN"
done
sleep 1
done 

Pod优先级(Priority)和抢占(Preemption)

pod priority指的是Pod的优先级,高优先级的Pod会优先被调度,或者在资源不足低情况牺牲低优先级的Pod,以便于重要的Pod能够得到资源部署.

定义PriorityClass对象:

apiVersion: scheduling.k8s.io/v1alpha1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

在Pod的spec. priorityClassName中指定已定义的PriorityClass名称

apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority

当节点没有足够的资源供调度器调度Pod、导致Pod处于pending时,抢占(preemption)逻辑会被触发。Preemption会尝试从一个节点删除低优先级的Pod,从而释放资源使高优先级的Pod得到节点资源进行部署。

参考地址:

http://dockone.io/article/2885

https://github.com/kubernetes/kubernetes/blob/master

猜你喜欢

转载自blog.csdn.net/u014106644/article/details/84372230
今日推荐