Kubernetes中的調度策略可以大致分為兩種
一種是全局的調度策略,要在啟動調度器時配置,包括kubernetes調度器自帶的各種predicates和priorities算法,具體可以參看上一篇文章;
另一種是運行時調度策略,包括nodeAffinity(主機親和性),podAffinity(POD親和性)以及podAntiAffinity(POD反親和性)。
nodeAffinity 主要解決POD要部署在哪些主機,以及POD不能部署在哪些主機上的問題,處理的是POD和主機之間的關系。
podAffinity 主要解決POD可以和哪些POD部署在同一個拓撲域中的問題(拓撲域用主機標簽實現,可以是單個主機,也可以是多個主機組成的cluster、zone等。)
podAntiAffinity主要解決POD不能和哪些POD部署在同一個拓撲域中的問題。它們處理的是Kubernetes集群內部POD和POD之間的關系。
三種親和性和反親和性策略的比較如下表所示:
策略名稱 | 匹配目標 | 支持的操作符 | 支持拓撲域 | 設計目標 |
nodeAffinity | 主機標簽 | In,NotIn,Exists,DoesNotExist,Gt,Lt | 不支持 | 決定Pod可以部署在哪些主機上 |
podAffinity | Pod標簽 | In,NotIn,Exists,DoesNotExist | 支持 | 決定Pod可以和哪些Pod部署在同一拓撲域 |
PodAntiAffinity | Pod標簽 | In,NotIn,Exists,DoesNotExist | 支持 | 決定Pod不可以和哪些Pod部署在同一拓撲域 |
親和性:應用A與應用B兩個應用頻繁交互,所以有必要利用親和性讓兩個應用的盡可能的靠近,甚至在一個node上,以減少因網絡通信而帶來的性能損耗。
反親和性:當應用的采用多副本部署時,有必要采用反親和性讓各個應用實例打散分布在各個node上,以提高HA。
主要介紹kubernetes的中調度算法中的Node affinity和Pod affinity用法
實際上是對前文提到的優選策略中的NodeAffinityPriority
策略和InterPodAffinityPriority
策略的具體應用。
kubectl explain pods.spec.affinity
親和性策略(Affinity)能夠提供比NodeSelector或者Taints更靈活豐富的調度方式,例如:
豐富的匹配表達式(In, NotIn, Exists, DoesNotExist. Gt, and Lt)
軟約束和硬約束(Required/Preferred)
以節點上的其他Pod作為參照物進行調度計算
親和性策略分為NodeAffinityPriority策略和InterPodAffinityPriority策略。
先回顧一下之前的節點選擇器
節點選擇器: nodeSelector nodeName
創建一個Pod 節點選擇器標簽
nodeSelector:
disktype: ssd
默認節點沒這個標簽:所以會調度失敗
[root@k8s-master schedule]# kubectl get node --show-labels|egrep disktype
[root@k8s-master schedule]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-demo 0/1 Pending 0 11s
[root@k8s-master schedule]# kubectl describe pod pod-demo
Warning FailedScheduling 28s (x2 over 28s) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
給一個節點打上標簽:
[root@k8s-master schedule]# kubectl label nodes k8s-node2 disktype=ssd
node/k8s-node2 labeled
[root@k8s-master schedule]# kubectl get node --show-labels|egrep disktype
k8s-node2 Ready <none> 63d v1.14.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
[root@k8s-master schedule]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-demo 1/1 Running 0 45s 10.244.1.14 k8s-node2 <none> <none>
Node affinity(節點親和性)
kubectl explain pods.spec.affinity.nodeAffinity
據官方說法未來NodeSeletor策略會被廢棄,由NodeAffinityPriority策略中requiredDuringSchedulingIgnoredDuringExecution替代。
NodeAffinityPriority策略和NodeSelector一樣,通過Node節點的Label標簽進行匹配,匹配的表達式有:In, NotIn, Exists, DoesNotExist. Gt, and Lt。
定義節點親和性規則有2種:硬親和性(require)和軟親和性(preferred)
硬親和性:requiredDuringSchedulingIgnoredDuringExecution
軟親和性:preferredDuringSchedulingIgnoredDuringExecution
- 硬親和性:實現的是強制性規則,是Pod調度時必須滿足的規則,否則Pod對象的狀態會一直是Pending
- 軟親和性:實現的是一種柔性調度限制,在Pod調度時可以盡量滿足其規則,在無法滿足規則時,可以調度到一個不匹配規則的節點之上。
需要注意的是preferred
和required
后半段字符串IgnoredDuringExecution表示:
在Pod資源基於節點親和性規則調度到某個節點之后,如果節點的標簽發生了改變,調度器不會講Pod對象從該節點上移除,因為該規則僅對新建的Pod對象有效。
硬親和性
kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution
apiVersion: v1
kind: Pod
metadata:
name: nodeaffinity-required
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
# - {key: zone,operator: In,values: ["ssd","hard"]}
- key: disktype
operator: In
values:
- ssd
- hard
[root@k8s-master schedule]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-required 1/1 Running 0 7s 10.244.1.16 k8s-node2 <none> <none>
發現和上面定義的節點選擇器效果一樣,未來是要取代節點選擇器的。
注意:
nodeSelectorTerms可以定義多條約束,只需滿足其中一條。
matchExpressions可以定義多條約束,必須滿足全部約束。
如下配置清單,必須存在滿足標簽zone=foo和ssd=true的節點才能夠調度成功
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
- {key: ssd, operator: Exists, values: []} #增加一個規則
[root@k8s-master ~]# kubectl get pods pod-affinity-required
NAME READY STATUS RESTARTS AGE
pod-affinity-required 0/1 Pending 0 16s
[root@k8s-master ~]# kubectl label node k8s-node1 ssd=true
[root@k8s-master ~]# kubectl get pods pod-affinity-required
NAME READY STATUS RESTARTS AGE
pod-affinity-required 1/1 Running 0 2m
軟親和性
kubectl explain pods.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 60
preference:
matchExpressions:
- {key: zone, operator: In, values: ["foo"]}
- weight: 30
preference:
matchExpressions:
- {key: ssd, operator: Exists, values: []}
總結:
- 同時指定
nodeSelector
andnodeAffinity
,pod必須都滿足 nodeAffinity
有多個nodeSelectorTerms
,pod只需滿足一個nodeSelectorTerms
多個matchExpressions
,pod必須都滿足- 由於
IgnoredDuringExecution
,所以改變labels不會影響已經運行pod
總的來說,node親和性與nodeSelector類似,是它的擴展,節點是否配置合乎需求的標簽,或者Pod對象定義合理的標簽選擇器,這樣才能夠基於標簽選擇出期望的目標節點
Pod affinity(Pod 親和性)
kubectl explain pods.spec.affinity.podAffinity
在出於高效通信的需求,有時需要將一些Pod調度到相近甚至是同一區域位置(比如同一節點、機房、區域)等等,比如業務的前端Pod和后端Pod,
此時這些Pod對象之間的關系可以叫做親和性。同時出於安全性的考慮,也會把一些Pod之間進行隔離,此時這些Pod對象之間的關系叫做反親和性。
調度器把第一個Pod放到任意位置,然后和該Pod有親和或反親和關系的Pod根據該動態完成位置編排,這就是Pod親和性和反親和性調度的作用。
Pod的親和性定義也存在硬親和性和軟親和性的區別,其約束的意義和節點親和性類似。
requiredDuringSchedulingIgnoredDuringExecution, 硬約束,一定要滿足,Pod的親和性調度必須要滿足后續定義的約束條件。
preferredDuringSchedulingIgnoredDuringExecution,軟約束,不一定滿足,Pod的親和性調度會盡量滿足后續定義的約束條件。
Pod的親和性調度要求各相關的Pod對象運行在同一位置,而反親和性則要求它們不能運行在同一位置。這里的位置實際上取決於節點的位置拓撲,拓撲的方式不同,Pod是否在同一位置的判定結果也會有所不同。
如果基於各個節點的kubernetes.io/hostname
標簽作為評判標准,那么會根據節點的hostname
去判定是否在同一位置區域。
根據節點上正在運行的pod的標簽來調度,而非node的標簽,要求對節點和Pod兩個條件進行匹配,其規則為:如果在具有標簽X的Node上運行了一個或多個符合條件Y的Pod,那么Pod應該運行在此Node上,
如果是互斥,則拒絕運行在此Node上。 也就是說根據某個已存在的pod,來決定要不要和此pod在一個Node上,在一起就需要設置親和性,不和它在一起就設置互斥性。
Pod親和性調度請使用:podAffinity,非親和性調度請使用:podAntiAffinity。
InterPodAffinityPriority策略有podAffinity和podAntiAffinity兩種配置方式。
InterPodAffinityPriority是干嘛的呢?簡單來說,就說根據Node上運行的Pod的Label來進行調度匹配的規則,匹配的表達式有:In, NotIn, Exists, DoesNotExist,通過該策略,可以更靈活地對Pod進行調度。
例如:將多實例的Pod分散到不通的Node、盡量調度A-Pod到有B-Pod運行的Node節點上等等。另外與Node-affinity不同的是:該策略是依據Pod的Label進行調度,所以會受到namespace約束。
硬親和性
通過Kubernetes內置節點標簽中的key來進行聲明,這個key的名字為topologyKey,用來表達節點所屬的拓朴結構之下。
pod的親和性表達方式與Node親和性是一樣的表達方式。
kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
參數配置說明:
kubectl -get nodes --show-labels
kubernetes內置標簽:
○ kubernetes.io/hostname
○ failure-domain.beta.kubernetes.io/zone
○ failure-domain.beta.kubernetes.io/region
○ beta.kubernetes.io/instance-type
○ beta.kubernetes.io/os
○ beta.kubernetes.io/arch
topologyKey
:
- 對於親和性和軟反親和性,不允許空
topologyKey
; - 對於硬反親和性,
LimitPodHardAntiAffinityTopology
控制器用於限制topologyKey
只能是kubernetes.io/hostname
; - 對於軟反親和性,空
topologyKey
被解讀成kubernetes.io/hostname
,failure-domain.beta.kubernetes.io/zone
andfailure-domain.beta.kubernetes.io/region
的組合; kubernetes.io/hostname
標簽是Kubernetes集群節點的內建標簽,它的值為當前節點的主機名,對於各個節點來說都是不同的
1:創建參照Pod
#查看調度到哪個Node之上:
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-flag 1/1 Running 0 4m 10.244.1.16 k8s-node1
2:創建一個pod的硬親和性
# 因為pod是屬於某個命名空間的,所以設置符合條件的目標Pod時,還可以指定哪個命名空間或全部命名空間里的Pod,
# namespace的定義與labelSelector同級,如果不指定命名空間,則與此處創建的pod在一個namespace之中
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
with-pod-affinity 1/1 Running 0 11s 10.244.1.16 k8s-node1
pod-flag 1/1 Running 0 1h 10.244.1.17 k8s-node1
的確是在同一個Node上。
如果在創建時,pod狀態一直處於Pending狀態,很有可能是因為找不到滿足條件的Node
基於單一節點的Pod親和性相對來說使用的情況會比較少,通常使用的是基於同一地區、區域、機架等拓撲位置約束。
比如部署應用程序(myapp)和數據庫(db)服務相關的Pod時,這兩種Pod應該部署在同一區域上,可以加速通信的速度
3:創建一個pod的反硬親和性
kubectl explain pods.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-flag 1/1 Running 0 1h 10.244.1.16 k8s-node1
with-pod-affinity 1/1 Running 0 1h 10.244.1.17 k8s-node1
with-pod-antiffinity 1/1 Running 0 1m 10.244.2.11 k8s-node2
#可以看到與參照pod不在同一個node之上,達到了互斥的作用
實例:
1.借助反硬特性我們可以部署3個redis實例,並且為了提升HA,部署在不同的節點:
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:3.2-alpine
2:部署三個web實例,為了提升HA,都不在一個node;並且為了方便與redis交互,盡量與redis在同一個node(硬特性和反硬特性的結合應用)。
apiVersion: apps/v1 kind: Deployment metadata: name: web-server spec: selector: matchLabels: app: web-store replicas: 3 template: metadata: labels: app: web-store spec: affinity: podAntiAffinity: #反親和性 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web-store topologyKey: "kubernetes.io/hostname" podAffinity: #親和性 requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: "kubernetes.io/hostname" containers: - name: web-app image: nginx:1.12-alpine
軟親和性
kubectl explain pods.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution
上述的清單配置當中,pod的軟親和調度需要將Pod調度到標簽為app=cache
並在區域zone當中,或者調度到app=db
標簽節點上的,但是我們的節點上並沒有類似的標簽,
所以調度器會根據軟親和調度進行隨機調度到k8s-node1
節點之上。如下:
[root@k8s-master ~]# kubectl get pods -o wide |grep myapp-with-preferred-pod-affinity
myapp-with-preferred-pod-affinity-5c44649f58-cwgcd 1/1 Running 0 1m 10.244.1.40 k8s-node01
myapp-with-preferred-pod-affinity-5c44649f58-hdk8q 1/1 Running 0 1m 10.244.1.42 k8s-node01
myapp-with-preferred-pod-affinity-5c44649f58-kg7cx 1/1 Running 0 1m 10.244.1.41 k8s-node01
pod的反軟親和度:
kubectl explain pods.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: security operator: In values: - S2 topologyKey: kubernetes.io/hostname

使用該配置模板創建三個pod,可以發現pod依舊分配到了不同的節點上。當創建第四個pod時,第四個pod能夠被順利創建
說明preferredDuringScheduling
在podAntiAnffinity下也是不嚴格匹配規則,如果是硬約束,會有一個處於 Pending
對稱性
考慮一個場景,兩個應用S1和S2。現在嚴格要求S1 pod不能與S2 pod運行在一個node,
如果僅設置S1的hard反親和性是不夠的,必須同時給S2設置對應的hard反親和性。
即調度S1 pod時,考慮node沒有S2 pod,同時需要在調度S2 pod時,考慮node上沒有S1 pod。考慮下面兩種情況:
1.先調度S2,后調度S1,可以滿足反親和性,
2.先調度S1,后調度S2,違反S1的反親和性規則,因為S2沒有反親和性規則,所以在schedule-time可以與S1調度在一個拓撲下。
這就是對稱性,即S1設置了與S2相關的hard反親和性規則,就必須對稱地給S2設置與S1相關的hard反親和性規則,以達到調度預期。
反親和性(soft/hard)具備對稱性,上面已經舉過例子了
hard親和性不具備對稱性,例如期望test1、test2親和,那么調度test2的時候沒有必要node上一定要有test1,但是有一個隱含規則,node上有test1更好
soft親和性具備對稱性,不是很理解,遺留
1.Pod間的親和性和反親和性需要大量的處理,需要消耗大量計算資源,會增加調度時間,這會顯着減慢大型集群中的調度。 我們不建議在大於幾百個節點的群集中使用它們。
2.Pod反親和性要求Node一致地標記,集群中的每個節點必須具有匹配topologyKey的標簽,Pod反親和性需要制定topologyKey如果某些或所有節點缺少指定的topologyKey標簽,則可能導致意外行為。
3.在反親和性中,空的selector表示不與任何pod親和。
4.由於hard規則在預選階段處理,所以如果只有一個node滿足hard親和性,但是這個node又不滿足其他預選判斷,比如資源不足,那么就無法調度。所以何時用hard,何時用soft需要根據業務考量。
5.如果所有node上都沒有符合親和性規則的target pod,那么pod調度可以忽略親和性
6.如果labelSelector和topologyKey同級,還可以定義namespaces列表,表示匹配哪些namespace里面的pod,默認情況下,會匹配定義的pod所在的namespace,如果定義了這個字段,但是它的值為空,則匹配所有的namespaces。
7.所有關聯requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全都滿足之后,系統才能將pod調度到某個node上。