• k8s 中的亲和性和反亲和性


    通常情况下,Pod分配到哪些Node是不需要管理员操心的,这个过程会由scheduler自动实现,因为调度程序会自动进行合理的调度(如通过一系列的评分机制将 pods 合理分配到最优节点上,而不会将 pod 分配在没有足够资源的节点上等)。但有时,我们需要指定一些调度的限制,例如某些应用应该跑在具有SSD存储的节点上,或者将两个通信比较频繁的不同服务 pod 调度到同一个可用域等等。

    labels 在 K8s 中是一个很重要的概念,作为一个标识,Service、Deployments 和 Pods 之间的关联都是通过 label 来实现的。而每个节点也都拥有 label ,通过设置 label 相关的策略可以使得 pods 关联到对应 label 的节点上。

    nodeSelector

    首先我们为Node规划标签,然后在创建部署的时候,通过使用nodeSelector标签来指定Pod运行在哪些节点上。

    nodeSelector 是最简单的约束方式。 nodeSelector 是 PodSpec 的一个字段。

    通过 --show-labels 可以查看当前 nodes 的 labels

    1. $ kubectl get nodes --show-labels
    2. NAME STATUS ROLES AGE VERSION LABELS
    3. minikube Ready 1m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/
    4. hostname=minikube

    如果没有额外添加 nodes labels ,那么看到的如上所示的默认标签。我们可以通过 kubectl label node 命令给指定 node 添加 labels :

    1. $ kubectl label node minikube disktype=ssd
    2. node/minikube labeled
    3. $ kubectl get nodes --show-labels
    4. NAME STATUS ROLES AGE VERSION LABELS
    5. minikube Ready 5m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=minikube

    当然,你也可以通过 kubectl label node 删除指定的 labels (标签 key 接 - 号即可)

    1. $ kubectl label node minikube disktype-
    2. node/minikube labeled
    3. $ kubectl get node --show-labels
    4. NAME STATUS ROLES AGE VERSION LABELS
    5. minikube Ready 23m v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=minikube

    创建测试 pod 并指定 nodeSelector 选项绑定节点:

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: nginx
    5. labels:
    6. env: test
    7. spec:
    8. containers:
    9. - name: nginx
    10. image: docker.io/nginx
    11. imagePullPolicy: IfNotPresent
    12. nodeSelector:
    13. disktype: ssd

    nodeSelector 可以很方便的解决以上比较简单的需求,但是它还不够灵活。比如我想部署的服务可以很好的分散在不同机架的服务器上,此时 nodeSelector 就并不是那么管用了。因此,Kubernetes 引入了亲和性和反亲和性概念。

    Affinity and anti-affinity

    nodeSelector的调度方式略显简单,通过亲和和反亲和配置,能够为调度提供更灵活的策略,主要有以下几点增强:

    • 更多的表达式支持,不仅仅是ADD和精确匹配了
    • 可以设置soft/preference的调度策略,而不是刚性的要求
    • 可以通过Pod的标签进行调度约束,不仅仅是Node的标签

    affinity 特性拥有两种类型,一种是 node affinity,一种是 pod affinity/anti-affinity。

    node affinity 类似 nodeSelector ,但同时拥有上文提到的 1、2 两点优势

    pod affinity/anti-affinity 针对 pods 指定 labels ,同时拥有以上三点优势。

    Node affinity

    Node affinity 是 Kubernetes 1.2版本后引入的新特性,类似于nodeSelector,允许我们指定一些Pod在Node间调度的约束。

    Node affinity 支持两种形式:

    • requiredDuringSchedulingIgnoredDuringExecution (硬限制, 同nodeSelector)
    • preferredDuringSchedulingIgnoredDuringExecution(软限制)

    可以认为前一种是必须满足,如果不满足则不进行调度,后一种是倾向满足,不满足的情况下会调度的不符合条件的Node上。

    IgnoreDuringExecution表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。

    下面看一个例子

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: nginx
    5. spec:
    6. affinity:
    7. nodeAffinity:
    8. requiredDuringSchedulingIgnoredDuringExecution:
    9. nodeSelectorTerms:
    10. - matchExpressions:
    11. - key: kubernetes.io/e2e-az-name
    12. operator: In
    13. values:
    14. - e2e-az1
    15. - e2e-az2
    16. preferredDuringSchedulingIgnoredDuringExecution:
    17. - weight: 1 //取值范围1-100
    18. preference:
    19. matchExpressions:
    20. - key: another-node-label-key
    21. operator: In
    22. values:
    23. - another-node-label-value
    24. containers:
    25. - name: nginx
    26. image: docker.io/nginx

    以上规则表达的意思是,该 Pod 只能被调度到拥有 kubernetes.io/e2e-az-name=e2e-az1 或者 kubernetes.io/e2e-az-name=e2e-az2 标签的节点上,其中在满足之前标签条件的同时更倾向于调度在拥有 another-node-label-key=another-node-label-value 标签的节点上。

    标签判断的操作符除了使用In之外,还可以使用NotInExistsDoesNotExistGtLt 操作符, 也可以使用 NotIn 、 DoesNotExist 来实现反亲和性,也可以通过​ node taints ​来实现。

    1)如果同时指定 nodeSelector 和 nodeAffinity ,两者同时满足才会被调度。

    2)如果指定多个nodeSelectorTerms则只要满足其中一个条件,就会被调度到相应的节点上。

    3)如果指定多个matchExpressions则所有的条件都必须满足,才会调度到对应的节点。

    inter-pod affinity/anti-affinity

    这个特性是Kubernetes 1.4后增加的,允许用户通过已经运行的Pod上的标签来决定调度策略,而不是节点的标签。 用文字描述就是“如果Node X上运行了一个或多个满足Y条件的Pod,那么这个Pod在Node应该运行在Pod X”,因为Node没有命名空间,Pod有命名空间,这样就允许管理员在配置的时候指定这个亲和性策略适用于哪个命名空间,可以通过topologyKey来指定。topology是一个范围的概念,可以是一个Node、一个机柜、一个机房或者是一个区域(如北美、亚洲)等,实际上对应的还是Node上的标签。

    同 node affinity,pod 亲和性和反亲和性也有两种类型:

    • requiredDuringSchedulingIgnoredDuringExecution,硬性要求,必须精确匹配
    • preferredDuringSchedulingIgnoredDuringExecution,软性要求

    pod 亲和性和反亲和性需要大量的计算,会显著降低集群的调度速度,不建议在大于几百个节点的集群中使用。 pod 反亲和性要求集群中的所有节点必须具有 topologyKey 匹配的标签,否则可能会导致意外情况发生。

    同的是,pod 通过 podAntiAffinity 设置反亲和性,如下例子:

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: with-pod-affinity
    5. spec:
    6. affinity:
    7. podAffinity:
    8. requiredDuringSchedulingIgnoredDuringExecution:
    9. - labelSelector:
    10. matchExpressions:
    11. - key: security
    12. operator: In
    13. values:
    14. - S1
    15. topologyKey: failure-domain.beta.kubernetes.io/zone
    16. podAntiAffinity:
    17. preferredDuringSchedulingIgnoredDuringExecution:
    18. - weight: 100
    19. podAffinityTerm:
    20. labelSelector:
    21. matchExpressions:
    22. - key: security
    23. operator: In
    24. values:
    25. - S2
    26. topologyKey: kubernetes.io/hostname
    27. containers:
    28. - name: with-pod-affinity
    29. image: k8s.gcr.io/pause:2.0

    以上示例表示,pod 必须调度在至少运行一个 security=S1 标签的 pod 的节点上(更准确的说,这个 pod 可以运行在节点 N 上,如果该节点有标签 key 为 failure-domain.beta.kubernetes.io/zone ,而且运行着标签为 security=S1 的实例)。

    另外,反亲和规则表明最好不要调度到运行有 security=S2 标签的 pod 的节点上(更准确的说,如果这个节点拥有标签 key 为 failure-domain.beta.kubernetes.io/zone ,但运行有 security=S2 标签的 pod,那么这个节点就不会被优先选择调度)。

    总结

    podAffinity 和 podAntiAffinity 支持 In 、 NotIn 、 Exists 、 DoesNotExist 四种表达式。

    原则上, topologyKey 可以为任何合法的键值对。但是因为性能和安全的原因,有以下限制:

    1. 针对 podAffinity 和 podAntiAffinity 中的 requiredDuringSchedulingIgnoredDuringExecution , topologyKey 为空是不允许的
    2. 针对 podAntiAffinity 中的 requiredDuringSchedulingIgnoredDuringExecution ,准入控制器选项 LimitPodHardAntiAffinityTopology 可以把 topologyKey 限制为 kubernetes.io/hostname ,如果想自定义值,可以修改准入控制器或者直接禁用
    3. 针对 podAntiAffinity 中的 preferredDuringSchedulingIgnoredDuringExecution , topologyKey 为空,则代表所有拓扑(仅限于 kubernetes.io/hostname , failure-domain.beta.kubernetes.io/zone and failure-domain.beta.kubernetes.io/region )
    4. 除了以上情况, topologyKey 可以为任意合法的键值对

    除了 labelSelector 和 topologyKey ,还可以指定 namespace 的 labelSelector 作为匹配。 labelSelector 和 topologyKey 属于同一级别,如果未定义或设置为空值,那么默认为定义 pod affinity 和 anti-affinity 所在的空间。

    实践案例

    始终调度在同一个节点

    在一个三个节点的集群中,一个 web 应用程序依赖内存存储,如 redis,我们想 web 程序尽可能的和缓存调度在同一个节点上。redis 的 Deployment 配置如下:

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: redis-cache
    5. spec:
    6. selector:
    7. matchLabels:
    8. app: store
    9. replicas: 3
    10. template:
    11. metadata:
    12. labels:
    13. app: store
    14. spec:
    15. affinity:
    16. podAntiAffinity:
    17. requiredDuringSchedulingIgnoredDuringExecution:
    18. - labelSelector:
    19. matchExpressions:
    20. - key: app
    21. operator: In
    22. values:
    23. - store
    24. topologyKey: "kubernetes.io/hostname"
    25. containers:
    26. - name: redis-server
    27. image: redis:3.2-alpine

    上面的例子中,创建了一个具有三个实例的部署,采用了Pod间的反亲和策略,限制创建的实例的时候,如果节点上已经存在具有相同标签的实例,则不进行部署,避免了一个节点上部署多个相同的实例。

    web-server 规则设置如下,表达 pod 不被调度在同一个节点上,并且必须调度在运行标签 app=store pod 的节点上。

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: web-server
    5. spec:
    6. selector:
    7. matchLabels:
    8. app: web-store
    9. replicas: 3
    10. template:
    11. metadata:
    12. labels:
    13. app: web-store
    14. spec:
    15. affinity:
    16. podAntiAffinity:
    17. requiredDuringSchedulingIgnoredDuringExecution:
    18. - labelSelector:
    19. matchExpressions:
    20. - key: app
    21. operator: In
    22. values:
    23. - web-store
    24. topologyKey: "kubernetes.io/hostname"
    25. podAffinity:
    26. requiredDuringSchedulingIgnoredDuringExecution:
    27. - labelSelector:
    28. matchExpressions:
    29. - key: app
    30. operator: In
    31. values:
    32. - store
    33. topologyKey: "kubernetes.io/hostname"
    34. containers:
    35. - name: web-app
    36. image: nginx:1.12-alpine
  • 相关阅读:
    Guava限流器原理浅析
    鸿蒙应用开发中使用axios出错
    无叶风扇32位MCU单片机MM32SPIN0230
    Zookeeper重要概念
    【数据结构】搜索二叉树(C++实现)
    要让程序员在代码中添加注释
    大模型对外提供应用的三种服务方式及示例
    【计算机网络】-性能指标(速率、带宽和吞吐量)
    【C++】怎么接受未知数量的参数?
    树的直径(dp和bfs求法)
  • 原文地址:https://blog.csdn.net/echizao1839/article/details/126266027