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
?and?nodeAffinity
,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: 100podAffinityTerm:labelSelector:matchExpressions:- key: securityoperator: Invalues:- S2topologyKey: 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上。
?
?
?
?
?
?