概述
k8s里对pod的调度的过程简单说就是:当Scheduler
通过API server
的watch接口监听到新建Pod副本的信息后,它会检查所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上,目标Node上的kubelet
服务进程接管后继工作,负责Pod生命周期的后半生。同时Scheduler
并将绑定信息传给API server
写入etcd中。
Kubernetes Scheduler
提供的调度流程分三步:
- 预选策略(predicate):遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择。
- 优选策略(priority):在选择出符合要求的候选节点中,采用优选规则计算出每个节点的积分,最后选择得分最高的。
- 选定(select):如果最高得分有好几个节点,select就会从中随机选择一个节点。
而具体方案有以下几种:
pod对pod:podAffinity和podantiAffinity
pod对node:nodeName、nodeSelector、Taint和Toleration、nodeAffinity和nodeantiAffinity、DeamonSet
强指定nodeName
先看一下当前node的情况:
最简单无脑的把pod分配到node上的方法就是nodeName
,这个方法是不走schedule分配的,而是直接到对应的node上由kubelet创建pod,举个例子,写一个pod.yaml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15apiVersion: v1
kind: Pod
metadata:
labels:
run: pod-manual-schedule
namespace: default
name: pod-manual-schedule
spec:
nodeName: "node2" #这里填写的是NAME段,指定这个pod去node2节点上
#nodeSelector:
# cloudnil.com/role: dev #指定调度节点为带有label标记为:cloudnil.com/role=dev的node节点
#schedulerName: default-scheduler #调度器可以自己确定,一般来说这一行不写,即默认的那个调度器
containers:
- name: my-pod
image: nginx:1.11.9
kubectl apply -f pod.yaml
之后,查看一下效果,果然分配到了node2上:
kubectl delete -f 1.yaml
删掉该pod。
跟nodeName
差不多功效的是nodeSelector
,只不过后者靠label而不是名字来给pod指定node。
但是要注意!!!如果在创建deployment/pod
的时候指定了nodeName
,那么k8s是不会做调度的,也就是说不管该node上的剩余资源是否满足request,都会硬往里塞。那么如果node上的剩余资源不足,就会发现pod处于outofcpu/outofmem
。
亲和性nodeaffinity
通过nodeAffinity
调度pod就多了一丝动态选择的味道,不像nodeName
那么死板了。首先kubectl label nodes node3 has-eip=yes
先给node3打上has-eip=yes
这样的label,然后写一个pod.yaml如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21apiVrsion: v1
kind: Pod
metadata:
labels:
run: node-affinity
namespace: default
name: node-affinity
spec:
containers:
- name: my-pod
image: nginx:1.11.9
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: has-eip
operator: In
values:
- "yes"
kubectl apply -f pod.yaml
之后,查看一下效果,果然分配到了node3上:
如果说就是不信邪,要把nodeAffinity和nodeName/nodeSelector一起用,而且还不是指向同一个node,那么这个pod就会调度失败,卡在Predicate MatchNodeSelector failed
这个阶段。
谈谈topologyKey
众所周知,为了照顾pod之间的超亲密关系,k8s还有一个podAffinity,比如一个yaml如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22apiVersion: v1
kind: Pod
metadata:
labels:
run: pod-affinity
namespace: default
name: pod-affinity
spec:
containers:
- name: my-pod
image: nginx:1.11.9
imagePullPolicy: IfNotPresent
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: run
operator: In
values:
- "node-affinity"
topologyKey: kubernetes.io/hostname
这里面有一个元素叫topologyKey
,对它的讲解并不多,很多资料也是一句带过。我这里打算研究一番:
topologyKey是拓扑域
,即一个范围。这个范围可大可小,可以是地区、机房也可以是一个node。它实际对应的还是一个node的标签,也就是说,其实topologyKey就是用于筛选Node的。
结合我上面那个yaml,这个yaml意思就是要在default
这个命名空间里创建一个叫pod-affinity
的pod,它要求必须与run:node-affinity
这样的pod在同一个node里,而且这个node还要有kubernetes.io/hostname
这样的label。
那么如何判断一个node的label里有没有kubernetes.io/hostname
?使用kubectl get nodes -o wide --show-labels
看一下:
可见图里三个node都有kubernetes.io/hostname
,但是如何结合拓扑域呢?举个例子:Pod1在k8s.io/hostname=node1
的Node上,Pod2在k8s.io/hostname=node2
的Node上,Pod3在k8s.io/hostname=node1
的Node上,则Pod2和Pod1、Pod3不在同一个拓扑域,而Pod1和Pod3在同一个拓扑域。
注意!原则上,topologyKey
可以是任何合法的标签Key。但是出于性能和安全原因,对topologyKey
有一些限制:
- 于亲和性和
requiredDuringSchedulingIgnoredDuringExecution
的Pod反亲和性,topologyKey
不能为空。 - 对于
requiredDuringSchedulingIgnoredDuringExecution
的Pod反亲和性,引入LimitPodHardAntiAffinityTopology
准入控制器来限制topologyKey
只能是kubernetes.io/hostname
。如果要使用自定义拓扑域,则可以修改准入控制器,或者直接禁用它。 - 对于
preferredDuringSchedulingIgnoredDuringExecution
的Pod反亲和性,空的topologyKey
表示所有拓扑域。截止v1.12版本,所有拓扑域还只能是kubernetes.io/hostname
、failure-domain.beta.kubernetes.io/zone
和failure-domain.beta.kubernetes.io/region
的组合。 - 除上述情况外,
topologyKey
可以是任何合法的标签key。
timeAdded的问题
在Node的taint
的配置里,可能会有timeAdded
这样的字眼:
1
2
3
4
5taints:
- effect: NoSchedule
key: dedicated
timeAdded: null
value: master
这个timeAdded
是啥?很少有人介绍,就有人误认为效果是类似toleration
里的tolerationSeconds
,其实是不对的。
timeadded
只有在effect:NoExecute
里才有具体的值,其余时候都是null
,这个字段的设计用途是:“考虑到不同的node可能时间不一致,如果k8s组件出现时间误差的时候,通过timeAdded
+ pod.tolerationSeconds
小于等于time.Now
来保证taint能成功的把pod转移到其他的node去”。
不过这个字段据说要慢慢被淘汰掉了,所以可以不用太关注它。
参考资料
http://bazingafeng.com/2019/03/31/k8s-affinity-topologykey/
https://github.com/kubernetes/kubernetes/issues/42394
https://segmentfault.com/a/1190000018446858