Scheduler负责调度Pod到集群内的节点,监听APIServer,查询还未分配Node的Pod,然后根据调度策略为这些Pod分配节点(更新Pod的NodeName字段)。
调度器需要充分考虑诸多因素:
- 公平调度
- 资源高效利用
- Qos
- affinity和anti-affinity
- 数据本地化
调度
调度分为两个阶段,两个阶段都是可配置的插件式的,挨个过插件
- Predicate,过滤不符合条件的节点
- Priority,节点打分,选择分值最高的节点
除了内置的插件,也支持编写自己的策略,在Pod的spec中指定自己的调度器。
资源需求
cpu/memory
在Pod的spec中指定container某个容器的资源request和limit。满足request量,就能调度上去。
- CPU
- requests 判断当前节点上在运行的Pod的CPU requests的总和,再加上当前调度的Pod的request,是否超过CPU的可分配资源
- limits 配置cgroup以限制资源上限
- 内存
- requests 判断节点剩余内存是否满足Pod的内存请求量
- limits 配置cgroup以限制资源上限
使用LimitRange对象约定某个namespace中的Pod的cpu、内存的默认限制值。在Pod没有限制资源使用量时,使用该默认设置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: 1Gi
cpu: 1
requests:
memory: 256Mi
cpu: 100m
|
临时存储
容器临时存储包含日志和可写层的数据,可以通过定义Pod Spec中的下面两个字段来申请
- limits.ephemeral-storage
- requests.ephemeral-storage
Pod调度完成后,对临时存储的限制不是基于cgroup的,而是kubelet定时检查容器日志和可写层的磁盘使用,如果超过限制就进行驱逐。
init container
调度包含多个init container的Pod时,只计算cpu.request最多的init容器,而不是计算所有init容器的和。
- 多个init容器是按顺序执行的,完成后退出,并不是一起执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 10']
containers:
- name: nginx
image: nginx
|
控制调度
通过nodeSelector/nodeAffinity/podAffinity/Taints/tolerations 来控制Pod在Node上的调度,也可以通过设置NodeName将Pod调度到指定Node。
nodeSelector
1
|
kubectl label nodes node-01 disktype=ssd
|
1
2
3
|
spec:
nodeSelector:
disktype: ssd
|
nodeAffinity
pod和node之间的亲和性。目前支持两种,分别代表必须满足的条件和优选条件
- requiredDuringScheduling, predicted阶段
- preferredDuringScheduling, priority阶段
修改亲和性时,应该使用kubectl replace执行。假设之前是required,需要改为prefered,如果用apply执行,相当于-XPATCH方法,会追加prefered,而不删除required,导致无法生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
|
podAffinity
pod之间的亲和性。
示例:必须调度到有带security=S1标签的运行Pod的node上,尽量不和带有security=S2标签的运行Pod在一个node。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx
|
Taints/Tolerations
Taints和Tolerations保证Pod不会被调度到不合适的Node上
- Taints应用于Node
- Tolerations应用于Pod
目前支持的Taints类型:
- NoSchedule 新的Pod不调度到该Node,不影响正在运行的Pod
- PreferNoSchedule 尽量不调度到该Node
- NoExecute 新的Pod不调度上来,并evict删除正在运行的Pod,可以增加一个延时tolerationSeconds
1
|
kubectl taint nodes master user-name=cadmin:NoSchedule # 格式 k=v:type
|
当Pod的toleration匹配Node上所有Taints时,就可以调度到该Node。
1
2
3
4
5
6
7
|
spec:
containers:
tolerations:
- key: "user-name"
operator: "Equal"
value: "cadmin"
effect: "NoSchedule"
|
优先级调度
支持定义Pod的优先级,保证高优先级的Pod优先调度。
- 需要在apiserver和scheduler的启动参数中配置
- 需要定义ProrityClass定义优先级,然后在Pod的spec中指定优先级名称
生产经验
- 放大效应:当一个node上出现问题导致负载较小时,用户的pod会优先调度到该node,导致用户pod创建失败
- 应用炸弹:恶意Pod运行时打开过多句柄,导致node 宕机,pod会被驱逐到其它node,再对其他node进行伤害