Docker 與 K8S學習筆記(二十五)—— Pod的各種排程策略(中)

2022-07-24 15:00:35

一、Pod親和性與反親和性排程

在實際應用中,我們往往會遇到特殊的Pod排程需求:存在某些相互依賴、頻繁呼叫的Pod,他們需要儘可能部署在同一個節點、網段、機櫃或區域中,這就是Pod間親和性,反之,出於避免競爭或容錯需求,我們需要使某些Pod儘可能遠離某些特定Pod時,這就是Pod間反親和性。簡單的說,就是相關的兩種或多種Pod是否可以在同一個拓撲域中共存或互斥。

拓撲域:一個拓撲域由一些Node組成,這些Node通常有相同的地理空間座標,如部署在同一個機架、機房或地區,我們一般用region表示機架或機房的拓撲區域,用Zone表示地區這種跨度大的拓撲區域。

 

Pod的親和性與反親和性設定是通過在Pod的定義上增加topologyKey屬性來實現的,我們來看一下具體的例子:我們部署web應用極其對應的redis快取服務,一般情況下我們希望redis多個範例儘可能分散部署,而web應用和redis快取儘可能部署在一臺機器上,但多個web應用副本應當分散部署,這該如何實現呢?

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:latest

上面是redis部署設定,包含三個副本以及 app=store 標籤選擇器。Deployment 中設定了 PodAntiAffinity,通過設定topologyKey為kbernetes.io/hostname確保排程器不會將三個副本排程到一個節點上。

[root@kubevm1 workspace]# kubectl create -f redis_deployment.yml
deployment.apps/redis-cache created
[root@kubevm1 workspace]# kubectl get pods -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
redis-cache-5d67cf4885-4vj4q   1/1     Running   0          15m   10.244.0.7    kubevm1   <none>           <none>
redis-cache-5d67cf4885-bn7dt   1/1     Running   0          15m   10.244.2.18   kubevm3   <none>           <none>
redis-cache-5d67cf4885-rx2b9   1/1     Running   0          15m   10.244.1.35   kubevm2   <none>           <none>

接下來是web應用的部署設定:通過設定 podAntiAffinity 以及 podAffinity。要求將其副本與 包含 app=store 標籤的 Pod 放在同一個節點上;同時也要求 web-app 的副本不被排程到同一個節點上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-app
  replicas: 3
  template:
    metadata:
      labels:
        app: web-app
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-app
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:latest

我們來看一下部署結果:

[root@kubevm1 workspace]# kubectl get pods -o wide
NAME                           READY   STATUS             RESTARTS   AGE     IP            NODE      NOMINATED NODE   READINESS GATES
redis-cache-5d67cf4885-4vj4q   1/1     Running            0          20m     10.244.0.7    kubevm1   <none>           <none>
redis-cache-5d67cf4885-bn7dt   1/1     Running            0          20m     10.244.2.18   kubevm3   <none>           <none>
redis-cache-5d67cf4885-rx2b9   1/1     Running            0          20m     10.244.1.35   kubevm2   <none>           <none>
web-server-f98798775-87fdn     1/1     Running            0          3m33s   10.244.1.36   kubevm2   <none>           <none>
web-server-f98798775-wd7vk     1/1     Running            0          3m33s   10.244.0.8    kubevm1   <none>           <none>
web-server-f98798775-whlbr     1/1     Running            0          3m33s   10.244.2.19   kubevm3   <none>           <none>

 

二、汙點與容忍度

節點親和性使得Pod能夠被排程到某些Node上執行,汙點則相反,它可以讓Node拒絕Pod的執行,比如有些Node磁碟滿了,需要清理,或者它的記憶體或CPU佔用較高,這個時候我們不希望將新的Pod排程過去。當然,被標記為汙點的Node並非故障不可用的節點,我們還可以通過設定容忍度將某些Pod排程過來。預設情況下,在Node上設定一個或多個汙點標籤後除非Pod明確宣告能夠容忍這些汙點,否則無法排程到這些Node上執行,我們用一個例子演示一下。

[root@kubevm1 ~]# kubectl taint nodes kubevm2 disk=full:NoSchedule
node/kubevm2 tainted

我們這裡給kubevm2設定汙點標籤,此標籤的鍵為disk,值為full,其效果是NoSchedule,這樣除非Pod明確宣告可以容忍磁碟滿的Node,否則將不會排程到kubevm2上,我們還是以上面redis的部署為例,當不設定容忍度時的情況是如何的:

[root@kubevm1 workspace]# kubectl create -f redis_deployment.yml
[root@kubevm1 workspace]# kubectl get pods
NAME                           READY   STATUS    RESTARTS   AGE
redis-cache-5d67cf4885-2xfdt   0/1     Pending   0          28s
redis-cache-5d67cf4885-j2zxh   1/1     Running   0          28s
redis-cache-5d67cf4885-mbk5g   1/1     Running   0          28s

由於redis的部署中設定了Pod反親和性,即每一個副本必須部署在不同節點上,所以可以看到當kubevm2設定汙點後,redis其中一個Pod一直處於Pending狀態無法排程。ok,接下來我們宣告一下容忍度試試:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      tolerations:
      - key: "disk"
        operator: "Exists"
        effect: "NoSchedule"
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:latest

我們建立此deployment看看效果:

[root@kubevm1 workspace]# kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
redis-cache-d67c79fd7-r2mw9   1/1     Running   0          44s   10.244.2.21   kubevm3   <none>           <none>
redis-cache-d67c79fd7-t2vsh   1/1     Running   0          44s   10.244.0.10   kubevm1   <none>           <none>
redis-cache-d67c79fd7-xppql   1/1     Running   0          44s   10.244.1.37   kubevm2   <none>           <none>

我們可以看到,通過設定容忍度,Pod也可以排程到kubevm2上了。

 

這裡要注意,在Pod的容忍度宣告中,key和effect需要與汙點的設定保持一致,並滿足以下兩個條件之一:

  • operator的值時Exists,則無需指定value;

  • operator的值是Equal則需要指定value並且和汙點的value相同。

所以我們也可以這樣設定容忍度:

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

除此之外還有兩種特殊情況需要注意:

  • 如果一個容忍度的 key 為空且 operator 為 Exists, 則能容忍所有汙點;

  • 如果 effect 為空,則能匹配所有effect。