Pod 亲和性

Pod 亲和性(podAffinity)主要解决 Pod 可以和哪些 Pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等),而 Pod 反亲和性主要是解决 Pod 不能和哪些 Pod 部署在同一个拓扑域中的问题,它们都是处理的 Pod 与 Pod 之间的关系,比如一个 Pod 在一个节点上了,那么我这个也得在这个节点,或者你这个 Pod 在节点上了,那么我就不想和你待在同一个节点上。

由于我们这里只有一个集群,并没有区域或者机房的概念,所以我们这里直接使用主机名来作为拓扑域,把 Pod 创建在同一个主机上面。

1
2
3
4
5
6
7
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
ydzs-master Ready master 55d v1.16.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
ydzs-node1 Ready <none> 55d v1.16.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-node1,kubernetes.io/os=linux
ydzs-node2 Ready <none> 55d v1.16.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,com=youdianzhishi,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-node2,kubernetes.io/os=linux
ydzs-node3 Ready <none> 53d v1.16.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-node3,kubernetes.io/os=linux,monitor=prometheus
ydzs-node4 Ready <none> 53d v1.16.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ydzs-node4,kubernetes.io/os=linux

同样,还是针对上面的资源对象,我们来测试下 Pod 的亲和性:(pod-affinity-demo.yaml)

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity
labels:
app: pod-affinity
spec:
replicas: 3
selector:
matchLabels:
app: pod-affinity
template:
metadata:
labels:
app: pod-affinity
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname

上面这个例子中的 Pod 需要调度到某个指定的节点上,并且该节点上运行了一个带有 app=busybox-pod 标签的 Pod。我们可以查看有标签 app=busybox-pod 的 pod 列表:

1
2
3
$ kubectl get pods -l app=busybox-pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-busybox 1/1 Running 0 27m 10.244.2.242 ydzs-node2 <none> <none>

我们看到这个 Pod 运行在了 ydzs-node2 的节点上面,所以按照上面的亲和性来说,上面我们部署的3个 Pod 副本也应该运行在 ydzs-node2 节点上:

1
2
3
4
5
6
7
$ kubectl apply -f pod-affinity-demo.yaml 
deployment.apps/pod-affinity created
$ kubectl get pods -o wide -l app=pod-affinity
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-587f9b5b58-5nxmf 1/1 Running 0 26s 10.244.2.249 ydzs-node2 <none> <none>
pod-affinity-587f9b5b58-m2j7s 1/1 Running 0 26s 10.244.2.248 ydzs-node2 <none> <none>
pod-affinity-587f9b5b58-vrd7b 1/1 Running 0 26s 10.244.2.250 ydzs-node2 <none> <none>

如果我们把上面的 test-busybox 和 pod-affinity 这个 Deployment 都删除,然后重新创建 pod-affinity 这个资源,看看能不能正常调度呢:

1
2
3
4
5
6
7
8
9
10
11
$ kubectl delete -f node-selector-demo.yaml
pod "test-busybox" deleted
$ kubectl delete -f pod-affinity-demo.yaml
deployment.apps "pod-affinity" deleted
$ kubectl apply -f pod-affinity-demo.yaml
deployment.apps/pod-affinity created
$ kubectl get pods -o wide -l app=pod-affinity
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-587f9b5b58-bbfgr 0/1 Pending 0 18s <none> <none> <none> <none>
pod-affinity-587f9b5b58-lwc8n 0/1 Pending 0 18s <none> <none> <none> <none>
pod-affinity-587f9b5b58-pc7ql 0/1 Pending 0 18s <none> <none> <none> <none>

我们可以看到都处于 Pending 状态了,这是因为现在没有一个节点上面拥有 app=busybox-pod 这个标签的 Pod,而上面我们的调度使用的是硬策略,所以就没办法进行调度了,大家可以去尝试下重新将 test-busybox 这个 Pod 调度到其他节点上,观察下上面的3个副本会不会也被调度到对应的节点上去。

我们这个地方使用的是 kubernetes.io/hostname 这个拓扑域,意思就是我们当前调度的 Pod 要和目标的 Pod 处于同一个主机上面,因为要处于同一个拓扑域下面,为了说明这个问题,我们把拓扑域改成 beta.kubernetes.io/os,同样的我们当前调度的 Pod 要和目标的 Pod 处于同一个拓扑域中,目标的 Pod 是拥有 beta.kubernetes.io/os=linux 的标签,而我们这里所有节点都有这样的标签,这也就意味着我们所有节点都在同一个拓扑域中,所以我们这里的 Pod 可以被调度到任何一个节点,重新运行上面的 app=busybox-pod 的 Pod,然后再更新下我们这里的资源对象:

1
2
3
4
5
$ kubectl get pods -o wide -l app=pod-affinity
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-76c56567c-792n4 1/1 Running 0 2m59s 10.244.2.254 ydzs-node2 <none> <none>
pod-affinity-76c56567c-8s2pd 1/1 Running 0 3m53s 10.244.4.18 ydzs-node4 <none> <none>
pod-affinity-76c56567c-hx7ck 1/1 Running 0 2m52s 10.244.3.23 ydzs-node3 <none> <none>

可以看到现在是分别运行在3个节点下面的,因为他们都属于 beta.kubernetes.io/os 这个拓扑域。

Pod 反亲和性

Pod 反亲和性(podAntiAffinity)则是反着来的,比如一个节点上运行了某个 Pod,那么我们的模板 Pod 则不希望被调度到这个节点上面去了。我们把上面的 podAffinity 直接改成 podAntiAffinity:(pod-antiaffinity-demo.yaml)

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-antiaffinity
labels:
app: pod-antiaffinity
spec:
replicas: 3
selector:
matchLabels:
app: pod-antiaffinity
template:
metadata:
labels:
app: pod-antiaffinity
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname

这里的意思就是如果一个节点上面有一个 app=busybox-pod 这样的 Pod 的话,那么我们的 Pod 就别调度到这个节点上面来,上面我们把app=busybox-pod 这个 Pod 固定到了 ydzs-node2 这个节点上面的,所以正常来说我们这里的 Pod 不会出现在该节点上:

1
2
3
4
5
6
7
$ kubectl apply -f pod-antiaffinity-demo.yaml 
deployment.apps/pod-antiaffinity created
$ kubectl get pods -l app=pod-antiaffinity -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-antiaffinity-84d5bf9df4-9c9qk 1/1 Running 0 73s 10.244.4.19 ydzs-node4 <none> <none>
pod-antiaffinity-84d5bf9df4-q6lkm 1/1 Running 0 67s 10.244.3.24 ydzs-node3 <none> <none>
pod-antiaffinity-84d5bf9df4-vk9tc 1/1 Running 0 57s 10.244.3.25 ydzs-node3 <none> <none>

我们可以看到没有被调度到 ydzs-node2 节点上,因为我们这里使用的是 Pod 反亲和性。大家可以思考下,如果这里我们将拓扑域更改成 beta.kubernetes.io/os 会怎么样呢?可以自己去测试下看看。