8.1 K8s调度策略之 Scheduler
调度器调度步骤:预选–>优选–>选定(如果优选得分相同,随机选定),如果某个pod没有被成功调度,则会处于Pending状态。
- CheckNodeCondition
- GeneralPredicates
- HostName:检查Pod对象是否定义了pod.spec.hostname
- PodfitsHostPorts:检查Pod的pod.spec.containers.ports.hostPort定义
- MatchNodeSelector:检查pods.spec.nodeSelector
- PodfitsResources:检查节点的资源是否能够满足Pod的资源需求
- NoDiskConflict:检查节点上是否有合适的存储卷可以满足Pod依赖的存储卷。默认未启用
- PodToleratesNodeTaints:检查Pods上的pod.spec.tolerations定义的可容忍污点是否完全包含节点的上的污点taints
- PodToleratesNodeNoExecuteTaints:驱离性污点,默认未启用
- CheckNodeLabelPresence:检查节点标签存在性,默认未启用
- CheckServiceAffinity:根据当前Pod对象所属Service已有的其他Pod对象所在节点进行调度,尽可能使属于同一Servie的多个Pod使用同一个节点。默认未启用
- CheckVolumBinding:
- NoVolumeZoneConflict:
- CheckNodeMemeoryPressure:
- CheckNodePIDPressure:
- CheckNodeDiskPressure:
- MatchInterPodAffinity:
所有预选策略都要评估,一票否决的方式。预选之后,会选择出有资格被pod运行的所有node。
LeastRequested:最少请求,资源使用率低的胜出
(cpu((capacity-sum(requested))*10/capacity)+memory((capacity-sum(requested))*10/capacity))/2
MostRequested:最多请求,资源使用率高的胜出。默认未启用
BalancedResourceAllocation:CPU和内存资源被占用率相近的胜出;
NodePreferAvoidPods:根据节点注解信息“scheduler.alpha.kubernetes.io/preferAvoidPods”是否存在,没有此信息,得分为10,权重10000。
TaintToleration:将Pod对象的spec.tolerations列表项与节点的taints列表项进行匹配度检查,匹配条目越多,得分越低;目的:查找污点少的节点
SeletorSpreading:目的:将Selector选择的Pod分散开。
InterPodAffinity:Pod亲和性项目多的胜出
NodeAffinity:Node亲和性项目多的胜出
NodeLabel:节点标签:根据节点是否应用特定标签名来评估得分,只看key,不看value。默认未启用
ImageLocality:根据满足当前Pod对象需求的已有镜像的体积大小之和,含镜像体积越多,得分越高。默认未启用
**经过优选策略评分后,根据得分选择出得分最高的,最符合条件的node(如果多个node得分相同且最高,则随机选择其中一个),将该node与该pod进行绑定。**之后由kubelet进行pod的部署。
标签选择器有两种:
等值关系:=或==、!=(不具有key或具有key但value不同)
集合关系:KEY in (value1,value2)、KEY notin (value1,value2)、KEY表示存在某个键、!KEY表示不存在某个键
同时指定多个标签使用逗号分隔,多个标签关系为逻辑与:release=canary,app=tomcat
许多资源支持内嵌字段定义其使用的标签选择器:
- matchLabels:直接给定key=value
- matchExpressions:基于给定的表达式来定义使用的标签选择器,格式为:{key:“KEY”,operator:“OPERATOR”,values:[value1,value2…]}
- OPERATOR为In、NotIn:values字段的值必须为非空列表
- OPERATOR为Exists、NotExists:values字段的值必须为空列表[]
[root@k8smaster ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8smaster Ready master 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8smaster,node-role.kubernetes.io/master=
k8snode01 Ready <none> 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8snode01
k8snode02 Ready <none> 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8snode02
[root@k8smaster ~]# kubectl label nodes k8snode01 disktype=hd
node/k8snode01 labeled
[root@k8smaster ~]# kubectl label nodes k8snode02 disktype=ssd
node/k8snode02 labeled
[root@k8smaster ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8smaster Ready master 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8smaster,node-role.kubernetes.io/master=
k8snode01 Ready <none> 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=hd,kubernetes.io/hostname=k8snode01
k8snode02 Ready <none> 48d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=k8snode02
用于选择满足条件的节点。
[root@k8smaster schedule]# cat pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
nodeSelector:
disktype: harddisk
node只有affinity,没有antiaffinity,用于选择满足条件的节点,类似于nodeSelector,但是比nodeSelector更加灵活。
说明:
当nodeAffinity和nodeSelector同时出现时,要二者同时满足条件才能被调度。
- 硬亲和(required):
[root@k8smaster schedule]# cat pod-nodeaffinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity: #没有node反亲和性
requiredDuringSchedulingIgnoredDuringExecution: #required表示硬亲和性,表示必须匹配
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
- 软亲和(preferred)
[root@k8smaster schedule]# cat pod-nodeaffinity-demo-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-node-affinity-demo-2
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #preferred倾向,表示软亲和性,尽量满足,可不满足匹配,优先运行在满足条件的节点上。
- weight: 60 #多个策略之间的比重。最优选的节点是权重总和最大的节点
preference:
matchExpressions:
- key: zone
operator: In
values:
- foo
- bar
Pod亲和性,第一个Pod随机调度,后面的Pod都以此为标准,运行在相近节点上,不需要考虑节点标签配置。但仍需要有一个逻辑条件预先设置好哪些节点是属于相近节点,同一位置,让pod去判断。
硬亲和:
[root@k8smaster schedule]# cat pod-required-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: frontend
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: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
- labelSelector: #与哪个pod进行亲和
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname #拓扑键,表示节点标签kubernetes.io/hostname相同的为相近节点。本例中的含义是:具有app=myapp标签的pod要调度到同一台主机上(topologyKey: kubernetes.io/hostname)
软亲和:
[root@k8smaster schedule]# cat pod-required-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: frontend
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: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #软亲和
- weight: 60
podAffinityTerm:
labelSelector: #与哪个pod进行亲和
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname #拓扑键,表示节点标签kubernetes.io/hostname相同的为相近节点。
调度器更倾向于将 pod 调度到满足该字段指定的反亲和性表达式的节点,但它可能会选择违反一个或多个表达式的节点。最优选的节点是权重总和最大的节点,即对于满足所有调度要求(比如资源请求、requiredDuringScheduling等)的每个节点,通过遍历podAffinityTerm元素来计算权重总和,如果节点满足podAffinityTerm,则在权重总和中添加该podAffinityTerm的“权重”; 具有最高和的节点是最优选的。
定义与podAffinity定义一致,只是改为podAntiAffinity即可。
硬亲和:
[root@k8smaster schedule]# cat pod-required-anti-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: frontend
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: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: zone
preferredDuringSchedulingIgnoredDuringExecution: #软亲和,多个podAffinityTerm进行评估,权重之和最大的节点为最优
- weight: 60 #podAffinityTerm的权重,取值1-100,只在preferredDuringSchedulingIgnoredDuringExecution中存在
podAffinityTerm:
labelSelector:
matchExpressions:
- {key: zone, operator: In, values: ["zone1"]}
topologyKey: kubernetes.io/hostname
污点taint:定义在node上的键值属性数据(键值属性数据:标签、注解、污点),实现让node拒绝pod。定义在node.spec.taints中
容忍度:定义在pod上的键值属性数据,是个列表,表示可以容忍的污点的列表
node.spec.taints.effect 定义node对pod的排斥效果:
NoSchedule:仅影响调度过程,对现存pod不产生影响,不能容忍就不能调度过来。
除非Pod配置了容忍,否则不允许Pod被调度到该节点上。需要注意的是:kubernetes允许通过Kubelet命令绕过调度器(静态Pod),直接在这些节点上启动Pod,即使这些Pod没有配置容忍。Kubernetes允许已经运行的Pod继续运行,即使这些Pod不满足容忍条件。 (仅影响调度过程,后加的污点对现存的Pod不产生影响。)
NoExecute:既影响调度过程,也影响现存的Pod对象。不容忍污点的Pod对象将被驱逐。
对已经运行的Pod,如果node后期又添加了一些污点,当这些Pod不容忍节点上的污点时,强制驱逐到其他满足容忍条件的节点上。如果配置了NoExecute,那么可以选择是否配置tolerationSeconds参数。也就是说Pod会在tolerationSeconds时间过后进行被驱逐。 (不仅不会调度,还会驱逐Node上已有的Pod)
PreferNoSchedule: 仅影响调度过程,不能容忍就不能调度过来,但是实在没办法也是能调度过来的。对节点新加了污点,那么对节点上现存的pod没有影响。
这个配置相比NoSchedule来说,在功能作用上是一样的,只不过不是强制执行的,只是参考执行。 (尽量不去满足不合要求的 pod 调度到 node 上来)
pod上定义容忍度的两种方式:
- 等值比较(Equal):表示key和value都要一致。默认值
- 存在性判断(Exists):key和effect必须匹配,value可以为空。
匹配逻辑:
pod必须容忍Node上的所有污点,如果有不被容忍的污点,要看该污点的effect。如果effect为PreferNoSchedule,也可以被调度,如果为NoSchedule或NoExecute,则不能被调度。总之, k8s 在处理 taints and tolerations 时类似一个过滤器:
- 对比一个 node 上所有的
taints - 忽略掉和 pod 中
toleration匹配的taints - 遗留下来未被忽略掉的所有
taints将对 pod 产生effect
给Node打上污点:
Usage: kubectl taint node Node_NAME KEY_1=VAL_1:EFFECT_1 … KEY_N=VAL_N:EFFECT_N
将Node上的污点删除:在key或key:effect后加一个减号
Usage: kubectl taint node Node_NAME KEY-|key:effect-
[root@k8smaster ~]# kubectl taint node k8snode01 node-type=production:NoSchedule
node/k8snode01 tainted
#表示只有容忍node-type=production:NoSchedule这个属性的pod才能调度到k8snode01上
[root@k8smaster ~]# kubectl describe node k8snode01
Name: k8snode01
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
disktype=hd
kubernetes.io/hostname=k8snode01
Annotations: flannel.alpha.coreos.com/backend-data={"VtepMAC":"1e:98:ec:57:26:42"}
flannel.alpha.coreos.com/backend-type=vxlan
flannel.alpha.coreos.com/kube-subnet-manager=true
flannel.alpha.coreos.com/public-ip=192.168.5.37
kubeadm.alpha.kubernetes.io/cri-socket=/var/run/dockershim.sock
node.alpha.kubernetes.io/ttl=0
volumes.kubernetes.io/controller-managed-attach-detach=true
CreationTimestamp: Fri, 27 Jul 2018 17:36:30 +0800
Taints: node-type=production:NoSchedule
.....
定义Pod的容忍度tolerations:
[root@k8smaster schedule]# cat deploy-demo.yaml
apiVersion: apps/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:v2
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-type"
operator: "Exists" #如果操作符为Exists,那么value属性可省略,表示通配任何value;如果为Equal,表示key和value都要一致;如果不指定operator,则默认为Equal;
value: "" #operator为Exists时,value为"",表示通配所有值。
effect: "" #如果为空,表示匹配所有的effect,即NoSchedule+PreferNoSchedule+NoExecute
#tolerationSeconds: 60 #只有effect为NoExecute才能使用,表示pod运行多久后后被驱逐