K8s Pod 调度的高级亲和性与反亲和性规则

好的,各位亲爱的 Kubernetes 爱好者们,大家好!我是你们的老朋友,今天咱们来聊聊 K8s 里那些“爱恨情仇”的故事——高级亲和性与反亲和性规则。

想象一下,你的 Kubernetes 集群就像一个繁忙的都市,里面住着各种各样的 Pod,它们就像都市里的居民,有的喜欢热闹,扎堆住在市中心;有的喜欢安静,偏爱郊外的别墅。而亲和性与反亲和性,就像是城市规划局制定的居住政策,决定了这些 Pod 们该如何选择自己的“邻居”。

一、 初识亲和性与反亲和性:牵线搭桥与划清界限

简单来说,亲和性(Affinity)就是“我喜欢你,我想和你住在一起”,而反亲和性(Anti-affinity)则是“我讨厌你,我不想见到你”。

  • 亲和性(Affinity): 鼓励 Pod 被调度到满足特定条件的节点上,或者与某些 Pod 尽可能地部署在一起。这就像是给 Pod 们“牵线搭桥”,让它们找到志同道合的伙伴。
  • 反亲和性(Anti-affinity): 避免 Pod 被调度到满足特定条件的节点上,或者避免与某些 Pod 部署在一起。这就像是给 Pod 们“划清界限”,避免“冤家路窄”。

二、 为什么我们需要高级亲和性与反亲和性?

你可能会说:“K8s 不是已经有节点选择器(Node Selector)了吗?也能实现一些简单的调度策略啊!”

没错,节点选择器就像是给 Pod 指定了一个简单的“门牌号”,让它们住在特定的节点上。但是,节点选择器的功能比较简单粗暴,只能根据节点的标签进行匹配。而高级亲和性与反亲和性则更加灵活、强大,可以实现更复杂的调度策略。

举个例子:

  • 场景一:高可用性
    • 假设你有一个重要的 Web 应用,为了保证高可用性,你希望将多个 Pod 分散到不同的节点上,避免单点故障。这时,反亲和性就派上用场了!你可以设置一个反亲和性规则,让这些 Pod 尽量不要部署在同一个节点上。
  • 场景二:性能优化
    • 假设你有一个计算密集型的任务,需要大量的 CPU 资源。你希望将相关的 Pod 部署在同一个节点上,利用节点上的所有 CPU 核心,提高计算效率。这时,亲和性就派上用场了!你可以设置一个亲和性规则,让这些 Pod 尽量部署在同一个节点上。
  • 场景三:资源隔离
    • 假设你有一些安全敏感的应用,不希望与其他应用共享节点资源,避免潜在的安全风险。这时,反亲和性也可以发挥作用!你可以设置一个反亲和性规则,让这些敏感应用与其他应用隔离部署。

三、 高级亲和性与反亲和性的语法结构

高级亲和性与反亲和性主要通过 Pod 的 spec.affinity 字段进行配置。这个字段包含以下几个部分:

  • nodeAffinity:节点亲和性,用于控制 Pod 被调度到哪些节点上。
  • podAffinity:Pod 亲和性,用于控制 Pod 与哪些 Pod 部署在一起。
  • podAntiAffinity:Pod 反亲和性,用于控制 Pod 避免与哪些 Pod 部署在一起。

咱们先来啃一啃 nodeAffinity 这块硬骨头。

3.1 节点亲和性(nodeAffinity

节点亲和性允许你基于节点的标签来控制 Pod 的调度。它有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution必须满足条件才能调度,如果调度后节点标签发生变化,Pod 不会被驱逐。可以理解为“硬性要求”,不满足条件,Pod 就寸步难行。
  • preferredDuringSchedulingIgnoredDuringExecution尽量满足条件进行调度,但是如果找不到满足条件的节点,Pod 也可以被调度到其他节点上。如果调度后节点标签发生变化,Pod 不会被驱逐。可以理解为“软性要求”,能满足最好,不能满足也没关系。

3.1.1 requiredDuringSchedulingIgnoredDuringExecution 详解

这种类型的节点亲和性定义了一组必须满足的条件,只有当节点满足这些条件时,Pod 才能被调度到该节点上。它的语法结构如下:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-required
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: <label-key>
            operator: <operator>
            values:
            - <value1>
            - <value2>
            ...
  containers:
  - name: nginx
    image: nginx
  • nodeSelectorTerms:一个或多个 nodeSelectorTerm 的列表,每个 nodeSelectorTerm 代表一组条件。
  • matchExpressions:一个或多个 matchExpression 的列表,每个 matchExpression 定义了一个具体的匹配规则。
    • key:节点的标签键。
    • operator:操作符,用于指定匹配规则。常用的操作符包括:
      • In:标签值在给定的列表中。
      • NotIn:标签值不在给定的列表中。
      • Exists:节点存在指定的标签。
      • DoesNotExist:节点不存在指定的标签。
      • Gt:标签值大于给定的值(仅适用于字符串类型的标签)。
      • Lt:标签值小于给定的值(仅适用于字符串类型的标签)。
    • values:标签值的列表,用于与节点标签进行比较。

举个栗子🌰:

假设你希望将 Pod 调度到拥有标签 disktype=ssd 的节点上,你可以这样配置:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-required
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
  containers:
  - name: nginx
    image: nginx

这意味着,只有拥有 disktype=ssd 标签的节点,才能成为该 Pod 的候选节点。

3.1.2 preferredDuringSchedulingIgnoredDuringExecution 详解

这种类型的节点亲和性定义了一组尽量满足的条件,调度器会尽量将 Pod 调度到满足这些条件的节点上,但是如果找不到满足条件的节点,Pod 也可以被调度到其他节点上。它的语法结构如下:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-preferred
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: <weight>
        preference:
          matchExpressions:
          - key: <label-key>
            operator: <operator>
            values:
            - <value1>
            - <value2>
            ...
  containers:
  - name: nginx
    image: nginx
  • weight:权重值,范围是 1-100。权重越高,表示调度器越倾向于选择满足条件的节点。
  • preference:一个 nodeSelectorTerm 对象,定义了具体的匹配规则,与 requiredDuringSchedulingIgnoredDuringExecution 中的 nodeSelectorTerm 结构相同。

举个栗子🌰:

假设你希望将 Pod 尽量调度到拥有标签 zone=us-east-1 的节点上,你可以这样配置:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-preferred
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 50
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - us-east-1
  containers:
  - name: nginx
    image: nginx

这意味着,调度器会尽量将 Pod 调度到拥有 zone=us-east-1 标签的节点上,但是如果找不到这样的节点,Pod 也可以被调度到其他节点上。权重值 50 表示调度器对这个偏好有一定的倾向性。

3.2 Pod 亲和性与反亲和性(podAffinity & podAntiAffinity

Pod 亲和性与反亲和性允许你基于 Pod 的标签来控制 Pod 的调度。它们可以实现更复杂的调度策略,例如:

  • 将相关的 Pod 部署在一起,提高通信效率。
  • 将不同的 Pod 分散到不同的节点上,提高可用性。
  • 避免将某些 Pod 部署在一起,避免资源竞争。

与节点亲和性类似,Pod 亲和性与反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution必须满足条件才能调度,如果调度后满足条件的 Pod 标签发生变化,Pod 不会被驱逐。
  • preferredDuringSchedulingIgnoredDuringExecution尽量满足条件进行调度,但是如果找不到满足条件的 Pod,Pod 也可以被调度到其他节点上。如果调度后满足条件的 Pod 标签发生变化,Pod 不会被驱逐。

3.2.1 podAffinity 详解

Pod 亲和性用于控制 Pod 与哪些 Pod 部署在一起。它的语法结构如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: <label-key>
            operator: <operator>
            values:
            - <value1>
            - <value2>
            ...
        topologyKey: <topology-key>
  containers:
  - name: nginx
    image: nginx
  • labelSelector:一个 LabelSelector 对象,用于选择满足条件的 Pod。
    • matchExpressions:一个或多个 matchExpression 的列表,每个 matchExpression 定义了一个具体的匹配规则,与节点亲和性中的 matchExpression 结构相同。
  • topologyKey:一个节点标签键,用于指定亲和性的作用范围。例如,如果 topologyKey 设置为 kubernetes.io/hostname,则表示亲和性只在同一个节点上生效。

举个栗子🌰:

假设你希望将 Pod 调度到与拥有标签 app=web 的 Pod 部署在同一个节点上,你可以这样配置:

apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - web
        topologyKey: kubernetes.io/hostname
  containers:
  - name: nginx
    image: nginx

这意味着,只有与拥有 app=web 标签的 Pod 部署在同一个节点上的节点,才能成为该 Pod 的候选节点。

3.2.2 podAntiAffinity 详解

Pod 反亲和性用于控制 Pod 避免与哪些 Pod 部署在一起。它的语法结构与 podAffinity 类似:

apiVersion: v1
kind: Pod
metadata:
  name: pod-anti-affinity
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: <label-key>
            operator: <operator>
            values:
            - <value1>
            - <value2>
            ...
        topologyKey: <topology-key>
  containers:
  - name: nginx
    image: nginx
  • labelSelector:一个 LabelSelector 对象,用于选择需要避免的 Pod。
  • topologyKey:一个节点标签键,用于指定反亲和性的作用范围。

举个栗子🌰:

假设你希望避免将 Pod 调度到与拥有标签 app=db 的 Pod 部署在同一个节点上,你可以这样配置:

apiVersion: v1
kind: Pod
metadata:
  name: pod-anti-affinity
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - db
        topologyKey: kubernetes.io/hostname
  containers:
  - name: nginx
    image: nginx

这意味着,拥有 app=db 标签的 Pod 所在的节点,将不会成为该 Pod 的候选节点。

四、 实战演练:打造你的专属调度策略

理论知识讲了一大堆,现在咱们来做一些实战演练,看看如何将这些高级亲和性与反亲和性规则应用到实际场景中。

场景一:高可用 Web 应用

假设你有一个 Web 应用,为了保证高可用性,你希望将多个 Pod 分散到不同的节点上,并且尽量部署在不同的可用区(Zone)中。

你可以这样配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - web
              topologyKey: kubernetes.io/hostname
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 50
            preference:
              matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: Exists
      containers:
      - name: web-app
        image: nginx
  • podAntiAffinity:避免将多个 app=web 的 Pod 部署在同一个节点上。
  • nodeAffinity:尽量将 Pod 部署在不同的可用区中。

场景二:高性能计算任务

假设你有一个计算密集型的任务,需要大量的 CPU 资源。你希望将相关的 Pod 部署在同一个节点上,利用节点上的所有 CPU 核心,提高计算效率。

你可以这样配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: compute-task
spec:
  replicas: 2
  selector:
    matchLabels:
      app: compute
  template:
    metadata:
      labels:
        app: compute
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - compute
            topologyKey: kubernetes.io/hostname
      containers:
      - name: compute-task
        image: busybox
        command: ["/bin/sh", "-c", "while true; do echo 'Computing...'; sleep 1; done"]
  • podAffinity:将多个 app=compute 的 Pod 部署在同一个节点上。

五、 注意事项:避开那些“坑”

在使用高级亲和性与反亲和性时,有一些注意事项需要特别关注:

  • 循环依赖: 避免出现循环依赖的情况,例如 Pod A 依赖 Pod B,Pod B 又依赖 Pod A,这会导致调度器无法找到合适的节点。
  • 资源不足: 确保集群有足够的资源满足 Pod 的调度需求,否则可能会导致 Pod 无法调度。
  • 标签管理: 保持节点和 Pod 标签的一致性和准确性,否则可能会导致亲和性规则失效。
  • 测试验证: 在生产环境中使用之前,务必进行充分的测试验证,确保调度策略符合预期。

六、 总结:让 K8s 成为你的“调度大师”

高级亲和性与反亲和性是 Kubernetes 中非常强大的调度工具,它们可以帮助你实现更复杂的调度策略,提高应用的可用性、性能和安全性。

希望通过今天的分享,大家能够对 K8s 的高级亲和性与反亲和性有更深入的了解,并能够灵活运用这些规则,打造你的专属调度策略,让 K8s 成为你的“调度大师”!

最后,送给大家一句名言:“代码虐我千百遍,我待代码如初恋!” 让我们一起在 K8s 的世界里,不断探索、学习、进步!🎉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注