亲和性和反亲和性的含义
首先在介绍拓扑分布约束之前,我们要知道 pod 的亲和性和反亲和性的概念,pod 的亲和性的本质都是 堆叠或打散 ,podAffinity
和 podAntiAffinity
两个特性对 Pod 处于不同disk=ssd的分布进行了一些控制
-
podAffinity
就是将一些 pod 调度在同一拓扑域之中,是堆叠的体现 -
podAntiAffinity
就是将 pod 调度在不同的拓扑域之中,使之只能被调度一个或者只能存在一个,是打散的体现
# 拓扑域的概念:拓扑域就是匹配节点的标签的 key 值,一般情况我们可以 explain 查看配置信息
# 示例
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname # node节点标签带有 kubernetes.io/hostname 为一个拓扑域
拓扑域小练习:
k8s 集群分为磁盘为 ssd 和 hdd 两种规格的节点,通过做 反亲和 硬策略 判断是否可以部署三个实例,node-1 和 node-2 为 ssd 节点,node-3 和node-4 为 hdd 规格
[root@master-1 PodTop]# kubectl label nodes node-1 disk=ssd
node/node-1 labeled
[root@master-1 PodTop]# kubectl label nodes node-2 disk=ssd
node/node-2 labeled
[root@master-1 PodTop]# kubectl label nodes node-3 disk=hdd
node/node-3 labeled
[root@master-1 PodTop]# kubectl label nodes node-4 disk=hdd
node/node-4 labeled
[root@master-1 PodTop]# kubectl get nodes -l disk=ssd
NAME STATUS ROLES AGE VERSION
node-1 Ready worker 53d v1.21.5
node-2 Ready worker 53d v1.21.5
[root@master-1 PodTop]# kubectl get nodes -l disk=hdd
NAME STATUS ROLES AGE VERSION
node-3 Ready worker 53d v1.21.5
node-4 Ready worker 53d v1.21.5
反亲和示例 yaml
# podaffinity.yaml
apiVersion apps/v1
kind Deployment
metadata
name nginx
labels
app nginx
spec
replicas2
selector
matchLabels
app nginx
template
metadata
labels
app nginx
spec
containers
name nginx
image nginx
ports
containerPort80
name nginxweb
affinity
podAntiAffinity
requiredDuringSchedulingIgnoredDuringExecution# 硬策略
labelSelector
matchExpressions
key app
operator In
values
nginx
topologyKey disk # 通过 disk 标签来判断不同的拓扑域
执行判断,查看示例部署节点
# 实例数量为 2 时,可以正常分布在 node-1 和 node-4 节点
[root@master-1 PodTop]# kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-6ddd9f758b-mslxd 0/1 ContainerCreating 0 16s <none> node-1 <none> <none>
nginx-6ddd9f758b-tj2kd 0/1 ContainerCreating 0 16s <none> node-4 <none> <none>
# 扩容至 3,查看分布情况,发现第三个实例为 Pending 状态
[root@master-1 PodTop]# kubectl scale deployment nginx --replicas=3
deployment.apps/nginx scaled
[root@master-1 PodTop]# kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-6ddd9f758b-dgrnx 0/1 Pending 0 2s <none> <none> <none> <none>
nginx-6ddd9f758b-mslxd 1/1 Running 0 51s 10.233.112.5 node-1 <none> <none>
nginx-6ddd9f758b-tj2kd 1/1 Running 0 51s 10.233.93.105 node-4 <none> <none>
# 通过 describe 查看 pending 的事件信息
# 提示我们,5个node 节点均不可用, 1个为master 有污点,另外4个node 不能满足反亲和的规则
# 因为 node-1 和 node-2 为同一个 `ssd` 拓扑域
# node-3 和 node-4 为同一个 `hdd` 拓扑域
# 反亲和硬策略是相同的拓扑域之间只能有一个实例存在,这是打散的体现,所以无法调度第三个实例
[root@master-1 PodTop]# kubectl describe pod nginx-6ddd9f758b-dgrnx
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 38s (x6 over 4m56s) default-scheduler 0/5 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 4 node(s) didn't match pod affinity/anti-affinity rules, 4 node(s) didn't match pod anti-affinity rules.
拓扑分布约束
通过上述实验,我们发现反亲和特性虽然可以将我们的不同实例调度到不同的拓扑域,但是并不能满足我们同一个拓扑域内的容灾和高可用,因为策略限制,只能存在一个实例。
那么没有解决办法了吗?
当然不是,k8s 还有一个 拓扑分布约束 topologySpreadConstraints
的概念,官方解释如下:
可以使用 *拓扑分布约束(Topology Spread Constraints)* 来控制 Pods 在集群内故障域之间的分布,例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。
pod.spec.topologySpreadConstraints
字段定义如下所示:
apiVersion v1
kind Pod
metadata
name mypod
spec
topologySpreadConstraints
maxSkew <integer>
topologyKey <string>
whenUnsatisfiable <string>
labelSelector <object>
你可以定义一个或多个 topologySpreadConstraint
来指示 kube-scheduler 如何根据与现有的 Pod 的关联关系将每个传入的 Pod 部署到集群中。字段包括:
- maxSkew
描述 Pod 分布不均的程度。这是给定拓扑类型中任意两个拓扑域中匹配的 pod 之间的最大允许差值。它必须大于零。取决于whenUnsatisfiable
的 取值,其语义会有不同。
- 当
whenUnsatisfiable
等于 "DoNotSchedule" 时,maxSkew
是目标拓扑域 中匹配的 Pod 数与全局最小值之间可存在的差异,硬策略。 - 当
whenUnsatisfiable
等于 "ScheduleAnyway" 时,调度器会更为偏向能够降低偏差值的拓扑域,软策略。 - topologyKey是节点标签的键。如果两个节点使用此键标记并且具有相同的标签值, 则调度器会将这两个节点视为处于同一拓扑域中。调度器试图在每个拓扑域中放置数量 均衡的 Pod。
- whenUnsatisfiable
指示如果 Pod 不满足分布约束时如何处理:
-
DoNotSchedule
(默认)告诉调度器不要调度,硬策略。 -
ScheduleAnyway
告诉调度器仍然继续调度,只是根据如何能将偏差最小化来对节点进行排序,软策略。 - labelSelector用于查找匹配的 pod。匹配此标签的 Pod 将被统计,以确定相应 拓扑域中 Pod 的数量。
拓扑分布约束练习
和上述练习题保持一致,node-1 和 node-2 为 ssd 节点,node-3 和node-4 为 hdd 规格,将 反亲和硬策略 修改为 拓扑分布约束,查看结果
# spread.yaml
apiVersion apps/v1
kind Deployment
metadata
name nginx
labels
app nginx
spec
replicas2
selector
matchLabels
app nginx
template
metadata
labels
app nginx
spec
containers
name nginx
image nginx
ports
containerPort80
name nginxweb
topologySpreadConstraints
maxSkew 1 # 不同拓扑域之间的 pod 的最大差值
topologyKey disk # 匹配拓扑域
whenUnsatisfiable DoNotSchedule # 默认硬策略
labelSelector
matchLabels
app nginx # 匹配 pod
查看 部署节点分布情况
[root@master-1 PodTop]# kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5555cdd6d6-mftrt 1/1 Running 0 21s 10.233.112.33 node-1 <none> <none>
nginx-5555cdd6d6-t2kwl 1/1 Running 0 20s 10.233.93.106 node-4 <none> <none>
# 扩容为 4节点,发现可以进行分布,但是分布并不均匀,不能同等分布到未部署的节点
# 这是因为在同一个拓扑域内是通过调度器随机分布,我们没有设置亲和性,所以导致同一个拓扑域内的分布并不是均匀的
# 要想均匀分布,我们可以通过叠加 podAffinity 或者 nodeAffinity 即可
[root@master-1 PodTop]# kubectl scale deployment nginx --replicas=4
deployment.apps/nginx scaled
[root@master-1 PodTop]# kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5555cdd6d6-9s6pr 1/1 Running 0 6m 10.233.112.7 node-1 <none> <none>
nginx-5555cdd6d6-mdmsw 1/1 Running 0 6m 10.233.93.107 node-4 <none> <none>
nginx-5555cdd6d6-mftrt 1/1 Running 0 6m38s 10.233.112.33 node-1 <none> <none>
nginx-5555cdd6d6-t2kwl 1/1 Running 0 6m37s 10.233.93.106 node-4 <none> <none>
- ##### 4实例 均匀分布在 4node 示例1:
# spread.yaml
apiVersion apps/v1
kind Deployment
metadata
name nginx
labels
app nginx
spec
replicas4
selector
matchLabels
app nginx
template
metadata
labels
app nginx
spec
containers
name nginx
image nginx
ports
containerPort80
name nginxweb
topologySpreadConstraints
maxSkew1
topologyKey disk
whenUnsatisfiable DoNotSchedule
labelSelector
matchLabels
app nginx
affinity
podAntiAffinity
requiredDuringSchedulingIgnoredDuringExecution# 硬策略
labelSelector
matchExpressions
key app
operator In
values
nginx
topologyKey kubernetes.io/hostname # 需要指定拓扑域和上述同
查看分布情况:
[root@master-1 PodTop]# kubectl get pod -o wide -l app=nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-65f888cfd-8g7l7 1/1 Running 0 16s 10.233.109.212 node-3 <none> <none>
nginx-65f888cfd-hfxn7 1/1 Running 0 16s 10.233.93.108 node-4 <none> <none>
nginx-65f888cfd-n6jh4 1/1 Running 0 16s 10.233.112.20 node-1 <none> <none>
nginx-65f888cfd-qmv4z 1/1 Running 0 16s 10.233.69.91 node-2 <none> <none>
- ##### 4 实例 均匀分布在 4node 示例2,可以拓展为 8 实例 均匀分布在 4节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 8
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
# tolerations:
# - key: "node-role.kubernetes.io/master"
# operator: "Exists"
# effect: "NoSchedule"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: hostname # 特定实例的标签的 key,作为拓扑域
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: nginx
查看 pod 分布情况,并扩容至 8实例
# 每个节点配置一个独立的标签
[root@master-1 PodTop]# kubectl label nodes node-1 hostname=node-1
[root@master-1 PodTop]# kubectl label nodes node-2 hostname=node-2
[root@master-1 PodTop]# kubectl label nodes node-3 hostname=node-3
[root@master-1 PodTop]# kubectl label nodes node-4 hostname=node-4
# 然后使用8各节点进行测试
查看 pod 分布情况
# 可以看到每个 node 都分布了两个实例
root@master-1 PodTop # kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5f977cc8c4-4xxkn 1/1 Running 0 112s 10.233.109.231 node-3 <none> <none>
nginx-5f977cc8c4-5lkpw 1/1 Running 0 112s 10.233.112.38 node-1 <none> <none>
nginx-5f977cc8c4-67znz 1/1 Running 0 112s 10.233.93.114 node-4 <none> <none>
nginx-5f977cc8c4-blwzh 1/1 Running 0 112s 10.233.109.230 node-3 <none> <none>
nginx-5f977cc8c4-hdbzn 1/1 Running 0 112s 10.233.112.41 node-1 <none> <none>
nginx-5f977cc8c4-kx4t4 1/1 Running 0 112s 10.233.93.118 node-4 <none> <none>
nginx-5f977cc8c4-rg7bg 1/1 Running 0 112s 10.233.69.131 node-2 <none> <none>
nginx-5f977cc8c4-sc9ph 1/1 Running 0 112s 10.233.69.125 node-2 <none> <none>
# 扩容到 12 实例,也会均匀分布在四个节点上,即每个节点有三个实例
root@master-1 PodTop # kubectl scale deployment nginx --replicas=12
root@master-1 PodTop # kubectl get pod -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5f977cc8c4-4xxkn 1/1 Running 0 4m20s 10.233.109.231 node-3 <none> <none>
nginx-5f977cc8c4-5lkpw 1/1 Running 0 4m20s 10.233.112.38 node-1 <none> <none>
nginx-5f977cc8c4-67znz 1/1 Running 0 4m20s 10.233.93.114 node-4 <none> <none>
nginx-5f977cc8c4-98hsh 1/1 Running 0 16s 10.233.69.128 node-2 <none> <none>
nginx-5f977cc8c4-blwzh 1/1 Running 0 4m20s 10.233.109.230 node-3 <none> <none>
nginx-5f977cc8c4-hdbzn 1/1 Running 0 4m20s 10.233.112.41 node-1 <none> <none>
nginx-5f977cc8c4-k9m8r 1/1 Running 0 16s 10.233.93.119 node-4 <none> <none>
nginx-5f977cc8c4-kx4t4 1/1 Running 0 4m20s 10.233.93.118 node-4 <none> <none>
nginx-5f977cc8c4-rg7bg 1/1 Running 0 4m20s 10.233.69.131 node-2 <none> <none>
nginx-5f977cc8c4-s8lrr 1/1 Running 0 15s 10.233.109.228 node-3 <none> <none>
nginx-5f977cc8c4-sc9ph 1/1 Running 0 4m20s 10.233.69.125 node-2 <none> <none>
nginx-5f977cc8c4-srvzq 1/1 Running 0 16s 10.233.112.44 node-1 <none> <none>
总结:
通过拓扑分布约束,我们可以灵活的将我们的实例达到 DaemonSet
类型的分布模式,并可以使每个 node 节点具有多实例,类似的 replicas=2 的 DaemonSet
类型的容灾和高可用的目的,这样我们的应用更具有健壮性。