写点什么

Kubernetes 中的亲和性与反亲和性

作者:xcbeyond
  • 2021 年 12 月 28 日
  • 本文字数:2978 字

    阅读完需:约 10 分钟

通常情况下,Pod 分配到哪些 Node 是不需要管理员操心的,这个过程会由 scheduler 自动实现。但有时,我们需要指定一些调度的限制,例如某些应用应该跑在具有 SSD 存储的节点上,有些应用应该跑在同一个节点上等等。

nodeSelector

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

apiVersion: v1kind: Podmetadata:  name: nginx  labels:    env: testspec:  containers:  - name: nginx    image: docker.io/nginx    imagePullPolicy: IfNotPresent  nodeSelector:    disktype: ssd
复制代码

nodeSelector 将会在后续的版本中废除,还是建议大家使用使用亲和性策略。

亲和与反亲和 Affinity and anti-affinity

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

  • 更多的表达式支持,不仅仅是 ADD 和精确匹配了

  • 可以设置 soft/preference 的调度策略,而不是刚性的要求

  • 可以通过 Pod 的标签进行调度约束,不仅仅是 Node 的标签

亲和性特性包含两种方式

节点亲和性 Node affinity

Node affinity 是 Kubernetes 1.2 版本后引入的新特性,类似于 nodeSelector,允许我们指定一些 Pod 在 Node 间调度的约束。支持两种形式:requiredDuringSchedulingIgnoredDuringExecutionpreferredDuringSchedulingIgnoredDuringExecution,可以认为前一种是必须满足,如果不满足则不进行调度,后一种是倾向满足,不满足的情况下会调度的不符合条件的 Node 上。IgnoreDuringExecution表示如果在 Pod 运行期间 Node 的标签发生变化,导致亲和性策略不能满足,则继续运行当前的 Pod。

apiVersion: v1kind: Podmetadata:  name: nginxspec:  affinity:    nodeAffinity:      requiredDuringSchedulingIgnoredDuringExecution:        nodeSelectorTerms:        - matchExpressions:          - key: kubernetes.io/e2e-az-name            operator: In            values:            - e2e-az1            - e2e-az2      preferredDuringSchedulingIgnoredDuringExecution:      - weight: 1               //取值范围1-100        preference:          matchExpressions:          - key: another-node-label-key            operator: In            values:            - another-node-label-value  containers:  - name: nginx    image: docker.io/nginx
复制代码

标签判断的操作符除了使用In之外,还可以使用NotInExistsDoesNotExistGtLt。如果指定多个nodeSelectorTerms,则只要满足其中一个条件,就会被调度到相应的节点上。如果指定多个matchExpressions,则所有的条件都必须满足才会调度到对应的节点。

Pod 间的亲和性与反亲和性 inter-pod affinity/anti-affinity

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

有两种类型

  • requiredDuringSchedulingIgnoredDuringExecution,刚性要求,必须精确匹配

  • preferredDuringSchedulingIgnoredDuringExecution,软性要求

apiVersion: v1kind: Podmetadata:  name: with-pod-affinityspec:  affinity:    podAffinity:      requiredDuringSchedulingIgnoredDuringExecution:      - labelSelector:          matchExpressions:          - key: security            operator: In            values:            - S1        topologyKey: failure-domain.beta.kubernetes.io/zone    podAntiAffinity:      preferredDuringSchedulingIgnoredDuringExecution:      - weight: 100        podAffinityTerm:          labelSelector:            matchExpressions:            - key: security              operator: In              values:              - S2          topologyKey: kubernetes.io/hostname  containers:  - name: with-pod-affinity    image: k8s.gcr.io/pause:2.0
复制代码

标签的判断操作支持InNotInExistsDoesNotExist。 原则上topologyKey可以是节点的合法标签,但是有一些约束:

  • 对于亲和性以及 RequiredDuringScheduling 的反亲和性,topologyKey 需要指定

  • 对于 RequiredDuringScheduling 的反亲和性,LimitPodHardAntiAffinityTopology 的准入控制限制 topologyKey 为 kubernetes.io/hostname,可以通过修改或者 disable 解除该约束

  • 对于 PreferredDuringScheduling 的反亲和性,空的 topologyKey 表示 kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone and failure-domain.beta.kubernetes.io/region 的组合.

  • topologyKey 在遵循其他约束的基础上可以设置成其他的 key.

常用场景

apiVersion: apps/v1kind: Deploymentmetadata:  name: redis-cachespec:  selector:    matchLabels:      app: store  replicas: 3  template:    metadata:      labels:        app: store    spec:      affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - store            topologyKey: "kubernetes.io/hostname"      containers:      - name: redis-server        image: redis:3.2-alpine
复制代码

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

apiVersion: apps/v1kind: Deploymentmetadata:  name: web-serverspec:  selector:    matchLabels:      app: web-store  replicas: 3  template:    metadata:      labels:        app: web-store    spec:      affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - web-store            topologyKey: "kubernetes.io/hostname"        podAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - store            topologyKey: "kubernetes.io/hostname"      containers:      - name: web-app        image: nginx:1.12-alpine
复制代码

再创建 3 个 Web 服务的实例,同上面Redis的配置,首先确保两个 Web 不会部署到相同的节点,然后在应用 Pod 间亲和策略,优先在有 Redis 服务的节点上部署 Web。

用户头像

xcbeyond

关注

🚩InfoQ写作平台签约作者 2019.06.20 加入

专注于技术输出、分享。 博客:https://xcbeyond.cn 公众号:程序猿技术大咖

评论

发布
暂无评论
Kubernetes中的亲和性与反亲和性