kube-scheduler
是 kubernetes 的调度器,它的主要作用就是根据特定的调度算法和调度策略将 Pod 调度到合适的 Node 节点上去,是一个独立的二进制程序,启动之后会一直监听 API Server,获取到 PodSpec.NodeName 为空的 Pod,对每个 Pod 都会创建一个 binding。
调度器需要充分考虑诸多的因素:
- 公平调度
- 资源高效利用
- QoS
- affinity 和 anti-affinity
- 数据本地化(data locality)
- 内部负载干扰(inter-workload interference)
- deadlines
调度主要分为以下几个部分:
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
过滤有一系列的算法可以使用:
Priorities
优先级是由一系列键值对组成的,键是该优先级的名称,值是它的权重值:
优先级源码函数:
finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + … + (weightn * priorityFuncn)
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}
]
}
自定义调度器主要的功能是查询未调度的 Pod,按照自定义的调度策略选择新的 Node,并将其更新到 Pod 的 Node Binding 上。
比如,一个最简单的调度器可以用 shell 来编写(假设 Kubernetes 监听在 localhost:8001
):
#!/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
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
# 选择使用自定义调度器 my-scheduler
schedulerName: my-scheduler
containers:
- name: nginx
image: nginx:1.10
Pod优先级、抢占功能,在kubernetes v1.8引入,在v1.11版本进入beta状态,并在v1.14版本进入GA阶段,已经是一个成熟的特性了。
顾名思义,Pod优先级、抢占功能,通过将应用细分为不同的优先级,将资源优先提供给高优先级的应用,从而提高了资源可用率,同时保障了高优先级的服务质量。
我们先来简单使用下Pod优先级、抢占功能。
我的集群版本是 v1.14,因此feature PodPriority
默认是开启的。抢占模式的使用分为两步:
查看当前系统默认的优先级:
[root@master101 scripts]# kubectl get priorityclasses.scheduling.k8s.io
NAME VALUE GLOBAL-DEFAULT AGE
system-cluster-critical 2000000000 false 7d
system-node-critical 2000001000 false 7d
在指定 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然后,在 PodSpec 中通过 PriorityClassName 设置 Pod 的优先级:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority