云原生Kubernetes:pod亲和性与反亲和性

目录

一、理论

1.调度策略

2.Pod 拓扑分布约束

3.调度场景详解

4.亲和性与反亲和性案例

二、实验

1.亲和性与反亲和性

三、问题

1.节点批次打标签错误

2.for循环批量创建pod报错

3.比较 podAffinity 和 podAntiAffinity

四、总结


一、理论

1.调度策略

(1)对比

2.Pod 拓扑分布约束

(1)概念

使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pod在集群内故障域之间的分布, 例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。

(2)拓扑分布约束 (涉及K8S v1.25及以上版本)

Pod 拓扑分布约束使你能够以声明的方式进行配置。

topologySpreadConstraints 字段,Pod API 包括一个 spec.topologySpreadConstraints 字段。这个字段的用法如下所示:

---
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.27 开始成为 Beta
      nodeAffinityPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
      nodeTaintsPolicy: [Honor|Ignore] # 可选;自从 v1.26 开始成为 Beta
  ### 其他 Pod 字段置于此处

(3) 示例

① 节点标签

拓扑分布约束依赖于节点标签来标识每个节点所在的拓扑域。 例如,某节点可能具有标签:

说明:
为了简便,此示例未使用众所周知的标签键 topology.kubernetes.io/zone 和 topology.kubernetes.io/region。 但是,建议使用那些已注册的标签键,而不是此处使用的私有(不合格)标签键 region 和 zone。

无法对不同上下文之间的私有标签键的含义做出可靠的假设。

有一个 4 节点的集群且带有以下标签

从逻辑上看集群如下

一致性

应该为一个组中的所有 Pod 设置相同的 Pod 拓扑分布约束。

通常,如果正使用一个工作负载控制器,例如 Deployment,则 Pod 模板会帮你解决这个问题。 如果混合不同的分布约束,则 Kubernetes 会遵循该字段的 API 定义; 但是,该行为可能更令人困惑,并且故障排除也没那么简单。

运维人员需要一种机制来确保拓扑域(例如云提供商区域)中的所有节点具有一致的标签。 为了避免运维人员需要手动为节点打标签,大多数集群会自动填充知名的标签, 例如 kubernetes.io/hostname。检查运维人员的集群是否支持此功能。

一个拓扑分布约束

假设拥有一个 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: registry.k8s.io/pause:3.1

从此清单看,topologyKey: zone 意味着均匀分布将只应用于存在标签键值对为 zone: <any value> 的节点 (没有 zone 标签的节点将被跳过)。如果调度器找不到一种方式来满足此约束, 则 whenUnsatisfiable: DoNotSchedule 字段告诉该调度器将新来的 Pod 保持在 pending 状态。

如果该调度器将这个新来的 Pod 放到可用区 A,则 Pod 的分布将成为 [3, 1]。 这意味着实际偏差是 2(计算公式为 3 - 1),这违反了 maxSkew: 1 的约定。 为了满足这个示例的约束和上下文,新来的 Pod 只能放到可用区 B 中的一个节点上:

或者:

可以调整 Pod 规约以满足各种要求:

将 maxSkew 更改为更大的值,例如 2,这样新来的 Pod 也可以放在可用区 A 中。
将 topologyKey 更改为 node,以便将 Pod 均匀分布在节点上而不是可用区中。 在上面的例子中,如果 maxSkew 保持为 1,则新来的 Pod 只能放到 node4 节点上。
将 whenUnsatisfiable: DoNotSchedule 更改为 whenUnsatisfiable: ScheduleAnyway, 以确保新来的 Pod 始终可以被调度(假设满足其他的调度 API)。但是,最好将其放置在匹配 Pod 数量较少的拓扑域中。 请注意,这一优先判定会与其他内部调度优先级(如资源使用率等)排序准则一起进行标准化。

3.调度场景详解

kube-scheduler 给一个 pod 做调度选择包含两个步骤: 1.过滤(节点预选),2.打分(节点优选,节点选定)

(1)Deployment/RC:全自动调度

Deployment/RC主要是自动部署应用的多个副本,并持续监控,以维持副本的数量。默认是使用系统Master的Scheduler经过一系列算法计算来调度,用户无法干预调度过程与结果。

(2)NodeSelector:定向调度
通过Node的标签和Pod的nodeSelector属性相匹配,可以达到将pod调度到指定的一些Node上。

 

(3)Node亲和性调度
目前有两种亲和性表达,NodeAffinity语法支持的操作符包括In/NotIn/Exists/DoesNotExist/Gt/Lt。

该pod可以被调度到key为“env”,值为“node01”或“node02”的节点。另外,在满足该条件的节点中,优先使用具有“disk”标签,且值为“ssd”的节点。

(4)POD亲和性调度

POD亲和性表示POD部署到满足某些label的pod所在的NODE上。pod需要被调度到某个NODE节点上,该节点属于kubernetes.io/hostname 这个域,并且上面运行了这样的 pod:这个pod 有一个app=nginx的 label。

(5)POD反亲和性调度

POD反亲和性表示POD不能部署到满足某些label的pod所在的NODE上。

该pod不能被调度到某个NODE节点上,该节点属于kubernetes.io/hostname 这个域,并且上面运行了这样的 pod:这个pod 有一个app=nginx的 label。

(6) Taints与Tolerations(污点与容忍)
Taints与前面的Affinity相反,它让Node拒绝Pod的运行。为node添加一个Taint,效果是NoSchedule(除了NoSchedule还可以取值PreferNoSchedule/NoExecute)。意味着除非Pod明确声明可以容忍这个Taint,否则不会被调度到该Node上。使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数完成该机制。

该pod容忍env=node02 且 effect 为 NoExecute的节点。可以进行调度

(7)DaemonSet调度

在每个Node上调度一个Pod,管理集群中每个Node上仅运行一份Pod的副本实例。

该pod会在每一个符合调度的node 节点上启动一个实例。该节点必须存在fluentd=true 的标签。

4.亲和性与反亲和性案例

(1)环境准备

node01、 node02 都有标签 test=a,有个pod1 运行在node01上, 标签为app=myapp01

#设置node01和node02节点,拥有标签 test=a
[root@master demo]# kubectl  label nodes node{01,02} test=a --overwrite
node/node01 labeled
node/node02 labeled

#查看拥有标签test=a的节点
[root@master demo]# kubectl  get nodes -l test=a
NAME     STATUS   ROLES    AGE    VERSION
node01   Ready    <none>   7d6h   v1.15.1
node02   Ready    <none>   7d6h   v1.15.1
[root@master demo]# vim test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp01
  labels:
    app: myapp01
spec:
  containers:
  - name: with-node-affinity
    image: soscscs/myapp:v1
#声明式创建pod 
[root@master demo]# kubectl  apply  -f test.yaml 
pod/myapp01 created

#查看pod myapp01的详细信息的标签
[root@master demo]# kubectl  get pods  myapp01  -o wide --show-labels 
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp01   1/1     Running   0          26s   10.244.1.112   node01   <none>           <none>            app=myapp01

(2) 亲和性+ In 测试

[root@master demo]# vim a.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp10
  labels:
    app: myapp03
spec:
  containers:
  - name: myapp03
    image: soscscs/myapp:v1
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - myapp01
        topologyKey: test

pod拥有app=myapp01 标签(设这个pod为x),则把新pod调度到和 pod x 拥有同一个拓扑域test=a 的 节点上。

[root@master demo]# for i in myapp{11..15}; do kubectl apply -f a.yaml; sed -ri "4s#myapp1[0-9]#$i#" a.yaml; done
pod/myapp10 created
pod/myapp11 created
pod/myapp12 created
pod/myapp13 created
pod/myapp14 created
[root@master demo]# kubectl  get pods -o wide --show-labels 
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp01   1/1     Running   0          88m   10.244.1.112   node01   <none>           <none>            app=myapp01
myapp10   1/1     Running   0          6s    10.244.2.104   node02   <none>           <none>            app=myapp03
myapp11   1/1     Running   0          6s    10.244.1.138   node01   <none>           <none>            app=myapp03
myapp12   1/1     Running   0          5s    10.244.2.105   node02   <none>           <none>            app=myapp03
myapp13   1/1     Running   0          5s    10.244.1.139   node01   <none>           <none>            app=myapp03
myapp14   1/1     Running   0          5s    10.244.2.106   node02   <none>           <none>            app=myapp03

因为拓扑域test=a内,已经有了pod存在于node01节点上,所以把新pod调度到和 pod x 拥有同一个拓扑域test=a 的 节点上。

(3)亲和性+NotIn 测试

[root@master demo]# vim a.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp10
  labels:
    app: myapp03
spec:
  containers:
  - name: myapp03
    image: soscscs/myapp:v1
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: NotIn
            values:
            - myapp01
        topologyKey: test
[root@master demo]# kubectl delete pod myapp{10..14};for i in myapp{11..15}; do kubectl apply -f a.yaml; sed -ri "4s#myapp1[0-9]#$i#" a.yaml; done
pod "myapp10" deleted
pod "myapp11" deleted
pod "myapp12" deleted
pod "myapp13" deleted
pod "myapp14" deleted
pod/myapp10 created
pod/myapp11 created
pod/myapp12 created
pod/myapp13 created
pod/myapp14 created
[root@master demo]# kubectl  get pods -o wide --show-labels 
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp01   1/1     Running   0          91m   10.244.1.112   node01   <none>           <none>            app=myapp01
myapp10   1/1     Running   0          5s    10.244.1.140   node01   <none>           <none>            app=myapp03
myapp11   1/1     Running   0          5s    10.244.2.107   node02   <none>           <none>            app=myapp03
myapp12   1/1     Running   0          4s    10.244.1.141   node01   <none>           <none>            app=myapp03
myapp13   1/1     Running   0          4s    10.244.2.108   node02   <none>           <none>            app=myapp03
myapp14   1/1     Running   0          4s    10.244.1.142   node01   <none>           <none>            app=myapp03

(4)非亲和性+In 测试

[root@master demo]# vim a.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp10
  labels:
    app: myapp03
spec:
  containers:
  - name: myapp03
    image: soscscs/myapp:v1
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - myapp01
        topologyKey: test
[root@master demo]# kubectl delete pod myapp{10..14};for i in myapp{11..15}; do kubectl apply -f a.yaml; sed -ri "4s#myapp1[0-9]#$i#" a.yaml; done
pod "myapp10" deleted
pod "myapp11" deleted
pod "myapp12" deleted
pod "myapp13" deleted
pod "myapp14" deleted
pod/myapp10 created
pod/myapp11 created
pod/myapp12 created
pod/myapp13 created
pod/myapp14 created
[root@master demo]# kubectl  get pods -o wide --show-labels 
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp01   1/1     Running   0          96m   10.244.1.112   node01   <none>           <none>            app=myapp01
myapp10   0/1     Pending   0          4s    <none>         <none>   <none>           <none>            app=myapp03
myapp11   0/1     Pending   0          3s    <none>         <none>   <none>           <none>            app=myapp03
myapp12   0/1     Pending   0          3s    <none>         <none>   <none>           <none>            app=myapp03
myapp13   0/1     Pending   0          3s    <none>         <none>   <none>           <none>            app=myapp03
myapp14   0/1     Pending   0          3s    <none>         <none>   <none>           <none>            app=myapp03


[root@master demo]# kubectl  describe  pod myapp10

(5)非亲和性 + NotIn 测试

[root@master demo]# vim a.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp10
  labels:
    app: myapp03
spec:
  containers:
  - name: myapp03
    image: soscscs/myapp:v1
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: NotIn
            values:
            - myapp01
        topologyKey: test
[root@master demo]# kubectl delete pod myapp{10..14};for i in myapp{11..15}; do kubectl apply -f a.yaml; sed -ri "4s#myapp1[0-9]#$i#" a.yaml; done
pod "myapp10" deleted
pod "myapp11" deleted
pod "myapp12" deleted
pod "myapp13" deleted
pod "myapp14" deleted
pod/myapp10 created
pod/myapp11 created
pod/myapp12 created
pod/myapp13 created
pod/myapp14 created
[root@master demo]# kubectl  get pods -o wide --show-labels 
NAME      READY   STATUS    RESTARTS   AGE    IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
myapp01   1/1     Running   0          102m   10.244.1.112   node01   <none>           <none>            app=myapp01
myapp10   1/1     Running   0          8s     10.244.2.109   node02   <none>           <none>            app=myapp03
myapp11   0/1     Pending   0          8s     <none>         <none>   <none>           <none>            app=myapp03
myapp12   0/1     Pending   0          7s     <none>         <none>   <none>           <none>            app=myapp03
myapp13   0/1     Pending   0          7s     <none>         <none>   <none>           <none>            app=myapp03
myapp14   0/1     Pending   0          7s     <none>         <none>   <none>           <none>            app=myapp03


[root@master demo]# kubectl  describe  pod myapp11

二、实验

1.亲和性与反亲和性

(1)环境准备

node01、 node02 都有标签 test=a,有个pod1 运行在node01上, 标签为app=myapp01

编写资源清单

声明式创建pod

查看pod myapp01的详细信息的标签

pod myapp01 拥有标签app=myapp01,且被调度到了node01上

(2) 亲和性+ In 测试

pod拥有app=myapp01 标签(设这个pod为x),则把新pod调度到和 pod x 拥有同一个拓扑域test=a 的 节点上。

创建pod myapp10-myapp14

因为拓扑域test=a内,已经有了pod存在于node01节点上,所以把新pod调度到和 pod x 拥有同一个拓扑域test=a 的 节点上。

pod从node01节点开始在两个节点上依次循环创建pod

查看资源清单文件,最后一次更改为15后,执行了14,然后修改为15结束for循环,实际是创建pod myapp10-myapp14就结束了

(3)亲和性+NotIn 测试

先修改资源清单文件

批量删除之前的pod myapp10-myapp14,并创建pod myapp10-myapp14

pod从node01节点开始,在两个节点上依次循环创建pod

(4)非亲和性+In 测试

先修改资源清单文件

批量删除之前的pod myapp10-myapp14,并创建pod myapp10-myapp14

查看信息,新创建的pod都处于Pending状态

查看详细信息

两个节点都不匹配pod非亲和规则,又因为使用的是硬限制,因此当找不到合适的node节点调度pod时,就会处于Pending状态

(5)非亲和性 + NotIn 测试

先修改资源清单文件

批量删除之前的pod myapp10-myapp14,并创建pod myapp10-myapp14

查看信息,只有第一个myapp10成功在node02上创建,剩余的4个pod都处于Pending状态

查看详细信息

两个节点都不满足现有的pod反亲和性特征

三、问题

1.节点批次打标签错误

(1)报错

(2)原因分析

语法错误

(3)解决方法

将括号()修改为花括号 { }

修改前:

修改后:

成功:

2.for循环批量创建pod报错

(1)报错

(2)原因分析

“s”命令缺少终止符合#

(3)解决方法

添加终止符#

修改前:

修改后:

成功:

3.比较 podAffinity 和 podAntiAffinity

(1)比较

在 Kubernetes 中, Pod亲和性和反亲和性控制 Pod 彼此的调度方式(更密集或更分散)。

podAffinity
吸引 Pod;你可以尝试将任意数量的 Pod 集中到符合条件的拓扑域中。

podAntiAffinity
驱逐 Pod。如果将此设为 requiredDuringSchedulingIgnoredDuringExecution 模式, 则只有单个 Pod 可以调度到单个拓扑域;如果你选择 preferredDuringSchedulingIgnoredDuringExecution, 则你将丢失强制执行此约束的能力。

要实现更细粒度的控制,你可以设置拓扑分布约束来将 Pod 分布到不同的拓扑域下,从而实现高可用性或节省成本。 这也有助于工作负载的滚动更新和平稳地扩展副本规模。

四、总结

批量删除之前的pod myapp10-myapp14,并创建pod myapp10-myapp14

[root@master]# kubectl delete pod myapp{10..14};for i in myapp{11..15}; do kubectl apply -f a.yaml; sed -ri "4s#myapp1[0-9]#$i#" a.yaml; done

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值