实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.22.2
containerd://1.5.5
链接:https://pan.baidu.com/s/1CAC9j5yU-sg-aDfLPYVflg?pwd=b3jp
提取码:b3jp
2022.2.19-40.拓扑分布约束-实验代码
⚠️
这个理解起来稍微有些复杂;
这个可能用的不多,但如果你想进行一些细粒度的控制的话,用这个还是很不错的;
在 k8s 集群调度中,亲和性相关的概念本质上都是控制 Pod 如何被调度 – 堆叠或打散。
podAffinity
以及 podAntiAffinity
两个特性对 Pod 在不同拓扑域的分布进行了一些控制。
podAffinity
可以将无数个 Pod 调度到特定的某一个拓扑域,这是堆叠的体现;
podAntiAffinity
则可以控制一个拓扑域只存在一个 Pod,这是打散的体现;
但这两种情况都太极端了,在不少场景下都无法达到理想的效果,例如为了实现容灾和高可用,将业务 Pod 尽可能均匀的分布在不同可用区就很难实现。
PodTopologySpread(Pod 拓扑分布约束)
特性的提出正是为了对 Pod 的调度分布提供更精细的控制,以提高服务可用性以及资源利用率,PodTopologySpread
由 EvenPodsSpread
特性门所控制,在 v1.16 版本第一次发布,并在 v1.18 版本进入 beta 阶段默认启用。
在 Pod 的 Spec 规范中新增了一个 topologySpreadConstraints
字段即可配置拓扑分布约束,如下所示:
spec:
topologySpreadConstraints:
- maxSkew: > #这个属性理解起来不是那么地直接。。。;倾斜度;
topologyKey: >
whenUnsatisfiable: >
labelSelector:
由于这个新增的字段是在 Pod spec 层面添加,因此更高层级的控制 (Deployment、DaemonSet、StatefulSet) 也能使用 PodTopologySpread
功能。
让我们结合上图来理解 topologySpreadConstraints
中各个字段的含义和作用:
labelSelector
: 用来查找匹配的 Pod,我们能够计算出每个拓扑域中匹配该 label selector 的 Pod 数量,在上图中,假如 label selector 是 app:foo
,那么 zone1 的匹配个数为 2, zone2 的匹配个数为 0。topologyKey
: 是 Node label 的 key,如果两个 Node 的 label 同时具有该 key 并且值相同,就说它们在同一个拓扑域。在上图中,指定 topologyKey
为 zone, 则具有 zone=zone1
标签的 Node 被分在一个拓扑域,具有 zone=zone2
标签的 Node 被分在另一个拓扑域。maxSkew
: 这个属性理解起来不是很直接,它描述了 Pod在不同拓扑域中不均匀分布的最大程度(指定拓扑类型中任意两个拓扑域中匹配的 Pod 之间的最大允许差值),它必须大于零。每个拓扑域都有一个 skew 值,计算的公式是:skew[i] = 拓扑域[i]中匹配的 Pod 个数 - min{其他拓扑域中匹配的 Pod 个数}
。在上图中,我们新建一个带有app=foo
标签的 Pod:
maxSkew: 1
的约束(差值为1)whenUnsatisfiable
: 描述了如果 Pod 不满足分布约束条件该采取何种策略:
假设你拥有一个 4 节点集群,其中标记为 foo:bar
的 3 个 Pod 分别位于 node1、node2 和 node3 中:
如果希望新来的 Pod 均匀分布在现有的可用区域,则可以按如下设置其约束:
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
topologyKey: zone
意味着均匀分布将只应用于存在标签键值对为 zone:
的节点。 whenUnsatisfiable: DoNotSchedule
告诉调度器如果新的 Pod 不满足约束,则不可调度。如果调度器将新的 Pod 放入 “zoneA”,Pods 分布将变为 [3, 1]
,因此实际的偏差为 2(3 - 1)
,这违反了 maxSkew: 1
的约定。此示例中,新 Pod 只能放置在 “zoneB” 上:
或者
你可以调整 Pod 约束以满足各种要求:
maxSkew
更改为更大的值,比如 “2”,这样新的 Pod 也可以放在 “zoneA” 上。topologyKey
更改为 “node”,以便将 Pod 均匀分布在节点上而不是区域中。 在上面的例子中,如果 maxSkew
保持为 “1”,那么传入的 Pod 只能放在 “node4” 上。whenUnsatisfiable: DoNotSchedule
更改为 whenUnsatisfiable: ScheduleAnyway
, 以确保新的 Pod 可以被调度。上面是单个 Pod 拓扑分布约束的情况,下面的例子建立在前面例子的基础上来对多个 Pod 拓扑分布约束进行说明。假设你拥有一个 4 节点集群,其中 3 个标记为 foo:bar
的 Pod 分别位于 node1、node2 和 node3 上:
我们可以使用 2 个 TopologySpreadConstraint
来控制 Pod 在区域和节点两个维度上的分布:
# two-constraints.yaml
kind: Pod
apiVersion: v1
metadata:
name: mypod
labels:
foo: bar
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
- maxSkew: 1
topologyKey: node
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
foo: bar
containers:
- name: pause
image: k8s.gcr.io/pause:3.1
在这种情况下,为了匹配第一个约束,新的 Pod 只能放置在 “zoneB” 中;而在第二个约束中, 新的 Pod 只能放置在 “node4” 上,最后两个约束的结果加在一起,唯一可行的选择是放置 在 “node4” 上。
多个约束之间是可能存在冲突的,假设有一个跨越 2 个区域的 3 节点集群:
如果对集群应用 two-constraints.yaml
,会发现 “mypod” 处于 Pending
状态,这是因为为了满足第一个约束,“mypod” 只能放在 “zoneB” 中,而第二个约束要求 “mypod” 只能放在 “node2” 上,Pod 调度无法满足这两种约束,所以就冲突了。
为了克服这种情况,你可以增加 maxSkew
或修改其中一个约束,让其使用 whenUnsatisfiable: ScheduleAnyway
。
仔细观察可能你会发现我们并没有类似于 topologyValues
的字段来限制 Pod 将被调度到哪些拓扑去,默认情况会搜索所有节点并按 topologyKey
对其进行分组。有时这可能不是理想的情况,比如假设有一个集群,其节点标记为 env=prod
、env=staging
和 env=qa
,现在你想跨区域将 Pod 均匀地放置到 qa
环境中,是否可行?
答案是肯定的,我们可以结合 NodeSelector
或 NodeAffinity
一起使用,PodTopologySpread
会计算满足选择器的节点之间的传播约束。
如上图所示我们可以通过指定 spec.affinity.nodeAffinity
将搜索范围限制为 qa
环境,在该范围内 Pod 将被调度到一个满足 topologySpreadConstraints
的区域,这里就只能被调度到 zone=zone2
的节点上去了。
除了为单个 Pod 设置拓扑分布约束,也可以为集群设置默认的拓扑分布约束,默认拓扑分布约束在且仅在以下条件满足 时才会应用到 Pod 上:
.spec.topologySpreadConstraints
设置任何约束;你可以在 调度方案(Schedulingg Profile)中将默认约束作为 PodTopologySpread
插件参数的一部分来进行设置。 约束的设置采用和前面 Pod 中的规范一致,只是 labelSelector
必须为空。配置的示例可能看起来像下面这个样子:
apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
profiles:
- pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
defaultingType: List
现在我们再去解决上节课留下的一个问题 - 如果想在每个节点(或指定的一些节点)上运行2个(或多个)Pod 副本,如何实现?
这里以我们的集群为例,加上 master 节点一共有3个节点,每个节点运行2个副本,总共就需要6个 Pod 副本,要在 master 节点上运行,则同样需要添加容忍,如果只想在一个节点上运行2个副本,则可以使用我们的拓扑分布约束来进行细粒度控制,对应的资源清单如下所示:
#01-daemonset2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: topo-demo
spec:
replicas: 6
selector:
matchLabels:
app: topo
template:
metadata:
labels:
app: topo
spec:
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
name: ngpt
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: topo
这里我们重点需要关注的就是 topologySpreadConstraints
部分的配置,我们选择使用 kubernetes.io/hostname
为拓扑域,相当于就是3个节点都是独立的,maxSkew: 1
表示最大的分布不均匀度为1,所以只能出现的调度结果就是每个节点运行2个 Pod。
maxSkew=2这个相当于是软策略,不可取。
maxSkew=1这个满足需求。
maxSkew=1,这个是一轮轮往下调度的;(那么一般情况,maxSkew=1应该是比较常用的)
直接创建上面的资源即可验证:
$ kubectl apply -f 01-daemonset2.yaml
deployment.apps/topo-demo created
$ kubectl get po -l app=topo -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
topo-demo-6bbf65d967-ctf66 1/1 Running 0 43s 10.244.1.110 node1 <none> <none>
topo-demo-6bbf65d967-gkqpx 1/1 Running 0 43s 10.244.0.16 master1 <none> <none>
topo-demo-6bbf65d967-jsj4h 1/1 Running 0 43s 10.244.2.236 node2 <none> <none>
topo-demo-6bbf65d967-kc9x8 1/1 Running 0 43s 10.244.2.237 node2 <none> <none>
topo-demo-6bbf65d967-nr9b9 1/1 Running 0 43s 10.244.1.109 node1 <none> <none>
topo-demo-6bbf65d967-wfzmf 1/1 Running 0 43s 10.244.0.17 master1 <none> <none>
可以看到符合我们的预期,每个节点上运行了2个 Pod 副本。
如果是要求每个节点上运行3个 Pod 副本呢?大家也可以尝试去练习下。
#02-daemonset.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: topo-demo2
spec:
replicas: 9
selector:
matchLabels:
app: topo2
template:
metadata:
labels:
app: topo2
spec:
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
name: ngpt1
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: topo2
查看现象:
hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml/2022.2.19-40.拓扑分布约束-实验代码$ kubectl apply -f 02-daemonset.yaml
deployment.apps/topo-demo2 created
hg@LAPTOP-G8TUFE0T:/mnt/c/Users/hg/Desktop/yaml/2022.2.19-40.拓扑分布约束-实验代码$ kubectl get po -l app=topo2 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
topo-demo2-87d77dbbd-25wbw 1/1 Running 0 53s 10.244.2.240 node2 <none> <none>
topo-demo2-87d77dbbd-77gm8 1/1 Running 0 53s 10.244.1.113 node1 <none> <none>
topo-demo2-87d77dbbd-97npc 1/1 Running 0 53s 10.244.0.20 master1 <none> <none>
topo-demo2-87d77dbbd-fspcq 1/1 Running 0 53s 10.244.1.111 node1 <none> <none>
topo-demo2-87d77dbbd-k5llk 1/1 Running 0 53s 10.244.1.112 node1 <none> <none>
topo-demo2-87d77dbbd-pc24x 1/1 Running 0 53s 10.244.2.238 node2 <none> <none>
topo-demo2-87d77dbbd-rdmwn 1/1 Running 0 53s 10.244.2.239 node2 <none> <none>
topo-demo2-87d77dbbd-s4rjp 1/1 Running 0 53s 10.244.0.18 master1 <none> <none>
topo-demo2-87d77dbbd-xrwmk 1/1 Running 0 53s 10.244.0.19 master1 <none> <none>
实验结束。
我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!
各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:
个人微信二维码:x2675263825 (舍得), qq:2675263825。
个人博客地址:www.onlyonexl.cn
个人微信公众号:云原生架构师实战
个人csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
个人已开源干货
名称 | 链接 |
---|---|
01 实战:打造一款王者云笔记:typora+坚果云+阿里云oss | https://www.jianguoyun.com/p/DXS6qiIQvPWVCRiS0qoE |
02 实战:定制宇宙中最美的typora主题皮肤 | https://www.jianguoyun.com/p/DeUK9u0QvPWVCRib0qoE |
03 玩转vscode | https://www.jianguoyun.com/p/DZe8gmsQvPWVCRid0qoE |
04 陈果的幸福哲学课 | https://www.jianguoyun.com/p/Db0kM7gQvPWVCRj2q6YE |
好了,关于拓扑分布约束实验就到这里了,感谢大家阅读,最后贴上我女神的photo,祝大家生活快乐,每天都过的有意义哦,我们下期见!