k8s之pod亲和性与反亲和性的topologyKey
k8s中pod亲和性和反亲和性
实战指定pod分散部署节点之pod反亲和性(podAntiAffinity)
k8s调度之亲和/反亲和
- 要有选择中的 Pod —— 才可以说是 Pod 间 —— 也就是要有 labelSelector(若没有设置此字段,表示没有选中的 Pod)
- 经过 labelSelector 和 topologyKey 的筛选,会得到一些满足条件的 Pod
- topologyKey 可以理解为 Node 的 label
- 比如默认所有节点都会有
kubernetes.io/hostname
这 label,相应的值为节点名称,如 master01 节点的 label 为kubernetes.io/hostname=master01
,这种情况每个节点对应的值都不同- topologyKey 必选字段 —— 规定可以调度的范围
- 假如集群内有一半节点具有
zone=nodename
标签,另一半节点不具备zone
标签- 那么若亲和性或反亲和性中配置
topologyKey =zone
,就会调度将新的 Pod 调度到具有zone=nodename
标签的节点范围中
- 亲和性:就是将新 Pod 调度到 labelSelector 选中的得分高 Pod 所在 Node(该节点具有
zone
label)- 反亲和性:就是将新 Pod 调度到 labelSelector 选中所有 Pod 之外的
zone
label 节点
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
affinity:
# 首先根据 labelSelector 选择具有 service.cpaas.io/name: deployment-nginx Label 的 所有 Pod
# 接下来根据 podAffinity 亲和性,将此 pod 调度到与选中 Pod 中具有 topologyKey 的 Node 上
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
service.cpaas.io/name: deployment-nginx
topologyKey: kubernetes.io/hostname
# 首先根据 labelSelector 选择具有 key 为 a ,value为 b 或 c 的 Label 的 Pod
# 接下来根据 podAntiAffinity,将此 pod 调度到与选中 Pod 中都不相同的 Node 上,该节点需要具有 topologyKey label
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: a
operator: In
values: ["b", "c"]
topologyKey: kubernetes.io/hostname
containers:
- name: test-pod
image: nginx:1.18
字段 | 含义 | |
---|---|---|
podAffinity | Pod 间的亲和性定义 | |
podAntiAffinity | Pod 间的反亲和性定义 | |
requiredDuringSchedulingIgnoredDuringExecution | 硬性要求,必须满足条件,保证分散部署的效果最好使用用此方式 | |
preferredDuringSchedulingIgnoredDuringExecution | 软性要求,可以不完全满足,即有可能同一node上可以跑多个副本 | |
requiredDuringSchedulingIgnoredDuringExecution | labelSelector | |
topologyKey | ||
preferredDuringSchedulingIgnoredDuringExecution | weight | |
podAffinityTerm | labelSelector | |
topologyKey | ||
topologyKey | 可以理解为 Node 的 Label,具有相同的 Label 的 Node,视为同一拓扑 | |
如三个节点打上 Label : - Node1 —— zone:beijing - Node2 —— zone:shanghai - Node3 —— zone:beijing 那么 Node1 和 Node3 为同一拓扑,Node2 为另一拓扑 |
||
topologyKey: kubernetes.io/hostname 上面为常见的配置,可以通过 kubectl get nodes --show-labels 看到节点上的 Lable,就具有此 kubernetes.io/hostname Label因此就是将每个节点,作为一个独立的拓扑 |
# 配置如下,只需要修改label的配置,即matchExpressions中的key和values的值
# 硬性要求
# 如果节点上的pod标签存在满足app=nginx,则不能部署到节点上
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
# 软性要求
# 如果节点上的pod标签存在满足app=nginx,也可以部署到节点上,尽可能先部署到其它节点,如果没有满足也可以部署到此节点(大概是这么理解吧)
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
pod亲和性调度需要各个相关的pod对象运行于"同一位置", 而反亲和性调度则要求他们不能运行于"同一位置",
这里指定“同一位置” 是通过 topologyKey 来定义的,topologyKey 对应的值是 node 上的一个标签名称,比如各别节点zone=A标签,各别节点有zone=B标签,pod affinity topologyKey定义为zone,那么调度pod的时候就会围绕着A拓扑,B拓扑来调度,而相同拓扑下的node就为“同一位置”。
topology 就是 拓扑 的意思,这里指的是一个 拓扑域,是指一个范围的概念,比如一个 Node、一个机柜、一个机房或者是一个地区(如杭州、上海)等,实际上对应的还是 Node 上的标签。这里的 topologyKey 对应的是 Node 上的标签的 Key(没有Value),可以看出,其实 topologyKey 就是用于筛选 Node 的。通过这种方式,我们就可以将各个 Pod 进行跨集群、跨机房、跨地区的调度了。
原则上,topologyKey 可以是任何合法的标签 Key。但是出于性能和安全原因,对 topologyKey 有一些限制:
requiredDuringSchedulingIgnoredDuringExecution
的 Pod 反亲和性,topologyKey 不能为空。requiredDuringSchedulingIgnoredDuringExecution
的 Pod 反亲和性,引入 LimitPodHardAntiAffinityTopology
准入控制器来限制 topologyKey 只能是 kubernetes.io/hostname
。如果要使用自定义拓扑域,则可以修改准入控制器,或者直接禁用它。preferredDuringSchedulingIgnoredDuringExecution
的 Pod 反亲和性,空的 topologyKey 表示所有拓扑域。截止 v1.12
版本,所有拓扑域还只能是 kubernetes.io/hostname
、failure-domain.beta.kubernetes.io/zone
和 failure-domain.beta.kubernetes.io/region
的组合。目标
- 现在集群有 3 个 Node(master、node01、node02)
- 测试通过【反亲和性】和【requiredDuringSchedulingIgnoredDuringExecution】硬性要求,是否可以将 4 个 Pod 调度到不同 Node 上
预计情况
- 三个 Pod 可以正常调度,分别调度到三个节点上
- 第四个 Pod 无法完成调度
# 第一个 Pod
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-1
name: dfy-po-1
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
-----
# 第二个 Pod
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-2
name: dfy-po-2
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-1
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-2
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
-----
# 第三个 Pod
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-3
name: dfy-po-3
spec:
affinity:
podAntiAffinity:
# 注意 多个 label 需要分组
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-1
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-2
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-3
-----
# 第四个 Pod
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-4
name: dfy-po-4
spec:
affinity:
podAntiAffinity:
# 可以看出这种分组写法,比较麻烦,因此可以考虑创建这些 Pod 时,具有相同的 Label,比如 run: dfy-anti,这样只需要写一条了
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-1
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-2
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-3
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-4
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
----
# 精简创建法
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-anti # 同一具有此 label
name: dfy-po-1 # 创建 4 个 Pod ,只需要改动名称即可
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-anti # 统一 label
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
# 总结:
# 1. 精简创建法,对于自己统一创建的资源(如 Pod)更方便
# 2. 复杂创建法(多个 labelSelector),对于【反亲和】别人创建的资源(如 Pod) 更方便
结果:
# 可以看到由于 反亲和性,第 4 个 Pod 无法调度成功
[root@galaxyoa1-master1 dfy]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dfy-po-1 1/1 Running 0 100m 200.20.10.1 node2 <none> <none>
dfy-po-2 1/1 Running 0 94m 200.20.203.23 node1 <none> <none>
dfy-po-3 1/1 Running 0 6m5s 200.21.176.5 master1 <none> <none>
dfy-po-4 0/1 Pending 0 4s <none> <none> <none> <none>
# 4 个 Pod 都是这种形式,requiredDuringSchedulingIgnoredDuringExecution 不写 labelSelector
# 测试结果:4 个 Pod 都可以正常部署,某个 Node 上会存在 2 个 Pod
# 原因:因为不具有 labelSelector,因此没有选中 Pod,【反亲和性】就不起作用了,这种情况就和普通调度一个 Pod 一样
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-1
name: dfy-po-1 # dfy-po-2 dfy-po-3 dfy-po-4
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
# 第三个 Pod 的部署 —— 错误做法
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-3
name: dfy-po-3
spec:
affinity:
podAntiAffinity:
# 未分组 不生效
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-1
run: dfy-po-2
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-3
# 第三个 Pod 的部署 —— 正确做法
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-3
name: dfy-po-3
spec:
affinity:
podAntiAffinity:
# 注意 多个 label 需要分组
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-1
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
run: dfy-po-2
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-3
# 前提有三个节点,三个 Pod 分别部署到各个节点了
# 此 Pod 会调度到 该三个 Pod 中随机一个的所在节点上
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-4
name: dfy-po-4
spec:
affinity:
podAntiAffinity:
# 注意 多个 label 需要分组
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
stablemodelAffinityTerm:
labelSelector:
matchExpressions:
- key: run
operator: In
values: ["dfy-po-1","dfy-po-2","dfy-po-3"]
topologyKey: kubernetes.io/hostname
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-4
# 此 Pod 会调度到 反亲和程度最低的 dfy-po-1
apiVersion: v1
kind: Pod
metadata:
labels:
run: dfy-po-4
name: dfy-po-4
spec:
affinity:
podAntiAffinity:
# 注意 多个 label 需要分组
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 20
stablemodelAffinityTerm:
labelSelector:
matchExpressions:
- key: run
operator: In
values: ["dfy-po-1"]
topologyKey: kubernetes.io/hostname
- weight: 30
stablemodelAffinityTerm:
labelSelector:
matchExpressions:
- key: run
operator: In
values: ["dfy-po-2"]
topologyKey: kubernetes.io/hostname
- weight: 50
stablemodelAffinityTerm:
labelSelector:
matchExpressions:
- key: run
operator: In
values: ["dfy-po-3"]
topologyKey: kubernetes.io/hostname
containers:
- image: galaxy.harbor.io/library/nginx:1.20.1-alpine-cst
name: dfy-po-4