Pod的调度在默认情况下是Scheduler Controller 采用默认算法的全自动调度,在实际使用中并不满足我们的需求,因为并不能事先掌握pod被调度到哪个Node之上,所以kubernetes又提供了好几种方式让我们自已选择调度到什么Node中,比如有NodeSelector(定向调度)、NodeAffinity(Node亲和性)、PodAffinity(Pod亲和性)。

NodeSelector调度算法比较简单,NodeSelector 只调度到某个拥有特定标签的Node上,如果没有满足条件的Node,那么此Pod将不会被运行,即使在集群中还有可用Node列表,这就限制了它的使用场景,现在基本上被NodeAffinity取代了,NodeAffinity在NodeSelector的基础之上的进行了扩展,使调度更加灵活,除了有一个必须要满足条件的Node之外,还要可设置优先条件,下面来看下NodeAffinity的使用方式:

一、NodeAffinity

NodeAffinity 亲和性有两种表达方式:

  • RequiredDuringSchedulingIgnoredDuringExecution :必须满足指定的规则才可以调度Pod到Node上,相当于硬限制。
  • PreferredDuringSchedulingIgnoredDuringExecution:强调优先满足指定的规则,相当于软限制,并不强求,如果多个优先级规则,还可以设置权重,以定义执行顺序。

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
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-aff
labels:
os: centos
spec:
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions: #设置node拥有的标签
- key: disktype
operator: In
values:
- hdd
#表示一定会调度在拥有此标签(disktype=hdd)的Node上。
如果将requiredDuringSchedulingIgnoredDuringExecution改为 preferredDuringSchedulingIgnoredDuringExecution
那么就不一定会调度到有disktype=hdd标签的Node,它会看情况,如果有这个标签的Node负载已经是很高的了,那么就会调度到负载较低的Node上

从上面的配置中可以看到,它操作符operator为In,NodeAffinity语法支持的操作符有In、NotIn、Exists、DoesNotExist、Gt、Lt虽然没有节点排斥功能,但是用NotIn和DoesNotExist就可以实现排斥功能了。

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

  • 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的Node上

  • 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可,nodeSelectorTerms 属性是设置要调度到的node的标签的,因此nodeSelectorTerms 如果有多个说明是可以调度到不同的标签,即不同的Node之上,所以只需要匹配一个就行了

  • 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的 matchExpressions 才能匹配成功。matchExpressions 是在nodeSelectorTerms 下,设置具体的一个Node标签,设置有多个如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的matchExpressions 才能匹配成功。matchExpressions 是在nodeSelectorTerms 下,设置具体的一个Node标签,设置有多个nodeSelectorTerms ,意味着匹配的Node需要有多个标签,且多个标签必须同时要有。nodeSelectorTerms ,意味着匹配的Node需要有多个标签,且多个标签必须同时要有。

上面的例子中,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
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-aff
labels:
os: centos
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #第一条件
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
preferredDuringSchedulingIgnoredDuringExecution: #第二个条件
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- hdd
containers:
- name: ubuntu-aff
image: 10.3.1.15:5000/ubuntu:16.04
env: #在容器内配置环境变量
- name: Test
value: "7890"
command: ["bash","-c","while true;do date;sleep 1;done"]

如上,创建pod时,节点亲和性有两个,也就是说如果某个Node运行这个Pod,那么这个Node需要满足的条件有两个,

  • 第一个条件是 属性为 requiredDuringSchedulingIgnoredDuringExecution ,此为硬性条件,必须要满足的: beta.kubernetes.io/arch=amd64

  • 第二个条件属性为 preferredDuringSchedulingIgnoredDuringExecution ,此为软性条件,即优先满足的条件,尽可能会满足,如果无法满足就退而求其次,运行在其它Node之上。

那么调度时该如何选择调度到哪个Node上?

为什么要设置两个条件呢,因为满足第一个条件的Node有很多,即拥有 beta.kubernetes.io/arch=amd64 的标签的Node有很多,这里第二个条件是说 要尽量运行在 disktype=hdd的Node上。

总的来说,即 只运行在arch=amd64的node上,在此条件之下优先运行在disktype=hdd的Node上。

二、PodAffinity: Pod亲和性与互斥性调度

根据节点上正在运行的pod的标签来调度,而非node的标签,要求对节点和Pod两个条件进行匹配,其规则为:如果在具有标签X的Node上运行了一个或多个符合条件Y的Pod,那么Pod应该运行在此Node上,如果是互斥,则拒绝运行在此Node上。 也就是说根据某个已存在的pod,来决定要不要和此pod在一个Node上,在一起就需要设置亲和性,不和它在一起就设置互斥性。X指的是一个集群中的节点、机架、、区域等概念,通过Kubernetes内置节点标签中的key来进行声明,这个key的名字为topologyKey,用来表达节点所属的拓朴结构之下。

pod的亲和性表达方式与Node亲和性是一样的表达方式。

kubernetes内置标签:

  • kubernetes.io/hostname

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

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

  • beta.kubernetes.io/instance-type

  • beta.kubernetes.io/os

  • beta.kubernetes.io/arch

1、创建参照Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pod affinity的参照Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels: # 定义多个标签,以便其它pod设置亲和与互斥
app: nginx
security: s1
spec:
containers:
- name: nginx-1-10
image: 10.3.1.15:5000/nginx:1.10
ports:
- containerPort: 80

#查看调度到哪个Node之上:
root@ubuntu:# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-flag 1/1 Running 0 4m 192.168.150.232 10.3.1.16

#运行在Node 10.3.1.16上

2、创建一个pod,与参照的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
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
labels:
os: ubuntu
spec:
affinity: #亲和性设置
podAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: #必须要满足的条件
- labelSelector: #与哪个pod有亲和性,在此设置此pod具有的标签
matchExpressions: #要匹配的pod的,标签定义,如果定义了多个matchExpressions,则所有标签必须同时满足。
- key: security #标签的key
operator: In #操作符
values:
- s1 #标签的值,即拥有label security=s1
topologyKey: kubernetes.io/hostname #节点所属拓朴域
#上面表达的意思是此处创建的Pod必须要与拥有标签security=s1的pod在同一Node上.
containers:
- name: wish-pod-affinity
image: 10.3.1.15:5000/ubuntu:16.04
env:
- name: Test
value: "7890"
command: ["bash","-c","while true;do date;sleep 1;done"]


# 因为pod是属于某个命名空间的,所以设置符合条件的目标Pod时,还可以指定哪个命名空间或全部命名空间里的Pod,
# namespace的定义与labelSelector同级,如果不指定命名空间,则与此处创建的pod在一个namespace之中
1
2
3
4
5
6
7
root@ubuntu:# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
with-pod-affinity 1/1 Running 0 11s 192.168.150.252 10.3.1.16
pod-flag 1/1 Running 0 1h 192.168.150.232 10.3.1.16

#的确是在同一个Node上。
#如果在创建时,pod状态一直处于Pending状态,很有可能是因为找不到满足条件的Node。

3、创建一个pod,与参照的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
apiVersion: v1
kind: Pod
metadata:
name: with-pod-antiffinity
labels:
os: ubuntu
spec:
affinity: #亲和性设置
podAntiAffinity: #设置pod反亲和性
requiredDuringSchedulingIgnoredDuringExecution: #必须要满足的条件
- labelSelector: #与哪个pod有反亲和性,互斥性,在此设置此pod具有的标签
matchExpressions:
- key: app #标签的key
operator: In
values:
- nginx #标签的值,即拥有label app=nginx
topologyKey: kubernetes.io/hostname #节点所属拓朴域
#上面表达的意思是此处创建的Pod必须要与拥有标签app=nginx的pod不在在同一个Node上.如果所有 Node将无法满足条件,则创建的pod一直处于Pending状态。
containers:
- name: wish-pod-antiaffinity
image: 10.3.1.15:5000/ubuntu:16.04
env:
- name: Test
value: "7890"
command: ["bash","-c","while true;do date;sleep 1;done"]
1
2
3
4
5
6
7
root@ubuntu15:/data/yaml# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-flag 1/1 Running 0 1h 192.168.150.232 10.3.1.16
with-pod-affinity 1/1 Running 0 1h 192.168.150.252 10.3.1.16
with-pod-antiffinity 1/1 Running 0 1m 192.168.77.204 10.3.1.17

#可以看到与参照pod不在同一个node之上。

更多的的Pod亲和性调度信息可以参考官方文档。