天天看點

了解 Kubernetes 的親和性排程 nodeSelector、nodeAffinity、podAffinity、Taints以及Tolerations用法

标簽: kubernetes   affinity   排程  

    • nodeSelector
    • nodeAffinity
    • podAffinity
    • 污點(Taints)與容忍(tolerations)
    • 參考資料

一般情況下我們部署的 POD 是通過叢集自動排程選擇某個節點的,預設情況下排程器考慮的是資源足夠,并且負載盡量平均,但是有的時候我們需要能夠更加細粒度的去控制 POD 的排程,比如我們内部的一些服務 gitlab 之類的也是跑在

Kubernetes

叢集上的,我們就不希望對外的一些服務和内部的服務跑在同一個節點上了,害怕内部服務對外部的服務産生影響;有的時候呢我們兩個服務直接交流比較頻繁,又希望能夠将這兩個服務的 POD 排程到同樣的節點上。這就需要用到 Kubernetes 裡面的一個概念:親和性,親和性主要分為兩類:

nodeAffinity

podAffinity

nodeSelector

我們知道

label

kubernetes

中一個非常重要的概念,使用者可以非常靈活的利用 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

就有兩上面兩種政策:

preferredDuringSchedulingIgnoredDuringExecution

requiredDuringSchedulingIgnoredDuringExecution

,前面的就是軟政策,後面的就是硬政策。

如下例子:(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-affinity

busybox-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 設定,會直接被逐出。

參考資料

  • https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
  • https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
  • https://coreos.com/fleet/docs/latest/affinity.html

繼續閱讀