親和性與反親和性


nodeSelector 提供了一個非常簡單的方式,將 Pod 限定到包含特定標簽的節點上。親和性與反親和性(affinity / anti-affinity)特性則極大地擴展了限定的表達方式。主要的增強點在於:

  1. 表達方式更加有效(不僅僅是多個精確匹配表達式的“和”關系)
  2. 可以標識該規則為“soft” / “preference” (軟性的、偏好的)而不是 hard requirement(必須的),此時,如果調度器發現該規則不能被滿足,Pod 仍然可以被調度
  3. 可以對比節點上(或其他拓撲域 topological domain)已運行的其他 Pod 的標簽,而不僅僅是節點自己的標簽,此時,可以定義類似這樣的規則:某兩類 Pod 不能在同一個節點(或拓撲域)上共存

# 節點親和性

節點親和性(node affinity)的概念與 nodeSelector 相似,可以基於節點的標簽來限定 Pod 可以被調度到哪些節點上。

當前支持兩種類型的節點親和性, requiredDuringSchedulingIgnoredDuringExecution (hard,目標節點必須滿足此條件) 以及 preferredDuringSchedulingIgnoredDuringExecution (soft,目標節點最好能滿足此條件)。名字中 IgnoredDuringExecution 意味着:如果 Pod 已經調度到節點上以后,節點的標簽發生改變,使得節點已經不再匹配該親和性規則了,Pod 仍將繼續在節點上執行(這一點與 nodeSelector 相似)。將來,Kubernetes 將會提供 requiredDuringSchedulingRequiredDuringExecution 這個選項,該選項與 requiredDuringSchedulingIgnoredDuringExecution 相似,不同的是,當節點的標簽不在匹配親和性規則之后,Pod 將被從節點上驅逐。

requiredDuringSchedulingIgnoredDuringExecution 的一個例子是,只在 Intel CPU 上運行該 PodpreferredDuringSchedulingIgnoredDuringExecution 的一個例子是,盡量在高可用區 XYZ 中運行這個 Pod,但是如果做不到,也可以在其他地方運行該 Pod

PodSpec 中通過 affinity.nodeAffinity 字段來定義節點親和性,示例文件如下:

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-name 且 value 為 e2e-az1e2e-az2 的標簽的節點上。此外,如果節點已經滿足了前述條件,將優先選擇包含 key 為 another-node-label-key 且 value 為 another-node-label-value 的標簽的節點。

例子中使用了操作符 In。節點親和性支持如下操作符:InNotInExistsDoesNotExistGtLt。使用 NotInDoesNotExist 可以實現節點反親和性(node anti-affinity)的效果,或者也可以使用 污點 為節點排斥某類 Pod。

如果某個 Pod 同時指定了 nodeSelectornodeAffinity,則目標節點必須同時滿足兩個條件,才能將 Pod 調度到該節點上。

如果為 nodeAffinity 指定多個 nodeSelectorTerms,則目標節點只需要滿足任意一個 nodeSelectorTerms 的要求,就可以將 Pod 調度到該節點上。

如果為 nodeSelectorTerms 指定多個 matchExpressions,則目標節點必須滿足所有的 matchExpressions 的要求,才能將 Pod 調度到該節點上。

當 Pod 被調度到某節點上之后,如果移除或者修改節點的標簽,Pod 將仍然繼續在節點上運行。換句話說,節點親和性規則只在調度該 Pod 時發生作用。

preferredDuringSchedulingIgnoredDuringExecution 中的 weight 字段取值范圍為 1-100。對於每一個滿足調度要求的節點(資源請求、親和性/反親和性規則,等),調度器將遍歷該節點匹配的 preferredDuringSchedulingIgnoredDuringExecution 中所有的weight 並求和。此求和結果將與節點的其他優先級計算的得分合並。得分最高的節點被優先選擇。

# Pod親和性與反親和性

Pod之間的親和性與反親和性(inter-pod affinity and anti-affinity)可以基於已經運行在節點上的 Pod 的標簽(而不是節點的標簽)來限定 Pod 可以被調度到哪個節點上。此類規則的表現形式是:

  • 當 X 已經運行了一個或者多個滿足規則 Y 的 Pod 時,待調度的 Pod 應該(或者不應該 - 反親和性)在 X 上運行

    • 規則 Y 以 LabelSelector 的形式表述,附帶一個可選的名稱空間列表

      與節點不一樣,Pod 是在名稱空間中的(因此,Pod的標簽是在名稱空間中的),針對 Pod 的 LabelSelector 必須同時指定對應的名稱空間

    • X 是一個拓撲域的概念,例如節點、機櫃、雲供應商可用區、雲供應商地域,等。X 以 topologyKey 的形式表達,該 Key代表了節點上代表拓撲域(topology domain)的一個標簽。

# pod affinity 的一個例子

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 的 affinity 定義了一個 Pod 親和性規則和一個 Pod 反親和性規則,例子中, podAffinityrequiredDuringSchedulingIgnoredDuringExecution,而 podAntiAffinity 則是 preferredDuringSchedulingIgnoredDuringExecution

  • Pod 親和性規則要求,該 Pod 可以被調度到的節點所在的可用區 zone 必須已經有一個已經運行的 Pod 包含標簽 key=security,value=S1,或者更准確地說,節點必須滿足如下條件:
    • 節點包含 key 為 failure-domain.beta.kubernetes.io/zone 的標簽,假設該標簽的值為 V
    • 至少有一個包含 key 為 failure-domain.beta.kubernetes.io/zone 且 value 為 V 的標簽的節點已經運行了一個包含標簽 key 為 security 且 value 為 S1 的 Pod
  • Pod 反親和性規則要求,該 Pod 最好不要被調度到已經運行了包含 key 為 security 且 value 為 S2 的標簽的 Pod 的節點上,或者更准確地說,必須滿足如下條件:
    • 如果 topologyKeyfailure-domain.beta.kubernetes.io/zone,則,Pod不能被調度到同一個 zone 中的已經運行了包含標簽 security: S2 的節點上

參考 design doc

可以了解更多 Pod 親和性與反親和性的例子。

原則上, topologyKey 可以是任何合法的標簽 key。然而,處於性能和安全的考慮,仍然對 topologyKey 有如下限制:

  1. 對親和性以及 requiredDuringSchedulingIgnoredDuringExecution Pod 反親和性,topologyKey 不能為空
  2. requiredDuringSchedulingIgnoredDuringExecution Pod 反親和性,管理控制器 LimitPodHardAntiAffinityTopology 被用來限制 topologyKey 必須為 kubernetes.io/hostname。如果想要使用其他的自定義 topology,必須修改該管理控制器,或者將其禁用
  3. preferredDuringSchedulingIgnoredDuringExecution Pod 反親和性,如果 topologyKey 為空,則代表所有的 topology (此時,不局限於 kubernetes.io/hostnamefailure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/region 的組合)
  4. 除了上述的情形以外,topologyKey 可以是任何合法的標簽 Key

除了 labelSelectortopologyKey 以外,還可以指定一個 namespaces 的列表,用作 labelSelector 的作用范圍(與 labelSelectortopologyKey 的定義為同一個級別)。如果不定義或者該字段為空,默認為 Pod 所在的名稱空間。

所有與 requiredDuringSchedulingIgnoredDuringExecution 親和性和反親和性關聯的 matchExpressions 必須被滿足,Pod 才能被調度到目標節點。

# 更多實用的例子

Pod 親和性與反親和性結合高級別控制器(例如 ReplicaSet、StatefulSet、Deployment 等)一起使用時,可以非常實用。此時可以很容易的將一組工作復雜調度到同一個 topology,例如,同一個節點。

# 始終在同一個節點

在一個三節點的集群中,部署一個使用 redis 的 web 應用程序,並期望 web-server 盡可能與 redis 在同一個節點上。

下面是 redis deployment 的 yaml 片段,包含三個副本以及 app=store 標簽選擇器。Deployment 中配置了 PodAntiAffinity,確保調度器不會將三個副本調度到一個節點上:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  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

下面是 webserver deployment 的 yaml 片段,配置了 podAntiAffinity 以及 podAffinity。要求將其副本與 包含 app=store 標簽的 Pod 放在同一個節點上;同時也要求 web-server 的副本不被調度到同一個節點上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  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

如果創建上述兩個 deployment,集群將如下所示:

Node-1 Node-2 Node-3
web-server-1 webserver-2 webserver-3
cache-1 cache-2 cache-3

web-server 的三個副本都自動與 cach 的副本運行在相同的節點上。

kubectl get pods -o wide    

輸出結果如下所示

NAME                           READY     STATUS    RESTARTS   AGE       IP           NODE
redis-cache-1450370735-6dzlj   1/1       Running   0          8m        10.192.4.2   kube-node-3
redis-cache-1450370735-j2j96   1/1       Running   0          8m        10.192.2.2   kube-node-1
redis-cache-1450370735-z73mh   1/1       Running   0          8m        10.192.3.1   kube-node-2
web-server-1287567482-5d4dz    1/1       Running   0          7m        10.192.2.3   kube-node-1
web-server-1287567482-6f7v5    1/1       Running   0          7m        10.192.4.3   kube-node-3
web-server-1287567482-s330j    1/1       Running   0          7m        10.192.3.2   kube-node-2    

# 始終不在相同的節點上

上面的例子使用了 PodAntiAffinity 規則與 topologyKey: "kubernetes.io/hostname" 來部署 redis 集群,因此沒有任何兩個副本被調度到同一個節點上。參考 ZooKeeper tutorial

了解如何使用相同的方式為 StatefulSet 配置反親和性以實現高可用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM