pod调度

NodeSelector 定向调度

将Pod调度到指定的一些Node上, 可以通过Node的标签(Label) 和Pod的nodeSelector属性相匹配来实现。

如果我们给多个Node都定义了相同的标签(例如zone=north) , 则scheduler会根据调度算法从这组Node中挑选一个可用的Node进行Pod调度。

如果我们指定了Pod的nodeSelector条件, 且在集群中不存在包含相应标签的Node, 则即使在集群中还有其他可供使用的Node, 这个Pod也无法被成功调度。

NodeAffinity: Node亲和性调度

RequiredDuringSchedulingIgnoredDuringExecution

必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像, 但是使用的是不同的语法), 相当于硬限制

PreferredDuringSchedulingIgnoredDuringExecution

强调优先满足指定规则, 调度器会尝试调度Pod到Node上, 但并不强求, 相当于软限制。 多个优先级规则还可以设置权重(weight) 值, 以定义执行的先后顺序。

IgnoredDuringExecution的意思是: 如果一个Pod所在的节点在Pod运行期间标签发生了变更, 不再符合该Pod的节点亲和性需求, 则系统将忽略Node上Label的变化, 该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
25
26
27
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
# 要求只运行在amd64的节点上
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
# 要求是尽量运行在磁盘类型为ssd的节点上
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0

NodeAffinity语法支持的操作符包括In、 NotIn、 Exists、 DoesNotExist、 Gt、 Lt。

注意事项:

  • 如果同时定义了nodeSelector和nodeAffinity, 那么必须两个条件都得到满足, Pod才能最终运行在指定的Node上。
  • 如果nodeAffinity指定了多个nodeSelectorTerms, 那么其中一个能够匹配成功即可。
  • 如果在nodeSelectorTerms中有多个matchExpressions, 则一个节点必须满足所有matchExpressions才能运行该Pod。

PodAffinity: Pod亲和与互斥调度策略

如果在具有标签X的Node上运行了一个或者多个符合条件Y的Pod, 那么Pod应该(如果是互斥的情况, 那
么就变成拒绝) 运行在这个Node上。

这里X指的是一个集群中的节点、 机架、 区域等概念, 通过Kubernetes内置节点标签中的key来进行声明。 这个key的名字为topologyKey, 意为表达节点所属的topology范围。

  • kubernetes.io/hostname
  • failure-domain.beta.kubernetes.io/zone
  • failure-domain.beta.kubernetes.io/region

和节点亲和相同, Pod亲和与互斥的条件设置也是requiredDuringSchedulingIgnoredDuringExecution和
preferredDuringSchedulingIgnoredDuringExecution。 Pod的亲和性被定义于PodSpec的affinity字段下的podAffinity子字段中。 Pod间的互斥性则被定义于同一层次的podAntiAffinity子字段中。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 参照pod
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels:
security: "S1"
app: "nginx"
spec:
containers:
- name: nginx
image: nginx

---
# 亲和pod例子
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: gcr.io/google_containers/pause:2.0

---
# 排斥pod例子
apiVersion: v1
kind: Pod
metadata:
name: anti-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
containers:
- name: anti-affinity
image: gcr.io/google_containers/pause:2.0

Pod亲和性的操作符也包括In、 NotIn、 Exists、DoesNotExist、 Gt、 Lt。

出于性能和安全方面的考虑, 对topologyKey有如下限制:

  • 在Pod亲和性和RequiredDuringScheduling的Pod互斥性的定义中, 不允许使用空的topologyKey。
  • 如果Admission controller包含了LimitPodHardAntiAffinityTopology, 那么针对Required DuringScheduling的Pod互斥性定义就被限制为kubernetes.io/hostname, 要使用自定义的topologyKey, 就要改写或禁用该控制器。
  • 在PreferredDuringScheduling类型的Pod互斥性定义中, 空的topologyKey会被解释为kubernetes.io/hostname、 failuredomain.beta.kubernetes.io/zone及failuredomain.beta.kubernetes.io/region的组合

PodAffinity规则设置的注意事项如下:

  • 除了设置Label Selector和topologyKey, 用户还可以指定Namespace列表来进行限制, 同样, 使用Label Selector对Namespace进行选择。 Namespace的定义和Label Selector及topologyKey同级。

  • 在所有关联requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全都满足之后, 系统才能将Pod调度到某个Node上

Taints和Tolerations( 污点和容忍)

Taint它让Node拒绝Pod的运行,Taint需要和Toleration配合使用, 让Pod避开那些不合适的Node。 在Node上设置一个或多个Taint之后, 除非Pod明确声明能够容忍这些污点, 否则无法在这些Node上运行。 Toleration是Pod的属性, 让Pod能够( 注意, 只是能够, 而非必须) 运行在标注了Taint的Node上。

1
2
3
# 用kubectl taint命令为Node设置Taint信息
# 为node1加上了一个Taint。 该Taint的键为key, 值为value, Taint的效果是NoSchedule。 这意味着除非Pod明确声明可以容忍这个Taint, 否则就不会被调度到node1上。
kubectl taint nodes node1 key=value:NoSchedule
1
2
3
4
5
6
7
8
9
10
11
12
13
# 在Pod上声明Toleration(如果不指定operator, 则默认值为Equal)
# 空的key配合Exists操作符能够匹配所有的键和值
# 空的effect匹配所有的effect
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
# 或者
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"

Kubernetes调度器处理多个Taint和Toleration的逻辑顺序为:首先列出节点中所有的Taint, 然后忽略Pod的Toleration能够匹配的部分, 剩下的没有忽略的Taint就是对Pod的效果 ,以下情况需要注意:

  • 如果在剩余的Taint中存在effect=NoSchedule, 则调度器不会把该Pod调度到这一节点上
  • 如果在剩余的Taint中没有NoSchedule效果, 但是有PreferNoSchedule效果, 则调度器会尝试不把这个Pod指派给这个节点
  • 如果在剩余的Taint中有NoExecute效果, 并且这个Pod已经在该节点上运行, 则会被驱逐; 如果没有在该节点上运行, 则也不会再被调度到该节点上

应用场景:

  • 独占节点
  • 具有特殊硬件设备的节点
  • 定义Pod驱逐行为, 以应对节点故障

Pod Priority Preemption: Pod优先级调度

Pod优先级调度(抢占式调度 )(1.14默认开启)

提高资源利用率的常规做法是采用优先级方案, 即不同类型的负载对应不同的优先级, 同时允许集群中的所有负载所需的资源总量超过集群可提供的资源, 在这种情况下, 当发生资源不足的情况时, 系统可以选择释放一些不重要的负载(优先级最低的) , 保障最重要的负载能够获取足够的资源稳定运行。

Eviction(驱逐)是kubelet进程的行为 ;Preemption(抢占)则是Scheduler执行的行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建优先级
apiVersion: scheduling.k8s.io/v1beta1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

---
# 给pod设置优先级
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority

DaemonSet: 在每个Node上都调度一个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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
template:
metadata:
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
image: gcr.io/google_containers/fluentd-elasticsearch:1.17
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD_ARGS
value: -q
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: false
- name: containers
mountPath: /var/lib/docker/containers
readOnly: false
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log

DaemonSet支持滚动升级

Job: 批处理调度

按照批处理任务实现方式的不同,批处理任务可以分为:

  • Job Template Expansion模式: 一个Job对象对应一个待处理的Work item, 有几个Work item就产生几个独立的Job, 通常适合Work item数量少、 每个Work item要处理的数据量比较大的场景
  • Queue with Pod Per Work Item模式: 采用一个任务队列存放Work item, 一个Job对象作为消费者去完成这些Work item, 在这种模式下, Job会启动N个Pod, 每个Pod都对应一个Work item
  • Queue with Variable Pod Count模式: 也是采用一个任务队列存放Work item, 一个Job对象作为消费者去完成这些Work item, 但与上面的模式不同, Job启动的Pod数量是可变的

考虑到批处理的并行问题, Kubernetes将Job分以下三种类型 :

  • Non-parallel Jobs: 通常一个Job只启动一个Pod, 除非Pod异常, 才会重启该Pod, 一旦
    此Pod正常结束, Job将结束
  • Parallel Jobs with a fixed completion count: 并行Job会启动多个Pod, 此时需要设定Jo的.spec.completions参数为一个正数, 当正常结束的Pod数量达至此参数设定的值后, Job结束。此外, Job的.spec.parallelism参数用来控制并行度, 即同时启动几个Job来处理Work Item
  • Parallel Jobs with a work queue: 任务队列方式的并行Job需要一个独立的Queue, Work item都在一个Queue中存放, 不能设置Job的.spec.completions参数

Cronjob: 定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: batch/v2alpha1 
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure