Kubernetes:如何实现跨集群节点均匀调度分布Pod(Pod拓扑分布约束)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

写在前面


  • 分享一些 k8s 跨集群节点均匀调度分布Pod 的笔记
  • 博文内容涉及:
    • pod 调度 && 拓扑分布约束 简单介绍
    • 跨节点均匀分布 pod Demo && 相关配置字段说明
    • 多个拓扑分布约束 Demo
    • 有冲突拓扑分布约束 Demo
  • 理解不足小伙伴帮忙指正

纵我不往子宁不来? ——《郑风·子衿》


Pod 调度的简单介绍

在 k8s 中 通过 kube-scheduler 组件来实现 pod 的调度所谓调度即把需要创建的 pod 放到 合适的 node 上大概流程为通过对应的 调度算法调度策略为待调度的 pod 列表中的 pod 选择一个最合适的 Node然后目标节点上的 kubelet 通过 watch 接口监听到 kube-schedule 产生的 Pod 绑定事件通过 APIService 获取对应的 Pod 清单下载 image 并且启动容器。

这里具体的 调度算法 大体上分两步筛选出候选节点确定最优节点确定最优节点涉及节点打分等。

常见的 Pod 的 调度策略选择器、指定节点、主机亲和性方式同时需要考虑节点的 coedondrain标记今天和小伙伴分享的是 调度策略的一种 即通过 Pod拓扑分布约束 用来实现 跨集群节点均匀调度分布Pod

为什么需要跨集群节点 均匀调度分布 Pod ? 我们知道在 k8s 中 如果只是希望每个节点均匀调度分布一个 pod那么可以利用 DaemonSet 来实现。如果多个就需要 pod 的拓扑分布约束均匀调度 Pod 实现在集群中均匀分布 Pod可以尽可能的利用 节点的超售Pod 的超用以实现高可用性和高效的集群资源利用。

k8s 中通过 Pod 拓扑分布约束(PodTopologySpread)来实现均匀调度 pod。这一特性从 v1.19 以后达到稳定状态。在 v1.25v1.1.26 的版本中添加的部分属性。

需要说明的是这里的 均匀调度 pod 不是说 只有对当前需要调度的 pod 在 工作节点发生均匀调度不考虑当前节点上之前存在的 pod , 而是基于 工作节点的 均匀调度。即所谓均匀调度分布是基于工作节点的。虽然 pod 的拓扑分布约束是定义在 pod 上的。

当前集群版本为 v1.22,所以只有部分字段

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl get nodes
NAME                          STATUS   ROLES                  AGE    VERSION
vms155.liruilongs.github.io   Ready    <none>                 54d    v1.22.2
vms156.liruilongs.github.io   Ready    <none>                 54d    v1.22.2
vms81.liruilongs.github.io    Ready    control-plane,master   378d   v1.22.2
vms82.liruilongs.github.io    Ready    <none>                 378d   v1.22.2
vms83.liruilongs.github.io    Ready    <none>                 378d   v1.22.2

通过帮助手册可以简单了解下

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl explain  pod.spec.topologySpreadConstraints
KIND:     Pod
VERSION:  v1

RESOURCE: topologySpreadConstraints <[]Object>

DESCRIPTION:
     TopologySpreadConstraints describes how a group of pods ought to spread
     across topology domains. Scheduler will schedule pods in a way which abides
     by the constraints. All topologySpreadConstraints are ANDed.

     TopologySpreadConstraint specifies how to spread matching pods among the
     given topology.

。。。。。

如何实现跨节点均匀分布 Pod

在 定义 Pod 的 yaml 资源文件中可以定义一个或多个 topologySpreadConstraints 条目以指导 kube-scheduler 如何将每个新来的 Pod 与跨集群的现有 Pod 相关联。从而实现 Pod 的 均匀调度这些字段包括(1.22 版本)

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # 配置一个拓扑分布约束
  topologySpreadConstraints:
    - maxSkew: <integer>
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>
  ### 其他 Pod 字段置于此处

在最新的版本中提供了其他的一些字段

---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # 配置一个拓扑分布约束
  topologySpreadConstraints:
    - maxSkew: <integer>
      minDomains: <integer> # 可选;自从 v1.25 开始成为 Beta
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>
      matchLabelKeys: <list> # 可选;自从 v1.25 开始成为 Alpha
      nodeAffinityPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
      nodeTaintsPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
  ### 其他 Pod 字段置于此处

具体通过这些字段如何配置先来看一个 Demo

pod 拓扑约束依赖于 节点标签 来识别每个工作节点的所在的 拓扑域。这里为了以均匀的方式在所有集群工作节点上均匀的分布 Pod我们使用 k8s 集群默认自带的 节点主机名节点标签作为拓扑域可以保证每个节点都在自己唯一的拓扑域中。

拓扑约束会基于指定的标签来做为拓扑域这里通过 kubernetes.io/hostname 的 values 作为 拓扑域。

┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$kubectl get node --label-columns kubernetes.io/hostname
NAME                          STATUS   ROLES                  AGE    VERSION   HOSTNAME
vms155.liruilongs.github.io   Ready    <none>                 54d    v1.22.2   vms155.liruilongs.github.io
vms156.liruilongs.github.io   Ready    <none>                 54d    v1.22.2   vms156.liruilongs.github.io
vms81.liruilongs.github.io    Ready    control-plane,master   378d   v1.22.2   vms81.liruilongs.github.io
vms82.liruilongs.github.io    Ready    <none>                 378d   v1.22.2   vms82.liruilongs.github.io
vms83.liruilongs.github.io    Ready    <none>                 378d   v1.22.2   vms83.liruilongs.github.io
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$

spec.topologySpreadConstaints 定义为

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  # 配置一个拓扑分布约束
  topologySpreadConstraints:
    - maxSkew: 1 # 以绝对均匀的方式分配 POD
      topologyKey: kubernetes.io/hostname #使用主机名这个标签作为拓扑域
      whenUnsatisfiable: ScheduleAnyway #始终调度 pod即使它不能满足 pod 的均匀分布
      labelSelector: <object> #作用于匹配这个选择器的 Pod
  ### 其他 Pod 字段置于此处

涉及多 pod 调度所以我们需要创建一个 deploy 同时创建一个 命名空间。具体的资源文件

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat liruilong-topo-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: liruilong-topo-namespace
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: liruilong
spec:
  replicas: 10
  template:
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: liruilong
      containers:
      - name: pause
        image: registry.aliyuncs.com/google_containers/pause:3.5

为了方便 Demo ,我们使用 kustomize 来整合 yaml 文件。并且配置一个通用的标签。


┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat kustomization.yaml
namespace: liruilong-topo-namespace
commonLabels:
  app: liruilong
resources:
- liruilong-topo-namespace.yaml
- deploy.yaml

生成的 yaml 文件为:

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl kustomize ./
apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: liruilong
  name: liruilong-topo-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: liruilong
  name: liruilong
  namespace: liruilong-topo-namespace
spec:
  replicas: 10
  selector:
    matchLabels:
      app: liruilong
  template:
    metadata:
      labels:
        app: liruilong
    spec:
      containers:
      - image: registry.aliyuncs.com/google_containers/pause:3.5
        name: pause
      topologySpreadConstraints:
      - labelSelector:
          matchLabels:
            app: liruilong
        maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: ScheduleAnyway

应用生成的 yaml 文件为了方便顺便切一下命名空间

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply -k ./
namespace/liruilong-topo-namespace created
deployment.apps/liruilong created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl config set-context $(kubectl config current-context) --namespace=liruilong-topo-namespace
Context "kubernetes-admin@kubernetes" modified.

查看 pod 分布master 节点设置了污点所以不调度剩下的 4 个 工作节点上面的副本数为 10所以均匀分布为 [ 2 2 3 3 ] 这里实际上是利用了 whenUnsatisfiable: ScheduleAnyway 的配置,即不管如何都会发生调度因为 master 上没有调度所有是 0但是其他节点为 2,3 即 不符合 maxSkew 设置的 1,简单理解 maxSkew 即 pod 在节点分布的差值不能超过的值。

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE    IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-547cf79f6f-2mw8f   1/1     Running   0          8m2s   10.244.171.150   vms82.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-4b687   1/1     Running   0          8m2s   10.244.217.21    vms155.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-68nmv   1/1     Running   0          8m2s   10.244.70.60     vms83.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-8n8zc   1/1     Running   0          8m2s   10.244.171.191   vms82.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-cgk9k   1/1     Running   0          8m2s   10.244.70.27     vms83.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-gpx4l   1/1     Running   0          8m2s   10.244.217.22    vms155.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-j4rk7   1/1     Running   0          8m2s   10.244.194.77    vms156.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-t4fhr   1/1     Running   0          8m2s   10.244.194.76    vms156.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-wndvb   1/1     Running   0          8m2s   10.244.194.78    vms156.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-zjnp9   1/1     Running   0          8m2s   10.244.217.20    vms155.liruilongs.github.io   <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

需要注意的是缩减 Deployment 并不能保证均匀分布并可能导致 Pod 分布不平衡。但是可以使用 Descheduler,或者销毁重建的方式重新平衡 Pod 分布。看下 Demo

这是添加一个补丁文件修改副本数为 4

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: liruilong
spec:
  replicas: 4
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat kustomization.yaml
namespace: liruilong-topo-namespace
commonLabels:
  app: liruilong
resources:
- liruilong-topo-namespace.yaml
- deploy.yaml
patchesStrategicMerge:
- increase_replicas.yaml

重新应用会发现 pod 的调度完全变成了非均匀即缩减 副本数并不会发生重新的 Pod 均匀分布。

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply  -k ./
namespace/liruilong-topo-namespace unchanged
deployment.apps/liruilong configured
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                         NOMINATED NODE   READINESS GATES
liruilong-547cf79f6f-2mw8f   1/1     Running   0          32m   10.244.171.150   vms82.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-68nmv   1/1     Running   0          32m   10.244.70.60     vms83.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-8n8zc   1/1     Running   0          32m   10.244.171.191   vms82.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-cgk9k   1/1     Running   0          32m   10.244.70.27     vms83.liruilongs.github.io   <none>           <none>

这要如果希望均匀分布要不使用工具要不只能重新部署下面为重新部署

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl delete  -k ./
namespace "liruilong-topo-namespace" deleted
deployment.apps "liruilong" deleted
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply  -k ./
namespace/liruilong-topo-namespace created
deployment.apps/liruilong created

重新部署可以发现 pod 均匀分布四个工作节点各部署一个 [1 1 1 1]

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-547cf79f6f-25c4b   1/1     Running   0          4s    10.244.171.180   vms82.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-2qhmh   1/1     Running   0          4s    10.244.217.25    vms155.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-8qmbc   1/1     Running   0          4s    10.244.194.81    vms156.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-gbw9f   1/1     Running   0          4s    10.244.70.23     vms83.liruilongs.github.io    <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

修改节副本数为 8 可以看到当前 pod 分布为 [ 2 2 2 2 ]

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$vim increase_replicas.yaml
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply  -k ./
namespace/liruilong-topo-namespace unchanged
deployment.apps/liruilong configured
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-547cf79f6f-25c4b   1/1     Running   0          85s   10.244.171.180   vms82.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-2qhmh   1/1     Running   0          85s   10.244.217.25    vms155.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-8qmbc   1/1     Running   0          85s   10.244.194.81    vms156.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-b9swn   1/1     Running   0          6s    10.244.171.187   vms82.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-dqxw5   1/1     Running   0          6s    10.244.70.15     vms83.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-gbw9f   1/1     Running   0          85s   10.244.70.23     vms83.liruilongs.github.io    <none>           <none>
liruilong-547cf79f6f-js8ft   1/1     Running   0          6s    10.244.217.26    vms155.liruilongs.github.io   <none>           <none>
liruilong-547cf79f6f-vnxhd   1/1     Running   0          6s    10.244.194.82    vms156.liruilongs.github.io   <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

具体的字段解释

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
      matchLabels:
        app: liruilong

maxSkew :描述这些 Pod 可能被均匀分布的程度。你必须指定此字段且该数值必须大于零。 其语义将随着 whenUnsatisfiable 的值发生变化:简单来讲就是如果为均匀分布那么两个节点之前的 pod 差值最大可以为多大。

  • 如果你选择 whenUnsatisfiable: DoNotSchedule则 maxSkew 定义目标拓扑中匹配 Pod 的数量与 全局最小值之间的最大允许差值。例如如果你有 3 个可用区分别有 2、2 和 1 个匹配的 Pod则 MaxSkew 设为 1 且全局最小值为 1。
  • 如果你选择 whenUnsatisfiable: ScheduleAnyway则该调度器会更为偏向能够降低偏差值的拓扑域。

topologyKey :是节点标签的键。如果节点使用此键标记并且具有相同的标签值 则将这些节点视为处于同一拓扑域中。我们将拓扑域中(即键值对的每个实例称为一个域。 调度器将尝试在每个拓扑域中放置数量均衡的 Pod。 另外我们将符合条件的域定义为其节点满足 nodeAffinityPolicynodeTaintsPolicy 要求的域。当 topologyKey 的值为 none 的时候。

whenUnsatisfiable: 指示如果 Pod 不满足分布约束时如何处理:

  • DoNotSchedule(默认告诉调度器不要调度。
  • ScheduleAnyway 告诉调度器仍然继续调度只是根据如何能将偏差最小化来对节点进行排序。

labelSelector: 用于查找匹配的 Pod。匹配此标签的 Pod 将被统计以确定相应拓扑域中 Pod 的数量

当 Pod 定义了不止一个 topologySpreadConstraint这些约束之间是逻辑与的关系。 kube-scheduler 会为新的 Pod 寻找一个能够满足所有约束的节点。

多个拓扑分布约束

在这之前需要做一些准备工作,在每个工作节点上在打一个标签 disktype=node-group1。作为新的拓扑域

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms82.liruilongs.github.io disktype=node-group1  --overwrite
node/vms82.liruilongs.github.io labeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms83.liruilongs.github.io disktype=node-group1  --overwrite
node/vms83.liruilongs.github.io labeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms155.liruilongs.github.io disktype=node-group2  --overwrite
node/vms155.liruilongs.github.io labeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms156.liruilongs.github.io disktype=node-group2  --overwrite
node/vms156.liruilongs.github.io labeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get node  --label-columns  disktype
NAME                          STATUS   ROLES                  AGE    VERSION   DISKTYPE
vms155.liruilongs.github.io   Ready    <none>                 55d    v1.22.2   node-group2
vms156.liruilongs.github.io   Ready    <none>                 55d    v1.22.2   node-group2
vms81.liruilongs.github.io    Ready    control-plane,master   379d   v1.22.2
vms82.liruilongs.github.io    Ready    <none>                 379d   v1.22.2   node-group1
vms83.liruilongs.github.io    Ready    <none>                 379d   v1.22.2   node-group1
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

当前集群的 pod 在 4 个工作节点的分布 [0 1 1 1]

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP              NODE                          NOMINATED NODE   READINESS GATES
liruilong-744498fcbd-4z7l4   1/1     Running   0          7h32m   10.244.70.34    vms83.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-jpngw   1/1     Running   0          7h32m   10.244.217.27   vms155.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-vt9gb   1/1     Running   0          7h32m   10.244.194.86   vms156.liruilongs.github.io   <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

这里添加一个新的 Pod 添加了两个拓扑分布约束要求这个 pod 即在 topologyKey: kubernetes.io/hostname 的拓扑域同时在 topologyKey: disktype 的拓扑域

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$cat two-constrains.yaml
kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    app: liruilong
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: liruilong
  - maxSkew: 1
    topologyKey: disktype
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: liruilong
  containers:
  - name: pause
    image: registry.aliyuncs.com/google_containers/pause:3.5

应用上面的 yaml 文件 pod 调度到了 82 节点当前的 pod 分布为 [1 1 1 1]

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-744498fcbd-4z7l4   1/1     Running   0          8h    10.244.70.34     vms83.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-jpngw   1/1     Running   0          8h    10.244.217.27    vms155.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-vt9gb   1/1     Running   0          8h    10.244.194.86    vms156.liruilongs.github.io   <none>           <none>
mypod                        1/1     Running   0          39m   10.244.171.178   vms82.liruilongs.github.io    <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

现在把 82 节点的 拓扑域标签去掉即 80 节点不属于 disktype 约束的拓扑域。

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms82.liruilongs.github.io disktype-
node/vms82.liruilongs.github.io unlabeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get node --label-columns disktype
NAME                          STATUS   ROLES                  AGE    VERSION   DISKTYPE
vms155.liruilongs.github.io   Ready    <none>                 55d    v1.22.2   node-group2
vms156.liruilongs.github.io   Ready    <none>                 55d    v1.22.2   node-group2
vms81.liruilongs.github.io    Ready    control-plane,master   379d   v1.22.2
vms82.liruilongs.github.io    Ready    <none>                 379d   v1.22.2
vms83.liruilongs.github.io    Ready    <none>                 379d   v1.22.2   node-group1

删除上面的 pod 重新应用 yaml 文件这时候 82 节点已不满足多拓扑分布的约束。可以看到 pod 调度到了 83 节点.

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl delete  -f two-constrains.yaml
pod "mypod" deleted
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply  -f two-constrains.yaml
pod/mypod created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP              NODE                          NOMINATED NODE   READINESS GATES
liruilong-744498fcbd-4z7l4   1/1     Running   0          8h    10.244.70.34    vms83.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-jpngw   1/1     Running   0          8h    10.244.217.27   vms155.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-vt9gb   1/1     Running   0          8h    10.244.194.86   vms156.liruilongs.github.io   <none>           <none>
mypod                        1/1     Running   0          8s    10.244.70.33    vms83.liruilongs.github.io    <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

有冲突的拓扑分布约束

如果所有节点都不满足 pod 的 拓扑分布约束当前 pod 就会调度失败。下面为创建了一个新的补丁文件修改副本数为 5使用 kubernetes.io/hostname 作为拓扑域

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl kustomize ./
apiVersion: v1
kind: Namespace
metadata:
  labels:
    app: liruilong
  name: liruilong-topo-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: liruilong
  name: liruilong
  namespace: liruilong-topo-namespace
spec:
  replicas: 5
  selector:
    matchLabels:
      app: liruilong
  template:
    metadata:
      labels:
        app: liruilong
    spec:
      containers:
      - image: registry.aliyuncs.com/google_containers/pause:3.5
        name: pause
      topologySpreadConstraints:
      - labelSelector:
          matchLabels:
            app: liruilong
        maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule

当前 whenUnsatisfiable: DoNotSchedule,即不满足约束时不发生调度 master 有污点默认不发生调度所以当 副本数为 5 的时候工作节点各调度一个剩下的一个 pod 调度到哪里都会违反 maxSkew: 1 所以发生 pending 。

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply  -k ./
namespace/liruilong-topo-namespace created
deployment.apps/liruilong created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-744498fcbd-22c56   1/1     Running   0          76s   10.244.194.89    vms156.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-7vn7r   1/1     Running   0          76s   10.244.70.61     vms83.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-8d9jq   0/1     Pending   0          76s   <none>           <none>                        <none>           <none>
liruilong-744498fcbd-8zh7q   1/1     Running   0          76s   10.244.171.157   vms82.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-rhtg5   1/1     Running   0          76s   10.244.217.28    vms155.liruilongs.github.io   <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

可以通过日志查看详细信息

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl describe pods liruilong-744498fcbd-8d9jq | grep -A 10 -i events
Events:
  Type     Reason            Age                    From               Message
  ----     ------            ----                   ----               -------
  Warning  FailedScheduling  6m23s                  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 topology spread constraints.
  Warning  FailedScheduling  4m22s (x1 over 5m22s)  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 topology spread constraints.
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

事件中提示: 1 个节点有污点{node-role.kubernetes.io/master: }该 pod 不能容忍4 个节点不符合 pod 拓扑结构的传播限制。

同样的如果我们把 master 节点 排除出拓扑区域那么就可以满足 maxSkew: 1可以看到 同样的资源。调度成功发生。

┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl label node vms81.liruilongs.github.io kubernetes.io/hostname-
node/vms81.liruilongs.github.io unlabeled
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl delete -k ./
namespace "liruilong-topo-namespace" deleted
deployment.apps "liruilong" deleted
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl apply -k ./
namespace/liruilong-topo-namespace created
deployment.apps/liruilong created
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                          NOMINATED NODE   READINESS GATES
liruilong-744498fcbd-5vbmz   1/1     Running   0          5s    10.244.171.143   vms82.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-cxlxm   1/1     Running   0          5s    10.244.70.37     vms83.liruilongs.github.io    <none>           <none>
liruilong-744498fcbd-hzgpr   1/1     Running   0          5s    10.244.194.90    vms156.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-nk858   1/1     Running   0          5s    10.244.194.91    vms156.liruilongs.github.io   <none>           <none>
liruilong-744498fcbd-x2pv2   1/1     Running   0          5s    10.244.217.29    vms155.liruilongs.github.io   <none>           <none>
┌──[root@vms81.liruilongs.github.io]-[~/ansible/podtopolog]
└─$

拓扑分布约束在实际的使用过程中还要考虑其他的调度策略比如选择器节点亲和性等感兴趣小伙伴可以到官网了解下关于 pod 通过拓扑分布约束实现的 跨节点的均匀分布 pod 就可以小伙伴们分享到这里生活加油。 ^_^

博文参考


https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/pod-priority-preemption/

https://medium.com/geekculture/kubernetes-distributing-pods-evenly-across-cluster-c6bdc9b49699

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: k8s