理解 Kubernetes 的親和性調度


 

這次給大家介紹下k8s的親和性調度:nodeSelector、nodeAffinity、podAffinity、Taints以及Tolerations用法。

一般情況下我們部署的 POD 是通過集群自動調度選擇某個節點的,默認情況下調度器考慮的是資源足夠,並且負載盡量平均,但是有的時候我們需要能夠更加細粒度的去控制 POD 的調度,比如我們內部的一些服務 gitlab 之類的也是跑在Kubernetes集群上的,我們就不希望對外的一些服務和內部的服務跑在同一個節點上了,害怕內部服務對外部的服務產生影響;有的時候呢我們兩個服務直接交流比較頻繁,又希望能夠將這兩個服務的 POD 調度到同樣的節點上。這就需要用到 Kubernetes 里面的一個概念:親和性,親和性主要分為兩類:nodeAffinitypodAffinity

nodeSelector

我們知道labelkubernetes中一個非常重要的概念,用戶可以非常靈活的利用 label 來管理集群中的資源,比如最常見的一個就是 service 通過匹配 label 去選擇 POD 的。而 POD 的調度也可以根據節點的 label 進行特定的部署。

我們可以通過下面的命令查看我們的 node 的 label:

$ kubectl get nodes --show-labels
NAME            STATUS    ROLES     AGE       VERSION   LABELS
192.168.1.140   Ready     <none>    42d       v1.8.1    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=192.168.1.140
192.168.1.161   Ready     <none>    118d      v1.8.1    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.161
192.168.1.170   Ready     <none>    118d      v1.8.1    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.170
192.168.1.172   Ready     <none>    114d      v1.8.1    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/cluster-service=true,kubernetes.io/hostname=192.168.1.172

現在我們先給節點192.168.1.140增加一個source=qikqiak的標簽,命令如下:

$ kubectl label nodes 192.168.1.140 source=qikqiak
node "192.168.1.140" labeled

我們可以通過上面的--show-labels參數可以查看上述標簽是否生效。當 node 被打上了相關標簽后,在調度的時候就可以使用這些標簽了,只需要在 POD 的 spec 字段中添加nodeSelector字段,里面是我們需要被調度的節點的 label。例如,下面是我們之前的一個默認的 busybox POD 的 YAML 文件:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: busybox-pod
  name: test-busybox
spec:
  containers:
  - command:
    - sleep
    - "3600"
    image: busybox
    imagePullPolicy: Always
    name: test-busybox

然后我需要讓上面的 POD 被調度到140的節點上,那么最簡單的方法就是去匹配140上面的 label,如下:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: busybox-pod
  name: test-busybox
spec:
  containers:
  - command:
    - sleep
    - "3600"
    image: busybox
    imagePullPolicy: Always
    name: test-busybox
  nodeSelector:
    source: qikqiak

然后我們可以通過 describe 命令查看調度結果:

$ kubectl describe pod test-busybox
......
Events:
  Type    Reason                 Age   From                    Message
  ----    ------                 ----  ----                    -------
  Normal  Scheduled              49s   default-scheduler       Successfully assigned test-busybox to 192.168.1.140
  Normal  SuccessfulMountVolume  49s   kubelet, 192.168.1.140  MountVolume.SetUp succeeded for volume "default-token-hmpbz"
  Normal  Pulling                49s   kubelet, 192.168.1.140  pulling image "busybox"
  Normal  Pulled                 41s   kubelet, 192.168.1.140  Successfully pulled image "busybox"
  Normal  Created                41s   kubelet, 192.168.1.140  Created container
  Normal  Started                41s   kubelet, 192.168.1.140  Started container

我們可以看到 Events 下面的信息,上面的 POD 被正確的調度到了140節點。通過上面的例子我們可以感受到nodeSelector的方式比較直觀,但是還夠靈活,控制粒度偏大,下面我們再看另外一種更加靈活的方式:nodeAffinity

nodeAffinity

nodeAffinity就是節點親和性,相對應的是Anti-Affinity,就是反親和性,這種方法比上面的nodeSelector更加靈活,它可以進行一些簡單的邏輯組合了,不只是簡單的相等匹配。 調度可以分成軟策略和硬策略兩種方式,軟策略就是如果你沒有滿足調度要求的節點的話,POD 就會忽略這條規則,繼續完成調度過程,說白了就是滿足條件最好了,沒有的話也無所謂了的策略;而硬策略就比較強硬了,如果沒有滿足條件的節點的話,就不斷重試直到滿足條件為止,簡單說就是你必須滿足我的要求,不然我就不干的策略。 nodeAffinity就有兩上面兩種策略:preferredDuringSchedulingIgnoredDuringExecutionrequiredDuringSchedulingIgnoredDuringExecution,前面的就是軟策略,后面的就是硬策略。

如下例子:(test-node-affinity.yaml)

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
  labels:
    app: node-affinity-pod
spec:
  containers:
  - name: with-node-affinity
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - 192.168.1.140
            - 192.168.1.161
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: source
            operator: In
            values:
            - qikqiak

上面這個 POD 首先是要求 POD 不能運行在140和161兩個節點上,如果有個節點滿足source=qikqiak的話就優先調度到這個節點上,同樣的我們可以使用descirbe命令查看具體的調度情況是否滿足我們的要求。這里的匹配邏輯是 label 的值在某個列表中,現在Kubernetes提供的操作符有下面的幾種:

  • In:label 的值在某個列表中
  • NotIn:label 的值不在某個列表中
  • Gt:label 的值大於某個值
  • Lt:label 的值小於某個值
  • Exists:某個 label 存在
  • DoesNotExist:某個 label 不存在

如果nodeSelectorTerms下面有多個選項的話,滿足任何一個條件就可以了;如果matchExpressions有多個選項的話,則必須同時滿足這些條件才能正常調度 POD。

podAffinity

上面兩種方式都是讓 POD 去選擇節點的,有的時候我們也希望能夠根據 POD 之間的關系進行調度,Kubernetes在1.4版本引入的podAffinity概念就可以實現我們這個需求。

nodeAffinity類似,podAffinity也有requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution兩種調度策略,唯一不同的是如果要使用互斥性,我們需要使用podAntiAffinity字段。 如下例子,我們希望with-pod-affinitybusybox-pod能夠就近部署,而不希望和node-affinity-pod部署在同一個拓撲域下面:(test-pod-affinity.yaml)

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
  labels:
    app: pod-affinity-pod
spec:
  containers:
  - name: with-pod-affinity
    image: nginx
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - busybox-pod
        topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - node-affinity-pod
          topologyKey: kubernetes.io/hostname

上面這個例子中的 POD 需要調度到某個指定的主機上,至少有一個節點上運行了這樣的 POD:這個 POD 有一個app=busybox-pod的 label。podAntiAffinity則是希望最好不要調度到這樣的節點:這個節點上運行了某個 POD,而這個 POD 有app=node-affinity-pod的 label。根據前面兩個 POD 的定義,我們可以預見上面這個 POD 應該會被調度到140的節點上,因為busybox-pod被調度到了140節點,而node-affinity-pod被調度到了140以為的節點,正好滿足上面的需求。通過describe查看:

$ kubectl describe pod with-pod-affinity
......
Events:
  Type    Reason                 Age   From                    Message
  ----    ------                 ----  ----                    -------
  Normal  Scheduled              8s    default-scheduler       Successfully assigned with-pod-affinity to 192.168.1.140
  Normal  SuccessfulMountVolume  7s    kubelet, 192.168.1.140  MountVolume.SetUp succeeded for volume "default-token-lcl77"
  Normal  Pulling                7s    kubelet, 192.168.1.140  pulling image "nginx"

上面的事件信息也驗證了我們的想法。

labelSelector和 topologyKey的同級,還可以定義 namespaces 列表,表示匹配哪些 namespace 里面的 pod,默認情況下,會匹配定義的 pod 所在的 namespace;如果定義了這個字段,但是它的值為空,則匹配所有的 namespaces。

查看上面我們定義的3個 POD 結果:

$ kubectl get po -o wide
NAME                 READY     STATUS    RESTARTS   AGE       IP             NODE
test-busybox         1/1       Running   0          8m        172.30.95.18   192.168.1.140
with-node-affinity   1/1       Running   0          10m       172.30.81.25   192.168.1.172
with-pod-affinity    1/1       Running   0          8m        172.30.95.17   192.168.1.140

親和性/反親和性調度策略比較如下:

調度策略 匹配標簽 操作符 拓撲域支持 調度目標
nodeAffinity 主機 In, NotIn, Exists, DoesNotExist, Gt, Lt 指定主機
podAffinity POD In, NotIn, Exists, DoesNotExist POD與指定POD同一拓撲域
podAnitAffinity POD In, NotIn, Exists, DoesNotExist POD與指定POD不在同一拓撲域

污點(Taints)與容忍(tolerations)

對於nodeAffinity無論是硬策略還是軟策略方式,都是調度 POD 到預期節點上,而Taints恰好與之相反,如果一個節點標記為 Taints ,除非 POD 也被標識為可以容忍污點節點,否則該 Taints 節點不會被調度pod。

比如用戶希望把 Master 節點保留給 Kubernetes 系統組件使用,或者把一組具有特殊資源預留給某些 POD,則污點就很有用了,POD 不會再被調度到 taint 標記過的節點。taint 標記節點舉例如下:

$ kubectl taint nodes 192.168.1.40 key=value:NoSchedule
node "192.168.1.40" tainted

如果仍然希望某個 POD 調度到 taint 節點上,則必須在 Spec 中做出Toleration定義,才能調度到該節點,舉例如下:

tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"

effect 共有三個可選項,可按實際需求進行設置:

  1. NoSchedule:POD 不會被調度到標記為 taints 節點。
  2. PreferNoSchedule:NoSchedule 的軟策略版本。
  3. NoExecute:該選項意味着一旦 Taint 生效,如該節點內正在運行的 POD 沒有對應 Tolerate 設置,會直接被逐出。

參考資料


免責聲明!

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



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