Skip to content

亲和性

  • https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
  • https://kuboard.cn/learning/k8s-intermediate/config/affinity.html#%E8%8A%82%E7%82%B9%E4%BA%B2%E5%92%8C%E6%80%A7

你可以约束 pod 只能或倾向于 在哪些节点上面运行。有好几种方法可以达到此效果,推荐使用的 标签选择器 来进行选择。

1. nodeSelector

nodeSelector 是推荐的最简单的节点选择约束形式。它是 PodSpec 的一个字段。要使 pod 有资格在一个节点上运行,该节点必须有每个指定的键值对作为标签(它还可以有其他标签)。

1.向节点附加标签

kubectl label nodes <node-name> <label-key>=<label-value>

2.向 pod 添加 nodeSelector 部分

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

2. 节点 隔离/限制(Node isolation/restriction)

当你使用 nodeSelector 时,在某些情况下 kubelet 可能会改变节点的标签值,从而导致意外的结果。现在你可以使用 NodeRestriction 插件来禁止 kubelet 修改带有 node-restriction.kubernetes.io/ 前缀的标签:

  1. 确保你使用了 Node authorizer ,并且启用了 NodeRestriction admission plugin
  2. 在 node 与 pod 中使用带有 node-restriction.kubernetes.io/ 前缀的标签。例如 example.com.node-restriction.kubernetes.io/fips=true

3. 亲和性与反亲和性

nodeSelector 提供了一种将 pod 约束到特定节点的方法,亲和性与反亲和性(affinity/anti-affinity)则功能极大地扩展了可以表达的约束类型。关键的增强是:

  1. affinity/anti-affinity语法更富有表达力。语法不只有“与”操作;
  2. 可以将规则声明为 “软”/“偏好” 的,而不是硬性要求;
  3. 可以根据在节点上面运行的其它 pod 的标签来约束 pod,而不是只根据自身的标签,这可以让一些 pod 要/不要 共处一地。

根据上述的描述,亲和性功能包含两个部分:

  • pod 与 node 间的亲和性(node affinity);
  • pods 之间的亲和性(inter-pod affinity)。

3.1 node affinity

节点亲和性与 nodeSelector 类似,通过 node 的标签来约束 pod。

当前(v1.18)版本有两中类型的 node affinity :

  • requiredDuringSchedulingIgnoredDuringExecution:硬性要求,但是比nodeSelector有更多的语法。
  • preferredDuringSchedulingIgnoredDuringExecution:偏好要求。

在 PodSpec 中的 affinity 下的 nodeAffinity 下面定义 node affinity ,例子:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

这个规则定义了:该 pod 只能在带有 key=kubernetes.io/e2e-az-namevaluee2e-az1e2e-az2 的node上面运行。另外,在满足这些约束的 nodes 中,偏向选择那个 key=another-node-label-keyvalue=another-node-label-value 的那个节点。

操作符(operator)有以下几种:

  • In
  • NotIn
  • Exists
  • DoesNotExist
  • Gt
  • Lt

可以使用NotInDoesNotExist来表达 反亲和性。

下面有几点要注意的地方:

  • 若同时指定了 nodeSelectornodeAffinity,则两者都要满足;
  • 若在nodeAffinity下面指定了多个nodeSelectorTerms,若 node 满足任意一个nodeSelectorTerms,那么 pod 可以被调度;
  • 若在nodeSelectorTerms下面指定了多个matchExpressions,则 node 必须满足所有matchExpressions, pod 才可以被调度;
  • 亲和性功能只适用于 pod 被调度的时候,在调度完成之后不会被驱逐。

preferredDuringSchedulingIgnoredDuringExecution 下的 weight 字段的范围是 1~100

3.2 Inter-pod affinity and anti-affinity

规则的形式是 “如果 X 已经运行了一个或多个满足规则 Y 的 pod,那么这个 pod 应该(或者,在反亲和性的情况下,不应该)运行在一个 X 中”

Y 表示为 labelSelector,并带有一个可选的命名空间关联列表;与节点不同,因为 pod 是属于命名空间的(因此 pod 上的标签是隐式命名空间的),所以 pod 标签上的标签选择器必须指定选择器应该应用于哪些名称空间。

从概念上讲,X 是一个拓扑域,例如节点,机架,云提供商区域等。可以使用 topologyKey来表示它,它是系统用来表示这种拓扑域的节点标签的键。

与节点亲和性一样,当前(v1.18)版本有两中类型的 inter-pod affinity :

  • requiredDuringSchedulingIgnoredDuringExecution:硬性要求。
  • preferredDuringSchedulingIgnoredDuringExecution:偏好要求。

表达的意思也是类似的,不再赘述。

在 PodSpec 中的 affinity 下面:

  • podAffinity 表示亲和性
  • podAntiAffinity 表示反亲和性

例子:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  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: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

该 pod 只能运行在含有 failure-domain.beta.kubernetes.io/zone 标签的 node (且该 node 上有符合规则的 pod )的上面。

操作符(operator)有以下几种:

  • In
  • NotIn
  • Exists
  • DoesNotExist

理论上讲, topologyKey 可以是任何合法的 标签键,但是为了性能和安全着想,下面有几条约束:

  • 不允许为空
  • 对于 pod 反亲和性的 requiredDuringSchedulingIgnoredDuringExecution ,可以引入 admission 控制器 LimitPodHardAntiAffinityTopology 来限制 topologyKeykubernetes.io/hostname

上面说到 pod 之间的亲和性需要指定 命名空间,命名空间列表位于配置文件中与 labelSelector and topologyKey 同级别的位置,若为空或没有指定,那么是该 pod 的命名空间。

所有与requiredDuringSchedulingIgnoredDuringExecution关联的matchExpressions都必须同时满足。

4. nodeName

nodeName 是最简单的节点选择约束了,但限制性很大:

  • 若节点不存在,pod 不会运行,并且在某些情况下会被自动删除。
  • 若节点没有足够的资源,pod 会失败并给出原因,例如 OutOfmemory or OutOfcpu。
  • 云环境中的节点名称并不总是可预测或稳定的。

例子:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01