十五,K8S集群调度原理及调度策略
k8s调度器Scheduler
Scheduler工作原理
请求及Scheduler调度步骤:
- 节点预选(Predicate):排除完全不满足条件的节点,如内存大小,端口等条件不满足。
- 节点优先级排序(Priority):根据优先级选出最佳节点
- 节点择优(Select):根据优先级选定节点
-
首先用户通过 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=””)便会立即触发调度流程进行调度。
-
而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点
- 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选
- 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点
- 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择
k8s的调用工作方式
Kubernetes调度器作为集群的大脑,在如何提高集群的资源利用率、保证集群中服务的稳定运行中也会变得越来越重要Kubernetes的资源分为两种属性。
- 可压缩资源(例如CPU循环,Disk I/O带宽)都是可以被限制和被回收的,对于一个Pod来说可以降低这些资源的使用量而不去杀掉Pod。
- 不可压缩资源(例如内存、硬盘空间)一般来说不杀掉Pod就没法回收。未来Kubernetes会加入更多资源,如网络带宽,存储IOPS的支持。
常用预选策略
预选策略 | 作用 |
---|---|
CheckNodeCondition | 检查是否可以在节点报告磁盘、网络不可用或未准备好时将Pod调度其上 |
HostName | 如果Pod对象拥有spec.hostname属性,则检查节点名称字符串是否和该属性值匹配。 |
PodFitsHostPorts | Pod的spec.hostPort属性时,检查端口是否被占用 |
MatchNodeSelector | Pod的spec.nodeSelector属性时,检查节点标签 |
NoDiskConflict | Pod依赖的存储卷在此节点是否可用,默认没有启用 |
PodFitsResources | 检查节点上的资源(CPU、内存)可用性是否满足Pod对象的运行需求。 |
PodToleratesNodeTaints | Pod的spec.tolerations属性,仅关注NoSchedule和NoExecute两个效用标识的污点 |
PodToleratesNodeNoExecuteTaints | Pod的spec.tolerations属性,是否能接纳节点的NoExecute类型污点,默认没有启用 |
CheckNodeLabelPresence | 仅检查节点上指定的所有标签的存在性,默认没有启用 |
CheckServiceAffinity | 将相同Service的Pod对象放置在同一个或同一类节点上以提高效率,默认没有启用 |
MaxEBSVolumeCount | 检查节点已挂载的EBS(亚马逊弹性块存储)存储卷数量是否超过设置的最大值,默认为39 |
MaxGCEPDVolumeCount | 检查节点上已挂载的GCE PD(谷歌云存储) 存储卷数量是否超过最大值,默认为16 |
MaxAzureDiskVolumeCount | 检查节点上已挂载的Azure Disk存储卷数量是否超过最大值,默认为16 |
CheckVolumeBinding | 检查节点上已绑定和未绑定的PVC是否满足需求 |
NoVolumeZoneConflict | 在给定区域zone限制下,检查此节点部署的Pod对象是否存在存储卷冲突 |
CheckNodeMemoryPressure | 检查节点内存压力,如果压力过大,那就不会讲pod调度至此 |
CheckPodePIDPressure | 检查节点PID资源压力 |
CheckNodeDiskPressure | 检查节点磁盘资源压力 |
MatchInterPodAffinity | 检查节点是否满足Pod对象亲和性或反亲和性条件 |
常用优先函数
函数名称 | 详细说明 |
---|---|
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越少的节点的得分越高。本质就是往运行同类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 | 是一个优先级函数,它给予所有节点相等权重。 |
MostRequestedPriority | 在 ClusterAutoscalerProvider 中,替换 LeastRequestedPriority,给使用多资源的节点,更高的优先级。 计算公式为: (cpu(10 sum(requested) / capacity) + memory(10 sum(requested) / capacity)) / 2 默认没有启用 |
节点亲和性调度
节点亲和性规则:硬亲和性 required 、软亲和性 preferred。
- 硬亲和性规则不满足时,Pod会置于Pending状态,软亲和性规则不满足时,会选择一个不匹配的节点
- 当节点标签改变而不再符合此节点亲和性规则时,不会将Pod从该节点移出,仅对新建的Pod对象生效
节点硬亲和性
requiredDuringSchedulingIgnoredDuringExecution
- 方式一:Pod使用 spec.nodeSelector (基于等值关系)
- 方式二:Pod使用 spec.affinity 支持matchExpressions属性 (复杂标签选择机制)
# 调度至 zone = foo 的节点
kubectl label nodes kube-node1 zone=foo
apiVersion: v1
kind: Pod
metadata:
name: with-required-nodeaffinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 定义硬亲和性
nodeSelectorTerms:
- matchExpressions: #集合选择器
- {key: zone,operator: In,values: ["foo"]}
containers:
- name: myapp
image: ikubernetes/myapp:v1
节点软亲和性
preferredDuringSchedulingIgnoredDuringExecution
- 柔性控制逻辑,当条件不满足时,能接受被编排于其他不符合条件的节点之上
- 权重 weight 定义优先级,1-100 值越大优先级越高
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-with-node-affinity
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp-pod
labels:
app: myapp
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #节点软亲和性
- weight: 60
preference:
matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
- weight: 30
preference:
matchExpressions:
- {key: ssd, operator: Exists, values: []}
containers:
- name: myapp
image: ikubernetes/myapp:v1
Pod资源亲和调度
- Pod对象间
亲和性
,将一些Pod对象组织在相近的位置(同一节点、机架、区域、地区) - Pod对象间
反亲和性
,将一些Pod在运行位置上隔开
调度器将第一个Pod放置于任何位置,然后与其有亲和或反亲和关系的Pod据此动态完成位置编排
# 基于MatchInterPodAffinity
预选策略完成节点预选,基于InterPodAffinityPriority
优选函数进行各节点的优选级评估
位置拓扑,定义"同一位置"
Pod硬亲和调度
requiredDuringSchedulingIgnoredDuringExecution
Pod亲和性描述一个Pod与具有某特征的现存Pod运行位置的依赖关系;即需要事先存在被依赖的Pod对象
# 被依赖Pod
kubectl run tomcat -l app=tomcat --image tomcat:alpine
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.topologyKey
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和调度
- labelSelector:
matchExpressions: #集合选择器
- {key: app, operator: In, values: ["tomcat"]} # 选择被依赖Pod
# 上面意思是,当前pod要跟标签为app值为tomcat的pod在一起
topologyKey: kubernetes.io/hostname # 根据挑选出的Pod所有节点的hostname作为同一位置的判定
containers:
- name: myapp
image: ikubernetes/myapp:v1
Pod软亲和调度
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-with-preferred-pod-affinity
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
name: myapp
labels:
app: myapp
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["cache"]}
topologyKey: zone
- weight: 20
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["db"]}
topologyKey: zone
containers:
- name: myapp
image: ikubernetes/myapp:v1
Pod反亲和调度
Pod反亲和调度用于分散同一类应用,调度至不同的区域、机架或节点等
将 spec.affinity.podAffinity
替换为 spec.affinity.podAntiAffinity
反亲和调度也分为柔性约束和强制约束
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: fronted
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: zone
污点和容忍度
污点 taints 是定义在节点
上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度容忍度 tolerations 是定义在Pod
上的键值属性数据,用于配置可容忍的污点,且调度器将Pod调度至其能容忍该节点污点的节点上或没有污点的节点上
使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数完成该机制
- 节点亲和性使得Pod对象被吸引到一类特定的节点 (nodeSelector和affinity)
- 污点提供让节点排斥特定Pod对象的能力
定义污点和容忍度
污点定义于nodes.spec.taints
容忍度定义于pods.spec.tolerations
语法: key=value:effect
effect定义排斥等级:
NoSchedule
,不能容忍,但仅影响调度过程,已调度上去的pod不受影响,仅对新增加的pod生效。PreferNoSchedule
,柔性约束,节点现存Pod不受影响,如果实在是没有符合的节点,也可以调度上来NoExecute
,不能容忍,当污点变动时,Pod对象会被驱逐
在Pod上定义容忍度时:
- 等值比较 容忍度与污点在key、value、effect三者完全匹配
- 存在性判断 key、effect完全匹配,value使用空值
一个节点可配置多个污点,一个Pod也可有多个容忍度
管理节点的污点
同一个键值数据,effect不同,也属于不同的污点
给节点添加污点:
kubectl taint node <node-name> <key>=<value>:<effect>
kubectl taint node node2 node-type=production:NoShedule #举例
查看节点污点:
kubectl get nodes <nodename> -o go-template={{.spec.taints}}
删除节点污点:
kubectl taint node <node-name> <key>[:<effect>]-
kubectl patch nodes <node-name> -p '{"spec":{"taints":[]}}'
kubectl taint node kube-node1 node-type=production:NoSchedule
kubectl get nodes kube-node1 -o go-template={{.spec.taints}}
# 删除key为node-type,effect为NoSchedule的污点
kubectl taint node kube-node1 node-type:NoSchedule-
# 删除key为node-type的所有污点
kubectl taint node kube-node1 node-type-
# 删除所有污点
kubectl patch nodes kube-node1 -p '{"spec":{"taints":[]}}'
给Pod对象容忍度
spec.tolerations
字段添加
tolerationSeconds
用于定义延迟驱逐Pod的时长
# 等值判断
tolerations:
- key: "key1"
operator: "Equal" #判断条件为Equal
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
# 存在性判断
tolerations:
- key: "key1"
operator: "Exists" #存在性判断,只要污点键存在,就可以匹配
effect: "NoExecute"
tolerationSeconds: 3600
apiVersion: v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "production":
effect: "NoExecute"
tolerationSeconds: 3600
问题节点标识
自动为节点添加污点信息,使用NoExecute效用标识,会驱逐现有Pod
K8s核心组件通常都容忍此类污点
node.kubernetes.io/not-ready 节点进入NotReady状态时自动添加
node.alpha.kubernetes.io/unreachable 节点进入NotReachable状态时自动添加
node.kubernetes.io/out-of-disk 节点进入OutOfDisk状态时自动添加
node.kubernetes.io/memory-pressure 节点内存资源面临压力
node.kubernetes.io/disk-pressure 节点磁盘面临压力
node.kubernetes.io/network-unavailable 节点网络不可用
node.cloudprovider.kubernetes.io/uninitialized kubelet由外部云环境程序启动时,自动添加,待到去控制器初始化此节点时再将其删除
Pod优选级和抢占式调度
优选级,Pod对象的重要程度
优选级会影响节点上Pod的调度顺序和驱逐次序
一个Pod对象无法被调度时,调度器会尝试抢占(驱逐)较低优先级的Pod对象,以便可以调度当前Pod
Pod优选级和抢占机制默认处于禁用状态
启用:同时为kube-apiserver、kube-scheduler、kubelet程序的 --feature-gates 添加 PodPriority=true
使用:
事先创建优先级类别,并在创建Pod资源时通过 priorityClassName属性指定所属的优选级类别