Kubernetes集群調度詳解(節(jié)點親和性、Pod親和性、Taint與Toleration)
引導:現(xiàn)在發(fā)覺每次pod調度都是隨機事件,不知道pod會被調度到哪一個節(jié)點
一、簡介
Scheduler 是 kubernetes 的調度器,主要的任務是把定義的 pod 分配到集群的節(jié)點上。聽起來非常簡單,但有很多要考慮的問題:
- 公平:如何保證每個節(jié)點都能被分配資源
- 資源高效利用:集群所有資源最大化被使用
- 效率:調度的性能要好,能夠盡快地對大批量的 pod 完成調度工作
- 靈活:允許用戶根據(jù)自己的需求控制調度的邏輯
Scheduler 是作為單獨的程序運行的,啟動之后會一直堅挺 API Server,獲取PodSpec.NodeName為空的 pod,對每個 pod 都會創(chuàng)建一個 binding(必須遵守的),表明該 pod 應該放到哪個節(jié)點上
二、調度過程
調度分為幾個部分:
- 首先是過濾掉不滿足條件的節(jié)點,這個過程稱為predicate(預選);
- 然后對通過的節(jié)點按照優(yōu)先級排序,這個是priority(優(yōu)選);
- 最后從中選擇優(yōu)先級最高的節(jié)點。如果中間任何一步驟有錯誤,就直接返回錯誤(先預選,后優(yōu)選)
Predicate(預選)有一系列的算法可以使用:
- PodFitsResources:節(jié)點上剩余的資源是否大于 pod 請求的資源
- PodFitsHost:如果 pod 指定了 NodeName,檢查節(jié)點名稱是否和 NodeName 匹配
- PodFitsHostPorts:節(jié)點上已經使用的 port 是否和 pod 申請的 port 沖突
- PodSelectorMatches:過濾掉和 pod 指定的 label 不匹配的節(jié)點
- NoDiskConflict:已經 mount 的 volume 和 pod 指定的 volume 不沖突,除非它們都是只讀
如果在 predicate 過程中沒有合適的節(jié)點,pod 會一直在pending狀態(tài)(pending:等待),不斷重試調度,直到有節(jié)點滿足條件。經過這個步驟,如果有多個節(jié)點滿足條件,就繼續(xù) priorities 過程:按照優(yōu)先級大小對節(jié)點排序
優(yōu)先級由一系列鍵值對組成,鍵是該優(yōu)先級項的名稱,值是它的權重(該項的重要性)。這些優(yōu)先級選項包括:
- LeastRequestedPriority:通過計算 CPU 和 Memory 的使用率來決定權重,使用率越低權重越高。換句話說,這個優(yōu)先級指標傾向于資源使用比例更低的節(jié)點
- BalancedResourceAllocation:節(jié)點上 CPU 和 Memory 使用率越接近,權重越高。這個應該和上面的一起使用,不應該單獨使用
- ImageLocalityPriority:傾向于已經有要使用鏡像的節(jié)點,鏡像總大小值越大,權重越高
通過算法對所有的優(yōu)先級項目和權重進行計算,得出最終的結果
三、自定義調度器
除了 kubernetes 自帶的調度器,你也可以編寫自己的調度器。通過spec:schedulername參數(shù)指定調度器的名字,可以為 pod 選擇某個調度器進行調度。
比如下面的 pod 選擇my-scheduler進行調度,而不是默認的default-scheduler:
四、節(jié)點親和性(pod與node的親和性)
pod.spec.nodeAffinity
- preferredDuringSchedulingIgnoredDuringExecution(優(yōu)先執(zhí)行計劃):軟策略
- requiredDuringSchedulingIgnoredDuringExecution(要求執(zhí)行計劃):硬策略
- preferred:首選,較喜歡
- required:需要,必修
鍵值運算關系
- In:label 的值在某個列表中
- NotIn:label 的值不在某個列表中
- Gt:label 的值大于某個值
- Lt:label 的值小于某個值
- Exists:某個 label 存在
- DoesNotExist:某個 label 不存在
1、硬策略:requiredDuringSchedulingIgnoredDuringExecution
#節(jié)點硬策略。排除node02,只能在node01上運行
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity: #親和性
nodeAffinity: #node親和性
# 硬親和性限制
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname # 標簽鍵名
operator: NotIn #鍵值運算關系 ,NotIn:label的值不在某個列表中。 表示不是node02節(jié)點就可運行
values:
- k8s-node02 # 標簽鍵值
kubectl get node --show-labels mkdir affi cd affi vim pod1.yaml kubectl create -f pod1.yaml kubectl get pod -o wide

kubectl delete pod --all && kubectl create -f pod1.yaml && kubectl get pod -o wide

把pod1.yaml改為In node3

然而實際并沒有node3節(jié)點,因此一直處于Pending狀態(tài),這就是硬策略

2、軟策略:preferredDuringSchedulingIgnoredDuringExecution
如果有的話就在上面運行,沒有的話就算了
vim pod2.yaml
#軟策略
apiVersion: v1
kind: Pod
metadata:
name: affinity2
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 #權重,權重越大越親和(多個軟策略的情況)
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node03 # 期望是node03
查看:kubectl get node --show-labels

3、軟硬策略(先滿足硬策略再滿足軟策略)
#軟硬合體
apiVersion: v1
kind: Pod
metadata:
name: affinity2
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity:
nodeAffinity: #node親和性
# 硬親和性限制
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname # 標簽鍵名
operator: NotIn #鍵值運算關系 ,NotIn:label的值不在某個列表中。 表示不是node02節(jié)點就可運行
values:
- k8s-node02 # 標簽鍵值
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 #權重,權重越大越親和(多個軟策略的情況)
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node03 # 期望是node03
五、Pod親和性(pod與pod之間的親和性)
pod.spec.affinity.podAffinity/podAntiAffinity
- preferredDuringSchedulingIgnoredDuringExecution:軟策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
親和性/反親和性調度策略比較如下:

kubectl get pod --show-labels

vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: hub.atguigu.com/library/myapp:v1
affinity:
#想讓兩個pod運行在同一個node上
podAffinity:
#硬策略
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector: #標簽選擇
matchExpressions:
- key: app #當app存在node-affinity-pod時就選擇
operator: In
values:
- node-affinity-pod
topologyKey: kubernetes.io/hostname
#hostname判斷是否在同一個pod,唯一
發(fā)覺處于同一個拓撲域topologyKey

vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-4
labels:
app: pod-4
spec:
containers:
- name: pod-4
image: hub.atguigu.com/library/myapp:v1
affinity:
#不想讓兩個pod運行在同一個node上
#匹配標簽如果app=pod-2,那么則不運行在這個節(jié)點
podAntiAffinity:
#軟策略
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app #匹配app=node-affinity-pod
operator: In
values:
- node-affinity-pod
topologyKey: kubernetes.io/hostname

補充:如果標簽不滿足的情況,改標簽的話就能滿足
kubectl label pod podname app=label --overwrite=true
六、Taint 和 Toleration
節(jié)點親和性,是pod的一種屬性(偏好或硬性要求),它使pod被吸引到一類特定的節(jié)點。Taint 則相反,它使節(jié)點能夠排斥一類特定的 pod
Taint 和 toleration 相互配合,可以用來避免 pod 被分配到不合適的節(jié)點上。每個節(jié)點上都可以應用一個或多個taint ,這表示對于那些不能容忍這些 taint 的 pod,是不會被該節(jié)點接受的。如果將 toleration 應用于 pod上,則表示這些 pod 可以(但不要求)被調度到具有匹配 taint 的節(jié)點上
污點 (Taint)
1、污點 ( Taint ) 的組成
使用kubectl taint命令可以給某個 Node 節(jié)點設置污點,Node 被設置上污點之后就和 Pod 之間存在了一種相斥的關系,可以讓 Node 拒絕 Pod 的調度執(zhí)行,甚至將 Node 已經存在的 Pod 驅逐出去每個污點的組成如下:
key=value:effect
每個污點有一個 key 和 value 作為污點的標簽,其中 value 可以為空,effect 描述污點的作用。
當前 taint effect 支持如下三個選項:
- NoSchedule:表示k8s將不會將Pod調度到具有該污點的Node上(K8Snode添加這個effecf類型污點,新的不能容忍的pod不能再調度過來,但是老的運行在node上不受影響)
- PreferNoSchedule:表示k8s將不會將Pod調度到具有該污點的Node上,同時會將Node上已經存在的Pod驅逐出去(pod會嘗試將pod分配到該節(jié)點)
- NoExecute:表示k8s將盡量避免將Pod調度到具有該污點的Node上(K8Snode添加這個effecf類型污點,新的不能容忍的pod不能調度過來,老的pod也會被驅逐)
這也是為什么生成的pod不會分配到master的原因(因為天生就打了這個污點NoSchedule)

2、污點的設置、查看和去除
#查看節(jié)點污點 模板:kubectl describe node node-name kubectl describe node k8s-node01 # 設置污點 模板:kubectl taint nodes node1 key1=value1:NoSchedule kubectl taint nodes k8s-node01 check=guozige:NoExecute 因為pod是自主式pod,沒有控制器控制的pod。去除后就不存在了 如果是Deployment和StatefullSet,它就會在node02上創(chuàng)建,維持副本數(shù) # 節(jié)點說明中,查找 Taints 字段 kubectl describe node k8s-node01 # 去除污點 模板:kubectl taint nodes node1 key1=value1:NoSchedule- kubectl taint nodes k8s-node01 check=guozige:NoExecute-
設置污點測試
容忍(Tolerations)
設置了污點的 Node 將根據(jù) taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之間產生互斥的關系,Pod 將在一定程度上不會被調度到 Node 上。但我們可以在 Pod 上設置容忍 ( Toleration ) ,意思是設置了容忍的 Pod 將可以容忍污點的存在,可以被調度到存在污點的 Node 上
pod.spec.tolerations
tolerations: #containers同級 - key: "check" #能容忍的污點key operator: "Equal" #Equal等于表示key=value , Exists不等于,表示當值不等于下面value正常 value: "guozige" #值 effect: "NoExecute" #effect策略 tolerationSeconds: 3600 #原始的pod多久驅逐,注意只有effect: "NoExecute"才能設置,不然報錯
- 其中 key, vaule, effect 要與 Node 上設置的 taint 保持一致
- operator 的值為 Exists 將會忽略 value 值
- tolerationSeconds 用于描述當 Pod 需要被驅逐時可以在 Pod 上繼續(xù)保留運行的時間
vim pod3.yaml

kubectl apply -f pod3.yaml kubectl get pod -o wide
容忍污點

1、當不指定 key 值時,表示容忍所有的污點 key:
tolerations: - operator: "Exists"
2、當不指定 effect 值時,表示容忍所有的污點作用
tolerations: - key: "key" operator: "Exists"
3、有多個 Master 存在時,防止資源浪費,可以如下設置
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule PreferNoSchedule 盡可能不在該節(jié)點上運行,只有當節(jié)點資源不夠用才運行
配置節(jié)點故障后Pod重新調度的時間
例如下面的配置文件,對于notReady和unreachable狀態(tài)的節(jié)點,其上的Pod等待300秒,如果仍未恢復,則會停止執(zhí)行。
八、指定調度節(jié)點
親和性和污點,容忍都比較含蓄。
指定調度節(jié)點是絕對指定目標,就要這個
1、Pod.spec.nodeName 將 Pod 直接調度到指定的 Node 節(jié)點上,會跳過 Scheduler 的調度策略,該匹配規(guī)則是強制匹配
vim pod5.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
template:
metadata:
labels:
app: myweb
spec:
nodeName: k8s-node01 #指定全在node01
containers:
- name: myweb
image: hub.atguigu.com/library/myapp:v1
ports:
- containerPort: 80

2、Pod.spec.nodeSelector:通過 kubernetes 的 label-selector 機制選擇節(jié)點,由調度器調度策略匹配 label,而后調度 Pod 到目標節(jié)點,該匹配規(guī)則屬于強制約束
vim pod6.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb66
spec:
replicas: 2
template:
metadata:
labels:
app: myweb66
spec:
nodeSelector:
disk: ssd #硬盤必須是ssd類型才運行
#type: backEndNode1 #標簽名,標簽值
containers:
- name: myweb66
image: hub.atguigu.com/library/myapp:v1
ports:
- containerPort: 80
測試驗證
kubectl label node k8s-node01 disk=ssd kubectl label node k8s-node02 disk=ssd kubectl edit deployment myweb66 增加副本數(shù)目 kubectl get pod 發(fā)現(xiàn)有些在node02上了
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
詳解Rainbond云原生平臺簡化Kubernetes業(yè)務問題排查
這篇文章主要介紹了詳解Rainbond云原生平臺簡化Kubernetes業(yè)務問題排查,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
解決k8s namespace 一直處于 Terminating 狀態(tài)的問題
這篇文章主要介紹了k8s namespace 一直處于 Terminating 狀態(tài)的解決方法,以下的 tool 為 Terminating 狀態(tài)的 namespace,下面相關的一些操作記得將 tool 修改成自己的 namespace 名稱,需要的朋友可以參考下2022-10-10
Kubernetes教程之Windows?HostProcess?運行容器化負載
這篇文章主要介紹了Kubernetes?Windows?HostProcess?運行容器化負載,本篇內容還是比較多的,總共包含了?Windows?HostProcess的創(chuàng)建、為?Windows?Pod?和容器配置?GMSA?和?Windows?的?Pod?和容器配置?RunAsUserName三大功能模塊,需要的朋友可以參考下2022-07-07
k8s?clientConfig和rawConfig區(qū)別解析
k8s clientConfig和rawConfig區(qū)別k8s.io/client-gov0.28.2基于kubeconfig可以創(chuàng)建clientConfig和rawConfig,兩者區(qū)別在于,clientConfig包含了訪問kube-apiserver的地址和認證鑒權信息,感興趣的朋友一起看看吧2025-03-03

