Kubernetes的调度

调度

基本概述

kube-scheduler 是kubernetes 集群的默认调度器,该调度器将Pod绑定在对应的node上,之后对应的node上的kubelet才能够创建和运行Pod。
对每个新建的Pod或者未被调度的Pod,kube-scheduler 会选择一个最优的node去运行这个Pod。Pod内的每个容器以及自身都有不通的资源需求。因此,Pod在被调度之前,会根据特定的资源调度需求,对集群中的Node进行一次过滤。
调度器会在集群中过滤后的可调度节点,根据一些列函数进行打分,最终选择出得分最高的node。之后,调度器将这个调度通知给kube-apiserver,进行绑定。

亲和性

原则

  • 同时指定了nodeSelector和nodeAffinity,必须同时满足两个条件
  • 如果nodeAffinity指定了多个nodeSelectorTerms,满足一个即可
  • 如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足全部才可以加
  • 如果修改或删除了Pod所调度到的节点的标签,Pod不会被删除
  • 软策略中的weight属性代表权重,范围1-100。如果存在多个软策略的话,权重越大越亲和。对于反亲和规则而言,权重越大越不亲和

应用的运行对于kubernetes在亲和性上有一些要求,可以概况为以下几个方面:

  1. Pod固定调度的某些节点上
  2. Pod不会调度到某些节点上
  3. Pod的多个副本调度到相同节点上
  4. Pod的多个副本调度不同节点上

调度到某些节点上

方式一、nodeSelector

在对于Pod的定义中通过nodeSelector指定label,Pod将会只调度到具有该标签的node上。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd

上述例子中,Pod将会只调度到包含disktype=ssd的node上。

方式二、nodeName

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Deployment
metadata:
name: webpc
spec:
replicas: 1
template:
metadata:
labels:
app: webpc
spec:
nodeName: web-server
...

上述例子中,将会只调度到主机名为web-server的node上。

方式三、亲和性

NodeAffinity 亲和性调度,用来取代nodeSelector的策略,支持IN,NOTLn,exists,doesNotExit,GT,Lt。

又分为两类:

  1. soft,表示调度的时候,无法满足节点的时候会选择非nodeSelector匹配的节点。PreferredDuringSchedulingIgnoreDuringExecution 优先满足制定规则,并不强求
  2. hard,表示调度的时候,必须满足亲和性设置。requiredDuringSchedulingIgnoredDuringExecution 必须调度到满足条件的节点,如不满足,则一直重试。

也可以分为三类:

  1. 亲和节点 NodeAffinity
  2. 亲和Pod podAffinity
  3. 不亲和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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: eureka
spec:
serviceName: "eureka"
selector:
matchLabels:
app: eureka-pod
replicas: 3
template:
metadata:
labels:
app: eureka-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node-217

# 验证,新的Pod已经调度到k8s-node-217
~# kubectl get pods -o wide |grep eureka
eureka-0 1/1 Running 0 2d23h 10.200.128.142 uat-master <none> <none>
eureka-1 1/1 Running 0 2d23h 10.200.128.141 uat-master <none> <none>
eureka-2 0/1 ContainerCreating 0 1s <none> k8s-node-217 <none> <none>

上述示例中,terms满足一个条件就行(或的关系),但是match的条件必须全部满足(且的关系)。

调度到不同的节点

调度到不同的节点,在亲和性中的应用需要使用到topolgyKey。toplogyKey 对应的值是node上的一个标签的名称,比如节点zone=A 标签,其他节点有zone=B 标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
KIND:     StatefulSet
VERSION: apps/v1

RESOURCE: affinity <Object>

DESCRIPTION:
If specified, the pod's scheduling constraints

Affinity is a group of affinity scheduling rules.

FIELDS:
nodeAffinity <Object>
Describes node affinity scheduling rules for the pod.

podAffinity <Object>
Describes pod affinity scheduling rules (e.g. co-locate this pod in the
same node, zone, etc. as some other pod(s)).

podAntiAffinity <Object>
Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod
in the same node, zone, etc. as some other pod(s)).
1
2
3
4
5
6
7
8
9
10
11
12
kubectl explain --api-version=apps/v1 sts.spec.template.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.topologyKey
KIND: StatefulSet
VERSION: apps/v1

FIELD: topologyKey <string>

DESCRIPTION:
This pod should be co-located (affinity) or not co-located (anti-affinity)
with the pods matching the labelSelector in the specified namespaces, where
co-located is defined as running on a node whose value of the label with
key topologyKey matches that of any node on which any of the selected pods
is running. Empty topologyKey is not allowed.

topology 就是拓扑的意思,指的是一个拓扑域,一个范围的概念,比如一个node、一个机柜、一个机房,实际对应的是node的标签。而这里的topologyKey对应的是node上标签的Key。那么怎么样才算属于一个拓扑域呢?

如果使用k8s.io/hostname,则表示拓扑域为node范围,那么k8s.io/hostname 对应的值不一样的就是不同的拓扑域。比如Pod1在k8s.io/hostname=n1的node上,Pod2在k8s.io/hostname=n2的node上,Pod3在k8s.io/hostname=n1的node上。那么Pod1和Pod3在一个拓扑域,Pod2 则和其他两个Pod不在同一个拓扑域。因此,概况来说,拥有相同标签的key,就可以认定为在同一个拓扑域。

原则上,topologyKey可以是任何合法的标签key,但是有一些限制:

  1. topologyKey不可以为空
  2. 对于 requiredDuringSchedulingIgnoredDuringExecution 的 Pod 反亲和性,引入 LimitPodHardAntiAffinityTopology 准入控制器来限制 topologyKey 只能是 kubernetes.io/hostname。如果要使用自定义拓扑域,则可以修改准入控制器,或者直接禁用它。
  3. 对于 preferredDuringSchedulingIgnoredDuringExecution 的 Pod 反亲和性,空的 topologyKey 表示所有拓扑域。除上述情况外,topologyKey 可以是任何合法的标签 key。

此外,还有一些官网文档中提示的拓扑域可以使用:

  1. topology.kubernetes.io/region
  2. topology.kubernetes.io/zone

示例:

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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: eureka
spec:
serviceName: "eureka"
selector:
matchLabels:
app: eureka-pod
replicas: 3
template:
metadata:
labels:
app: eureka-pod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- eureka-pod

上述示例中指定创建了label为app=eureka-pod 的statefulSet,并且指定副本数量为3。根据反亲和性规则,以kubernetes.io/hostname为key的拓扑域,将不会调度到node上已经运行了label为app=eureka-pod的Pod,这样就会将3个副本分别部署在不同hostname的node上。

验证:

1
2
3
4
~# kubectl get pods -o wide |grep eureka
eureka-0 1/1 Running 0 5m47s 10.200.128.152 uat-master <none> <none>
eureka-1 0/1 Pending 0 38s <none> <none> <none> <none>
eureka-2 1/1 Running 0 108s 10.200.96.155 k8s-node-217 <none> <none>

由于示例中只有两个node,因此有一个Pod处于pending状态,但是其他的两个节点均被分配了Pod。

亲和性的进阶应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- eureka-pod
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- geteway-pod

上述例子中,根据反亲和性和亲和性规则,每个节点上都会有eureka-pod的Pod,和gateway-pod的Pod。

参考

  1. https://kubernetes.io/zh/docs/reference/labels-annotations-taints/