Kubernetes使用節點親緣性將POD調度到特定節點上


  節點污點可以用來讓pod遠離特定的節點,盡量在不修改已有pod信息的前提,通過在節點添加污點信息,來拒絕pod在某些節點上的部署。

  而現在介紹一種叫做節點親緣性,通過明確的在pod中添加的信息,來決定一個pod可以或者不可以被調度到哪些節點上。

  對比節點親緣性和節點選擇器

  在早期版本的Kubernetes中,初始的節點親緣性機制,就是pod描述中的nodeSelector字段。節點必須包含所有pod對應字段中的指定label,才能成為pod調度的目標節點。

  節點選擇器實現簡單,但是它不能滿足你的所有需求。正因為如此,一種更強大的機制被引入。節點選擇器最終會被棄用,所以現在了解新的節點親緣性機制就變得重要起來。

  與節點選擇器類似,每個pod可以定義自己的節點親緣性規則。這些規則可以允許指定硬性限制或者偏好。如果指定一種偏好的話,你將告知Kubernetes對於某個特定的pod,它更傾向於調度到某些節點上,之后Kubernetes將盡量把這個pod調度到這些節點上面。如果沒法實現的話,pod將被調度到其他某個節點上。

  檢查默認的節點標簽

  節點親緣性根據節點的標簽來進行選擇,這點跟節點選擇器是一致的。現在檢查一個Google Kubernetes引擎集群(GKE)中節點的標簽,來看一下它們默認的標簽是什么,如以下代碼所示。

#代碼16.7 GKE節點的默認標簽
$ kubectl describe node gke-kubia-default-pool-db274c5a-mjnf
Name:     gke-kubia-default-pool-db274c5a-mjnf
Role:
Labels:   beta.kubernetes.io/arch=amd64
          beta.kubernetes.io/fluentd-ds-ready=true
          beta.kubernetes.io/instance-type=f1-micro
          beta.kubernetes.io/os=linux
          cloud.google.com/gke-nodepool=default-pool
          failure-domain.beta.kubernetes.io/region=europe-west1         #最后這三個標簽對於節點親緣性來說最為重要
          failure-domain.beta.kubernetes.io/zone=europe-west1-d         
          kubernetes.io/hostname=gke-kubia-default-pool-db274c5a-mjnf   

  這個節點有很多標簽,但涉及節點親緣性和pod親緣性時,最后三個標簽是最重要的。這三個標簽的含義如下:

    • failure-domain.beta.kubernetes.io/region表示該節點所在的地理地域。
    • failure-domain.beta.kubernetes.io/zone表示該節點所在的可用性區域(availability zone)。
    • kubernetes.io/hostname很顯然是該節點的主機名。

  這三個以及其他標簽,將被用於pod親緣性規則。在第三章中,你己經學會 如何給一個節點添加自定義標簽,並且在pod的節點選擇器中使用它。可以通過給 pod加上節點選擇器的方式,將節點部署到含有這個自定義標簽的節點上。現在, 你將學習到怎么用節點親緣性規則實現同樣的功能。

 

1.指定強制性節點親緣性規則

  有這么一個例子,使用了節點選擇器使得需要GPU的pod只被調度到有GPU的節點上。包含了nodeSelector字段的pod描述如以下代碼所示。

#代碼16.8 使用了節點選擇器的pod : kubia-gpu-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kubia-gpu
spec:
  nodeSelector:
    gpu: "true"
  containers:
  - image: luksa/kubia        #這個pod會被調度到包含了gpu=true標簽的節點上
    name: kubia

  nodeSelector字段表示,pod只能被部署在包含了gpu=true標簽的節點上。 如果將節點選擇器替換為節點親緣性規則,pod定義將會如以下代碼清單所示。

#代碼16.9 使用了節點親緣性規則的pod : kubia-gpu-nodeaffinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kubia-gpu
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - "true"
  containers:
  - image: luksa/kubia
    name: kubia

  首先會注意到的是,這種寫法比簡單的節點選擇器要復雜得多,但這是因為它的表達能力更強,再詳細看一下這個規則。

  較長的節點親緣性屬性名的意義

  正如你所看到的,這個pod的描述包含了affinity字段,該字段又包含了nodeAffinity字段,這個字段有一個極其長的名字,所以,先重點關注這個。

  把這個名字分成兩部分,然后分別看下它們的含義:

    • requiredDuringScheduling...表明了該字段下定義的規則,為了讓pod能調度到該節點上,明確指出了該節點必須包含的標簽。
    • ...IgnoredDuringExecution表明了該字段下定義的規則,不會影響己經在節點上運行着的pod。

  目前,當你知道當前的親緣性規則只會影響正在被調度的pod,並且不會導致己經在運行的pod被剔除時,情況可能會更簡單一些。這就是為什么目前的規則都是以IgnoredDuringExecution結尾的。最終,Kubernetes也會支持RequiredDuringExecution,表示如果去除掉節點上的某個標簽,那些需要節點包含該標簽的pod將會被剔除,但是,Kubernetes目前還不支持次特性。所以,可以暫時不去關心這個長字段的第二部分。

  了解節點選擇器條件

  記住上一節所解釋的內容,將更容易理解nodeSelectorTerms和matchExpressions字段,這兩個字段定義了節點的標簽必須滿足哪一種表達式,才能滿足pod調度的條件。樣例中的單個表達式比較容易理解,節點必須包含一個叫作gpu的標簽,並且這個標簽的值必須是true。

  因此,這個pod只會被調度到包含gpu=true的節點上。如圖16.2所示。

  現在,更有趣的部分來了,節點親緣性也可以在調度時指定節點的優先級,將在接下來的部分看到。

 

2.調度pod時優先考慮某些節點

  最近介紹的節點親緣性的最大好處就是,當調度某一個pod時,指定調度器可以優先考慮哪些節點,這個功能是通過preferredDuringSchedulingIgnoredDuringExecution字段來實現的。

  想象一下擁有一個跨越多個國家的多個數據中心,每一個數據中心代表了一個單獨的可用性區域。在每個區域中,有一些特定的機器,只提供給你自己或者你的合作公司使用。現在,你想要部署一些pod,希望將pod優先部署在區域zone1,並且是為你公司部署預留的機器上。如果你的機器沒有足夠的空間用於這些pod,或者出於其他一些重要的原因不希望這些pod調度到上面,那么就會調度到其他區域的其他機器上面,這種情況你也是可以接受的。節點親緣性就可以實現這樣的功能。

  給節點加上標簽

  首先,節點必須加上合適的標簽。每個節點需要包含兩個標簽,一個用於表示所在的這個節點所歸屬的可用性區域,另一個用於表示這是一個獨占的節點還是一個共享的節點。

  在接下來假設雙節點的集群環境,將使用這個集群中的兩個工作節點,當然也可以使用GKE或者其他多節點的集群。

  首先,給這些節點加上標簽,如以下代碼所示。

#代碼16.10 給節點加上標簽
$ kubectl label node node1.k8s availability-zone=zone1
node "node1.k8s" labeled
$ kubectl label node node1.k8s share-type=dedicated
node "node1.k8s" labeled
$ kubectl label node node2.k8s availability-zone=zone2
node "node2.k8s" labeled
$ kubectl label node node2.k8s share-type=shared
node "node2.k8s" labeled
$ kubectl get node -L availability-zone -L share-type
NAME         STATUS    AGE       VERSION   AVAILABILITY-ZONE   SHARE-TYPE
master.k8s   Ready     4d        v1.6.4    <none>              <none>
node1.k8s    Ready     4d        v1.6.4    zone1               dedicated
node2.k8s    Ready     4d        v1.6.4    zone2               shared

  指定優先級節點親緣性規則

  當這些節點的標簽設置好,現在可以創建一個Deployment,其中優先選擇zone1中的dedicated節點。下面的代碼清單顯示了這個Deployment的描述。

#代碼16.11 含有優先級節點親緣性規則的Deployment: preferred-deployment.yamI
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pref
spec:
  replicas: 5
  template:
    metadata:
      labels:
        app: pref
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:  #指定優先級,這不是必需的
          - weight: 80                                #這塊是節點有限調度到zone1,這是最重要的偏好
            preference:
              matchExpressions:
              - key: availability-zone
                operator: In
                values:
                - zone1
          - weight: 20                                 #同時優先調度pod到獨占節點,但是該優先級為zone1優先級的1/4
            preference:
              matchExpressions:
              - key: share-type
                operator: In
                values:
                - dedicated
      containers:
      - args:
        - sleep
        - "99999"
        image: busybox
        name: main

  查看上面的代碼清單,定義了一個節點親緣性優先級,而不是強制要求(后面會講解)。想要pod被調度到包含標簽availability-zone=zone1以及share-type=dedicated的節點上。第一個優先級規則是相對重要的,因此將其weight設置為80,而第二個優先級規則就不那么重要(weight設置為20)。

  了解節點優先級是如何工作的

  如果集群包含多個節點,當調度上面的代碼中的Deployment pod時,節點將會分成4個組,如圖16.3所示。那些包含availability-zone以及share-type標簽,並且匹配pod親緣性的節點,將排在最前面。然后,由於pod的節點親緣性規則配置的權重,接下來是zone1的shared節點,然后是其他區域的dedicated節點,優先級最低的是剩下的其他節點。

  在一個包含兩個節點的集群中部署節點

  如果在一個包含兩個節點的集群中創建該部署,看到的最多的應該是pod被部署在了node1上面。檢查下面的代碼清單看情況是否屬實。

#代碼16.12 查看pod調度情況
$ kubectl get po -o wide
NAME                READY   STATUS    RESTARTS  AGE   IP          NODE
pref-607515-1rnwv   1/1     Running   0         4m    10.47.0.1   node2.k8s
pref-607515-27wp0   1/1     Running   0         4m    10.44.0.8   node1.k8s
pref-607515-5xd0z   1/1     Running   0         4m    10.44.0.5   node1.k8s
pref-607515-jx9wt   1/1     Running   0         4m    10.44.0.4   node1.k8s
pref-607515-mlgqm   1/1     Running   0         4m    10.44.0.6   node1.k8s

  5個pod被創建,其中4個部署在了node1,1個部署在了node2。為什么會有1個pod會被調度到node2而不是node1?原因是除了節點親緣性的優先級函數,調度器還是使用其他的優先級函數來決定節點被調度到哪。其中之一就是Selector SpreadPriority函數,這個函數確保了屬於同一個ReplicaSet或者Service的pod,將分散部署在不同節點上,以避免單個節點失效導致這個服務也宕機。這就是有1個pod被調度到node2的最大可能。

  可以去試着擴容部署至20個實例或更多,將看到大多數的pod被調度到node1。如果沒有設置任何節點親緣性優先級,pod將會被均勻地分配在兩個節點上面。

 

3.更多的親緣性匹配規則及語法

  上面也簡單介紹了簡單的原理以及過程,現在介紹更多的規則以及案例。

  節點親和調度分成軟策略(soft)和硬策略(hard),在軟策略下,如果沒有滿足調度條件的節點,pod會忽略這條規則,繼續完成調度。

  • requiredDuringSchedulingIgnoredDuringExecution

表示pod必須部署到滿足條件的節點上,如果沒有滿足條件的節點,就不停重試。其中IgnoreDuringExecution表示pod部署之后運行的時候,如果節點標簽發生了變化,不再滿足pod指定的條件,pod也會繼續運行。

  • requiredDuringSchedulingRequiredDuringExecution

表示pod必須部署到滿足條件的節點上,如果沒有滿足條件的節點,就不停重試。其中RequiredDuringExecution表示pod部署之后運行的時候,如果節點標簽發生了變化,不再滿足pod指定的條件,則重新選擇符合要求的節點。

  • preferredDuringSchedulingIgnoredDuringExecution

  表示優先部署到滿足條件的節點上,如果沒有滿足條件的節點,就忽略這些條件,按照正常邏輯部署。

  • preferredDuringSchedulingRequiredDuringExecution

  表示優先部署到滿足條件的節點上,如果沒有滿足條件的節點,就忽略這些條件,按照正常邏輯部署。其中RequiredDuringExecution表示如果后面節點標簽發生了變化,滿足了條件,則重新調度到滿足條件的節點。

  再來一個官方示例

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: gcr.io/google_containers/pause:2.0

  這個pod同時定義了requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution 兩種 nodeAffinity。第一個要求 pod 運行在特定AZ的節點上,第二個希望節點最好有對應的another-node-label-key:another-node-label-value 標簽。

  這里的匹配邏輯是label在某個列表中,可選的操作符有:

    • In: label的值在某個列表中
    • NotIn:label的值不在某個列表中
    • Exists:某個label存在
    • DoesNotExist:某個label不存在
    • Gt:label的值大於某個值(字符串比較)
    • Lt:label的值小於某個值(字符串比較)

  如果nodeAffinity中nodeSelector有多個選項,節點滿足任何一個條件即可;如果matchExpressions有多個選項,則節點必須同時滿足這些選項才能運行pod 。


免責聲明!

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



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