持续集成部署-k8s-高级调度-亲和力
- [1. 亲和力的基本概念](#1. 亲和力的基本概念)
- [2. 亲和性和非亲和性](#2. 亲和性和非亲和性)
- [3. 节点亲和力的使用](#3. 节点亲和力的使用)
- [4. 节点亲和性权重](#4. 节点亲和性权重)
- [5. 验证节点亲和性](#5. 验证节点亲和性)
- [6. Pod 间亲和性与反亲和性](#6. Pod 间亲和性与反亲和性)
- [7. Pod 间亲和性与反亲和性的类型](#7. Pod 间亲和性与反亲和性的类型)
- [8. 调度一组具有 Pod 间亲和性的 Pod](#8. 调度一组具有 Pod 间亲和性的 Pod)
- [9. 验证 Pod 亲和性](#9. 验证 Pod 亲和性)
1. 亲和力的基本概念
在Kubernetes
中,亲和力(Affinity
)是一种高级调度机制,用于指定Pod如何与节点进行亲和匹配。通过亲和力规则,你可以控制Pod
被调度到特定节点的方式,以便更好地满足应用程序的需求和约束条件。
亲和力规则
分为两种类型:node亲和性
和pod亲和性
。
-
Node亲和性:
- Node亲和性规则允许你指定
Pod
应该被调度到具有特定标签的节点上。这种标签可以是节点的特性、硬件配置或者地理位置等。通过使用Node亲和性,你可以确保某些Pod只被调度到符合特定要求的节点上。
- Node亲和性规则允许你指定
-
Pod亲和性:
- Pod亲和性规则允许你指定Pod应该与其他Pod共同部署在同一个节点上。这种规则可以确保某些相关的Pod被调度到相同的节点上,以减少网络延迟或提高局部性能。
相较于污点和容忍而言,污点和容忍更倾向于一个排除的效果,你能容忍我的污点,你就来,你不能容忍我的污点,那你就走。
接着来看下一种新的调度效果叫做亲和力。亲和力刚好跟污点容忍相反,就是配置 Pod 尽可能的到某个地方,而反亲和力刚好跟亲和力相反,就是尽可能的不到某个地方,跟污点一个效果。
污点是使用的是污点和容忍,而亲和力匹配使用的标签 Label。亲和度越高,越有可能被调度选中。
污点配合亲和力,就能够实现更细致、更复杂、更智能的调度功能。
2. 亲和性和非亲和性
nodeSelector
提供了一种最简单的方法来将 Pod
约束到具有特定标签的节点上。 亲和性和反亲和性扩展了你可以定义的约束类型。使用亲和性与反亲和性的一些好处有:
- 亲和性、反亲和性语言的表达能力更强。
nodeSelector
只能选择拥有所有指定标签的节点。 亲和性、反亲和性为你提供对选择逻辑的更强控制能力。 - 你可以标明某规则是"软需求"或者"偏好",这样调度器在无法找到匹配节点时仍然调度该 Pod。
- 你可以使用节点上(或其他拓扑域中)运行的其他 Pod 的标签来实施调度约束, 而不是只能使用节点本身的标签。这个能力让你能够定义规则允许哪些 Pod 可以被放置在一起。
亲和性功能由两种类型的亲和性组成:
节点亲和性
功能类似于nodeSelector
字段,但它的表达能力更强,并且允许你指定软规则。- Pod 间亲和性/反亲和性允许你根据其他 Pod 的标签来约束 Pod。
节点亲和性概念上类似于 nodeSelector
, 它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。 节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
: 调度器只有在规则被满足的时候才能执行调度。此功能类似于nodeSelector
, 但其语法表达能力更强。preferredDuringSchedulingIgnoredDuringExecution
: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
说明:
在上述类型中,IgnoredDuringExecution 意味着如果节点标签在 Kubernetes 调度 Pod 后发生了变更,Pod 仍将继续运行。
3. 节点亲和力的使用
看下官方文档里面的 Demo:pods/pod-with-node-affinity.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity: # 定义亲和性规则
nodeAffinity: # 定义节点亲和性规则
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms: # 定义选择节点的条件
- matchExpressions: # 定义匹配表达式
- key: topology.kubernetes.io/zone # 指定匹配的标签键
operator: In # 指定匹配操作符为In,表示节点的标签键值在指定的values中
values: # 指定匹配的标签值列表,包括"antarctica-east1"和"antarctica-west1"
- antarctica-east1
- antarctica-west1
preferredDuringSchedulingIgnoredDuringExecution: # 定义优选的节点亲和性规则,即首选但不是必需的
- weight: 1 # 定义优选规则的权重为1
preference:
matchExpressions: # 定义首选规则的匹配表达式
- key: another-node-label-key # 指定匹配的标签键
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
所应用的规则如下:
- 节点必须包含一个键名为
topology.kubernetes.io/zone
的标签, 并且该标签的取值必须为antarctica-east1
或antarctica-west1
。 - 节点最好具有一个键名为
another-node-label-key
且取值为another-node-label-value
的标签。
可以使用 operator
字段来为 Kubernetes
设置在解释规则时要使用的逻辑操作符。 你可以使用 In
、NotIn
、Exists
、DoesNotExist
、Gt
和 Lt
之一作为操作符。
In
:满足一个就行;NotIn
:一个都不能满足,反亲和性;Exists
:只要存在,就满足;DoesNotExist
:只有不存在,才满足;Gt
:必须要大于节点上的数值才满足;Lt
:必须要小于节点上的数值才满足;
在要求该Pod
被调度到拥有特定标签的节点上(拓扑域为"antarctica-east1
"或"antarctica-west1
")。同时也定义了一个首选规则
,希望该Pod
被调度到带有特定标签
的节点上(标签键为"another-node-label-key
",标签值为"another-node-label-value
")。
通过这样的配置,Kubernetes
调度器将会优先考虑满足必需的节点亲和性规则,如果没有满足的节点,则会考虑首选的节点亲和性规则。
NotIn
和 DoesNotExist
可用来实现节点反亲和性行为。 你也可以使用节点污点 将 Pod
从特定节点上驱逐。
说明:
如果你同时指定了
nodeSelector
和nodeAffinity
,两者 必须都要满足, 才能将 Pod 调度到候选节点上。如果你在与
nodeAffinity
类型关联的nodeSelectorTerms
中指定多个条件, 只要其中一个nodeSelectorTerms
满足(各个条件按逻辑或操作组合)的话,Pod 就可以被调度到节点上。如果你在与
nodeSelectorTerms
中的条件相关联的单个matchExpressions
字段中指定多个表达式, 则只有当所有表达式都满足(各表达式按逻辑与操作组合)时,Pod 才能被调度到节点上。
4. 节点亲和性权重
可以为 preferredDuringSchedulingIgnoredDuringExecution
亲和性类型的每个实例设置 weight
字段,其取值范围是 1
到 100
。 当调度器找到能够满足 Pod
的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight
值加和。
最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod
作出调度决定时,总分最高
的节点的优先级
也最高。
配置文件:pods/pod-with-affinity-anti-affinity.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: with-affinity-anti-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: label-1
operator: In
values:
- key-1
- weight: 50
preference:
matchExpressions:
- key: label-2
operator: In
values:
- key-2
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution
规则, 其中一个节点具有标签 label-1:key-1
,另一个节点具有标签 label-2:key-2
, 调度器会考察各个节点的 weight
取值,并将该权重值添加到节点的其他得分值之上,
说明:
如果你希望
Kubernetes
能够成功地调度此例中的Pod
,你必须拥有打了kubernetes.io/os=linux
标签的节点。
5. 验证节点亲和性
看下当前 k8s 环境节点的标签:
bash
[root@docker-54 ~]# kubectl get no --show-labels
NAME STATUS ROLES AGE VERSION LABELS
docker-54 Ready control-plane,master 201d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
docker-55 Ready <none> 201d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,type=microservice
docker-56 Ready <none> 201d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,type=microservice
[root@docker-54 ~]#
可以看到,上面节点都已经具备标签kubernetes.io/os=linux
了。
为了验证上面的节点亲和力配置,我这里将对两个从节点,分别添加标签:
bash
[root@docker-54 ~]# kubectl label no docker-55 label-1=key-1
node/docker-55 labeled
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl label no docker-56 label-2=key-2
node/docker-56 labeled
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get no --show-labels
NAME STATUS ROLES AGE VERSION LABELS
docker-54 Ready control-plane,master 202d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
docker-55 Ready <none> 201d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,label-1=key-1,type=microservice
docker-56 Ready <none> 201d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,label-2=key-2,type=microservice
[root@docker-54 ~]#
上面我对 docker-55
节点添加了 标签 label-1=key-1
,对 docker-56
节点添加了 标签 label-2=key-2
。
这里我直接使用上面配置文件的亲和性的内容,来修改我这边当前环境里面的一个 deploy ,直接编辑 deploy:kubectl edit deploy nginx-deploy
yaml
template:
metadata:
labels:
app: nginx-deploy
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: label-1
operator: In
values:
- key-1
- weight: 50
preference:
matchExpressions:
- key: label-2
operator: In
values:
- key-2
注意这里亲和力的配置也是跟 containers
是平级的。
保存之后,看下 Pod 的调度情况:
bash
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-b8976d7b5-b86q4 1/1 Terminating 0 55m 10.244.1.78 docker-55 <none> <none>
nginx-deploy-b8976d7b5-fvqn8 1/1 Terminating 1 (55m ago) 115m 10.244.2.119 docker-56 <none> <none>
nginx-deploy-c7f9688fd-mqfmf 1/1 Running 0 27s 10.244.2.123 docker-56 <none> <none>
nginx-deploy-c7f9688fd-w6vhk 1/1 Running 0 29s 10.244.2.122 docker-56 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-c7f9688fd-mqfmf 1/1 Running 0 54s 10.244.2.123 docker-56 <none> <none>
nginx-deploy-c7f9688fd-w6vhk 1/1 Running 0 56s 10.244.2.122 docker-56 <none> <none>
[root@docker-54 ~]#
可以看到,原来这两个 Pod 是分散在 两个从节点上的,在添加了上面的亲和力配置之后,Pod 都调度到了 docker-56 节点。
看下上面的节点亲和力配置,虽然两个从节点都满足了第一个要求,具有标签kubernetes.io/os=linux
,但是具有标签 label-2=key-2
的节点权重高,所以 Pod 会优先调度到具体此标签的节点上。
接着再次修改亲和力的配置,将标签 label-2=key-2
的判断条件由 In
改为 NotIn
,猜测下,这时候两个 Pod 都不能调度到带有标签 label-2=key-2
的节点上了。
验证看下:
bash
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-75578bc7f7-47cqt 0/1 ContainerCreating 0 2s <none> docker-55 <none> <none>
nginx-deploy-75578bc7f7-78j75 1/1 Running 0 4s 10.244.1.79 docker-55 <none> <none>
nginx-deploy-c7f9688fd-mqfmf 1/1 Running 0 7m52s 10.244.2.123 docker-56 <none> <none>
nginx-deploy-c7f9688fd-w6vhk 1/1 Terminating 0 7m54s 10.244.2.122 docker-56 <none> <none>
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-75578bc7f7-47cqt 1/1 Running 0 4s 10.244.1.80 docker-55 <none> <none>
nginx-deploy-75578bc7f7-78j75 1/1 Running 0 6s 10.244.1.79 docker-55 <none> <none>
nginx-deploy-c7f9688fd-mqfmf 1/1 Terminating 0 7m54s 10.244.2.123 docker-56 <none> <none>
nginx-deploy-c7f9688fd-w6vhk 1/1 Terminating 0 7m56s 10.244.2.122 docker-56 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
nginx-deploy-75578bc7f7-47cqt 1/1 Running 0 52s 10.244.1.80 docker-55 <none> <none>
nginx-deploy-75578bc7f7-78j75 1/1 Running 0 56s 10.244.1.79 docker-55 <none> <none>
可以看到确实符合上面的亲和力配置。
这里 Master 节点 docker-54 虽然符合带有标签kubernetes.io/os=linux
,但是不会被调度到它上面,是因为 Master 节点带有污点。
bash
[root@docker-54 ~]# kubectl describe no docker-54 |grep Taints
Taints: node-role.kubernetes.io/master:NoSchedule
[root@docker-54 ~]#
如果把这个污点删除掉,则上面的 Pod 还是有可能被调度到 Master 节点上的,接着试下看看:
bash
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-75578bc7f7-47cqt 1/1 Running 0 52s 10.244.1.80 docker-55 <none> <none>
nginx-deploy-75578bc7f7-78j75 1/1 Running 0 54s 10.244.1.79 docker-55 <none> <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoSchedule-
node/docker-54 untainted
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl describe no docker-54 |grep Taints
Taints: <none>
[root@docker-54 ~]#
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl delete po nginx-deploy-75578bc7f7-47cqt nginx-deploy-75578bc7f7-78j75
pod "nginx-deploy-75578bc7f7-47cqt" deleted
pod "nginx-deploy-75578bc7f7-78j75" deleted
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-75578bc7f7-8zz8h 1/1 Running 0 33s 10.244.0.11 docker-54 <none> <none>
nginx-deploy-75578bc7f7-xvnfm 1/1 Running 0 33s 10.244.1.81 docker-55 <none> <none>
[root@docker-54 ~]#
可以看到,确实在 Master 节点去掉污点之后,删除 Pod 重新调度,发现可以调度到 Master 节点上。
在没有删除 Master 节点的污点的时候,由于 该 Pod 没有配置容忍,即使带有亲和力的标签,也不会被调度到 Master 节点上,然而在删除 Master 节点的污点之后,由于符合亲和力的必备标签,所以有可能会被调度选中。
6. Pod 间亲和性与反亲和性
Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 可以调度到的节点,而不是基于节点上的标签。
Pod 间亲和性与反亲和性的规则格式为"如果 X 上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 上"。 这里的 X 可以是节点、机架、云提供商可用区或地理区域或类似的拓扑域, Y 则是 Kubernetes 尝试满足的规则。
你通过标签选择算符 的形式来表达规则(Y),并可根据需要指定选关联的名字空间列表。 Pod
在 Kubernetes
中是名字空间作用域的对象,因此 Pod
的标签
也隐式地具有名字空间属性
。 针对 Pod 标签的所有标签选择算符都要指定名字空间,Kubernetes
会在指定的名字空间内寻找标签。
你会通过 topologyKey
来表达拓扑域(X)的概念,其取值是系统用来标示域的节点标签键。 相关示例可参见常用标签、注解和污点。
说明:
Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。 我们不建议在包含数百个节点的集群中使用这类设置。
说明:Pod 反亲和性需要节点上存在一致性的标签。换言之, 集群中每个节点都必须拥有与
topologyKey
匹配的标签。 如果某些或者所有节点上不存在所指定的topologyKey
标签,调度行为可能与预期的不同。
7. Pod 间亲和性与反亲和性的类型
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
可以使用 requiredDuringSchedulingIgnoredDuringExecution
亲和性来告诉调度器, 将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。
类似地,可以使用 preferredDuringSchedulingIgnoredDuringExecution
反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中。
要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity
字段。 对于 Pod 间反亲和性,可以使用 Pod 规约中的 .affinity.podAntiAffinity
字段。
8. 调度一组具有 Pod 间亲和性的 Pod
如果当前正被调度的 Pod 在具有自我亲和性的 Pod 序列中排在第一个, 那么只要它满足其他所有的亲和性规则,它就可以被成功调度。 这是通过以下方式确定的:确保集群中没有其他 Pod 与此 Pod 的名字空间和标签选择算符匹配; 该 Pod 满足其自身定义的条件,并且选定的节点满足所指定的所有拓扑要求。 这确保即使所有的 Pod 都配置了 Pod 间亲和性,也不会出现调度死锁的情况。
Pod 亲和性示例
考虑下面的 Pod 规约:pods/pod-with-pod-affinity.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity: # 定义亲和性规则
podAffinity: # 定义Pod亲和性规则
requiredDuringSchedulingIgnoredDuringExecution: # 定义在调度时必须满足的Pod亲和性规则
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone # 指定拓扑域的标签键 也就是 依赖的 Pod 所在的节点必须要有这个标签才行
podAntiAffinity: # 定义Pod反亲和性规则
preferredDuringSchedulingIgnoredDuringExecution: # 定义优选的Pod反亲和性规则,即首选但不是必需的
- weight: 100 # 定义优选规则的权重为100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: registry.k8s.io/pause:2.0
上面示例定义了一条 Pod 亲和性
规则和一条 Pod 反亲和性
规则。Pod 亲和性规则配置为 requiredDuringSchedulingIgnoredDuringExecution
,而 Pod 反亲和性配置为 preferredDuringSchedulingIgnoredDuringExecution
。
亲和性规则规定,只有节点属于特定的 区域 且该区域中的其他 Pod 已打上 security=S1
标签时,调度器才可以将示例 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(称之为 "Zone V")的集群,此区域由带有 topology.kubernetes.io/zone=V
标签的节点组成,那么只要 Zone V 内已经至少有一个
Pod 打了 security=S1
标签, 调度器就可以将此 Pod 调度到 Zone V 内的任何节点
。相反,如果 Zone V 中没有带有 security=S1
标签的 Pod, 则调度器不会将示例 Pod 调度给该区域中的任何节点
。
反亲和性规则规定,如果节点属于特定的 区域 且该区域中的其他 Pod 已打上 security=S2
标签,则调度器应尝试避免将 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(我们称之为 "Zone R")的集群,此区域由带有 topology.kubernetes.io/zone=R
标签的节点组成,只要 Zone R 内已经至少有一个
Pod 打了 security=S2
标签, 调度器应避免将 Pod 分配给 Zone R 内的任何节点。相反,如果 Zone R 中没有带有 security=S2
标签的 Pod, 则反亲和性规则不会影响将 Pod 调度到 Zone R。
通过这样的配置,Kubernetes
调度器将会优先考虑满足必需的Pod亲和性规则,如果没有满足的节点,则会考虑首选的Pod反亲和性规则。
可以针对 Pod 间亲和性与反亲和性为其 operator
字段使用 In
、NotIn
、Exists
、 DoesNotExist
等值。
原则上,topologyKey
可以是任何合法的标签键。出于性能和安全原因,topologyKey
有一些限制:
对于 Pod 亲和性
而言,在 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
不允许为空。
对于 requiredDuringSchedulingIgnoredDuringExecution
要求的 Pod 反亲和性, 准入控制器 LimitPodHardAntiAffinityTopology
要求 topologyKey
只能是 kubernetes.io/hostname
。如果你希望使用其他定制拓扑逻辑, 你可以更改准入控制器或者禁用之。
除了 labelSelector
和 topologyKey
,你也可以指定 labelSelector
要匹配的名字空间列表,方法是在 labelSelector
和 topologyKey
所在层同一层次上设置 namespaces
。 如果 namespaces
被忽略或者为空,则默认为 Pod 亲和性/反亲和性的定义所在的名字空间。
9. 验证 Pod 亲和性
按照上面的配置文件,这里先将 Master节点打上topology.kubernetes.io/zone=R
标签,给从节点打上topology.kubernetes.io/zone=V
标签。
bash
[root@docker-54 ~]# kubectl label no docker-54 topology.kubernetes.io/zone=R
node/docker-54 labeled
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl label no docker-55 docker-56 topology.kubernetes.io/zone=V
node/docker-55 labeled
node/docker-56 labeled
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl get no --show-labels
NAME STATUS ROLES AGE VERSION LABELS
docker-54 Ready control-plane,master 202d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-54,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=,topology.kubernetes.io/zone=R
docker-55 Ready <none> 202d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-55,kubernetes.io/os=linux,label-1=key-1,topology.kubernetes.io/zone=V,type=microservice
docker-56 Ready <none> 202d v1.22.6 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=docker-56,kubernetes.io/os=linux,label-2=key-2,topology.kubernetes.io/zone=V,type=microservice
[root@docker-54 ~]#
接着新建 s2 标签的配置文件:s2-nginx-deploy-affinity.yaml
bash
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy-s2 # deployment 的名字
namespace: default # 所在的命名空间
spec:
replicas: 1 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
topology.kubernetes.io/zone: V
security: S2
spec: # pod 期望信息
nodeSelector:
kubernetes.io/hostname: docker-54
containers: # pod 的容器
- image: nginx:1.7.9 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
这里上面配置的意思是部署一个 Nginx 的 Pod,使用标签选择器,部署到 docker-54 节点上,并且这个 Pod 带有两个后面测试 Pod 亲和力用到的标签:topology.kubernetes.io/zone: V
和 security: S2
。
直接创建这个 deployment
看下,使用节点选择器直接调度到 Master
节点上,应该不会成功,因为我这里还没有处理 Master 节点上的污点,看下效果:
bash
[root@docker-54 deployments]# kubectl create -f s2-nginx-deploy-affinity.yaml
deployment.apps/nginx-deploy-s2 created
[root@docker-54 deployments]#
[root@docker-54 ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-75578bc7f7-8zz8h 1/1 Running 11 (46m ago) 11h 10.244.0.11 docker-54 <none> <none>
nginx-deploy-75578bc7f7-xvnfm 1/1 Running 11 (46m ago) 11h 10.244.1.81 docker-55 <none> <none>
nginx-deploy-s2-5fd5bf9d5c-52d2k 0/1 Pending 0 19s <none> <none> <none> <none>
[root@docker-54 ~]#
[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-75578bc7f7-8zz8h 1/1 Running 11 (50m ago) 11h 10.244.0.11 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-75578bc7f7-xvnfm 1/1 Running 11 (50m ago) 11h 10.244.1.81 docker-55 <none> <none> app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-s2-5fd5bf9d5c-52d2k 0/1 Pending 0 5m2s <none> <none> <none> <none> app=nginx-deploy,pod-template-hash=5fd5bf9d5c,security=S2,topology.kubernetes.io/zone=V
[root@docker-54 deployments]#
可以看到 Pod 的状态为 Pending,接着看下事件:
bash
[root@docker-54 ~]# kubectl describe po nginx-deploy-s2-5fd5bf9d5c-52d2k
Name: nginx-deploy-s2-5fd5bf9d5c-52d2k
Namespace: default
// 省略中间内容
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 31s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match Pod's node affinity/selector.
[root@docker-54 ~]#
可以看到,上面显示了默认调度 3 个节点,没有一个可用,其中一个节点带有污点{node-role.kubernetes.io/master: }
,并且这个 Pod 没有配置容忍,所以不能调度到它上面,然后另外两个节点不包含 Pod 的节点亲和力标签。
接着来暂时去掉 Master 节点的污点,看下 Pod 能不能正常调度到 Master 节点上:
bash
[root@docker-54 ~]# kubectl describe no docker-54 | grep Taint
Taints: node-role.kubernetes.io/master:NoSchedule
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl taint no docker-54 node-role.kubernetes.io/master:NoSchedule-
node/docker-54 untainted
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl describe no docker-54 | grep Taint
Taints: <none>
[root@docker-54 ~]#
接着再次看下 Pod 状态:
bash
[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-75578bc7f7-8zz8h 1/1 Running 11 (55m ago) 11h 10.244.0.11 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-75578bc7f7-xvnfm 1/1 Running 11 (55m ago) 11h 10.244.1.81 docker-55 <none> <none> app=nginx-deploy,pod-template-hash=75578bc7f7
nginx-deploy-s2-5fd5bf9d5c-52d2k 1/1 Running 0 9m46s 10.244.0.12 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=5fd5bf9d5c,security=S2,topology.kubernetes.io/zone=V
[root@docker-54 deployments]#
可以看到 nginx-deploy-s2
的 Pod 被调度到 Maser 节点上了。这里不知道是不是 K8s 的bug,明明对节点 docker-54
打了 topology.kubernetes.io/zone=R
的标签,这个 Pod 竟然能调度到这个节点上。
然后我试了下将配置文件中 Pod 模板的标签选择器,配置成topology.kubernetes.io/zone=R
发现还是可以正常调度到 docker-54
节点。那是不是意味着 k8s 在调度时,如果仅仅配置了标签选择器,就会存在一个 BUG,就是仅比对了标签的key,没有比对标签的 value,也就是污点里面判断时用到的 Exists
的功能。这三个节点上都包含 topology.kubernetes.io/zone
的标签,虽然值不一样。
接着新建一个deploy,配置 Pod 带有 S1 的标签:s1-nginx-deploy-affinity.yaml
bash
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy-s1 # deployment 的名字
namespace: default # 所在的命名空间
spec:
replicas: 2 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
topology.kubernetes.io/zone: V
security: S1
spec: # pod 期望信息
containers: # pod 的容器
- image: nginx:1.7.9 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
可以看到这个配置的 Pod 的标签是 topology.kubernetes.io/zone: V
和 security: S1
,刚好对应我们的两个从节点的标签,启动看下是否会调度到两个从节点上:
bash
[root@docker-54 deployments]# kubectl create -f s1-nginx-deploy-affinity.yaml
deployment.apps/nginx-deploy-s1 created
[root@docker-54 deployments]#
[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-s1-7bbd9fbdb7-25627 1/1 Running 0 2s 10.244.0.19 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s1-7bbd9fbdb7-bqwm4 1/1 Running 0 2s 10.244.2.127 docker-56 <none> <none> app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s2-df8df55b9-f8cb6 1/1 Running 0 79s 10.244.0.18 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=df8df55b9,security=S2,topology.kubernetes.io/zone=R
[root@docker-54 deployments]#
理想情况下,s1 的 deploy 的两个 Pod 应该被调度到两个带有 topology.kubernetes.io/zone=V
的标签的从节点上,但是!!!事实情况却不是这样:
bash
[root@docker-54 deployments]# kubectl get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-s1-7bbd9fbdb7-25627 1/1 Running 0 2s 10.244.0.19 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s1-7bbd9fbdb7-bqwm4 1/1 Running 0 2s 10.244.2.127 docker-56 <none> <none> app=nginx-deploy,pod-template-hash=7bbd9fbdb7,security=S1,topology.kubernetes.io/zone=V
nginx-deploy-s2-df8df55b9-f8cb6 1/1 Running 0 79s 10.244.0.18 docker-54 <none> <none> app=nginx-deploy,pod-template-hash=df8df55b9,security=S2,topology.kubernetes.io/zone=R
[root@docker-54 deployments]#
其中一个节点死活就是不去 docker-55 节点上,就非得赖上 docker-54 节点了。
我还特意检查了下 docker-55 节点上是不是配置了污点什么的:
bash
[root@docker-54 ~]# kubectl describe no docker-55 | grep Taints
Taints: <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl describe no docker-54 | grep Taints
Taints: <none>
[root@docker-54 ~]#
[root@docker-54 ~]# kubectl describe no docker-56 | grep Taints
Taints: <none>
[root@docker-54 ~]#
多次删除 deploy ,还是这样,玩不下去了。。。。。。。
就这吧,辣鸡标签选择器,果然仅用他来控制调度不靠谱,怪不得有污点和亲和力的功能的出现,不争气的东西。。。。。