亲和性和反亲和性的含义
首先在介绍拓扑分布约束之前,我们要知道 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
apiVersionapps/v1
kindDeployment
metadata
  namenginx
  labels
    appnginx
spec
  replicas2
  selector
    matchLabels
      appnginx
  template
    metadata
      labels
        appnginx
    spec
      containers
namenginx
        imagenginx
        ports
containerPort80
          namenginxweb
      affinity
        podAntiAffinity
          requiredDuringSchedulingIgnoredDuringExecution# 硬策略
labelSelector
              matchExpressions
keyapp
                operatorIn
                values
nginx
            topologyKeydisk  # 通过 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 字段定义如下所示:
apiVersionv1
kindPod
metadata
  namemypod
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
apiVersionapps/v1
kindDeployment
metadata
  namenginx
  labels
    appnginx
spec
  replicas2
  selector
    matchLabels
      appnginx
  template
    metadata
      labels
        appnginx
    spec
      containers
namenginx
        imagenginx
        ports
containerPort80
          namenginxweb
      topologySpreadConstraints
maxSkew1  # 不同拓扑域之间的 pod 的最大差值
        topologyKeydisk # 匹配拓扑域
        whenUnsatisfiableDoNotSchedule # 默认硬策略
        labelSelector
          matchLabels
            appnginx # 匹配 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
apiVersionapps/v1
kindDeployment
metadata
  namenginx
  labels
    appnginx
spec
  replicas4
  selector
    matchLabels
      appnginx
  template
    metadata
      labels
        appnginx
    spec
      containers
namenginx
        imagenginx
        ports
containerPort80
          namenginxweb
      topologySpreadConstraints
maxSkew1
        topologyKeydisk
        whenUnsatisfiableDoNotSchedule
        labelSelector
          matchLabels
            appnginx
      affinity
        podAntiAffinity
          requiredDuringSchedulingIgnoredDuringExecution# 硬策略
labelSelector
              matchExpressions
keyapp
                operatorIn
                values
nginx
            topologyKeykubernetes.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 类型的容灾和高可用的目的,这样我们的应用更具有健壮性。










