一、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。