云原生學習路線導航頁(持續更新中)
- kubernetes學習系列快捷鏈接
- Kubernetes架構原則和對象設計(一)
- Kubernetes架構原則和對象設計(二)
- Kubernetes架構原則和對象設計(三)
- Kubernetes控制平面組件:etcd(一)
- Kubernetes控制平面組件:etcd(二)
- Kubernetes控制平面組件:API Server詳解(一)
- Kubernetes控制平面組件:API Server詳解(二)
- Kubernetes控制平面組件:調度器Scheduler(一)
- Kubernetes控制平面組件:調度器Scheduler(二)
本文是kubernetes的控制面組件調度器Scheduler第二篇,本篇詳細講解了pod對調度策略的配置方法和原理,包括:節點調度、節點親和/反親和、pod親和/反親和、拓撲打散約束、容忍調度、優先級調度以及多調度器
- 希望大家多多 點贊 關注 評論 收藏,作者會更有動力繼續編寫技術文章
1.指定Node調度(nodeName+nodeSelector)
1.1.基本原理
- 通過顯式指定Pod應調度到特定節點,完全繞過Kubernetes調度器的決策邏輯。常用于需要固定節點(如專用硬件節點)的場景。
1.2.涉及Pod字段及解釋
1.2.1.nodeName
spec:nodeName: worker-node-01 # 直接指定目標節點名稱
- 作用:強制Pod調度到指定節點
- 特性:
- 若節點不存在則Pod保持Pending狀態
- 繞過調度器資源檢查邏輯
- 節點標簽變更不影響已調度Pod
1.2.2.nodeSelector
spec:nodeSelector:disktype: ssd # 必須存在的節點標簽gpu-model: "a100" # 精確匹配標簽值
- 匹配規則:
- 節點必須具有所有指定的標簽label
- 值匹配為精確字符串比較(區分大小寫)
- 調度流程:調度器先篩選符合標簽的節點,再檢查資源是否充足
1.3.配置示例
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:nodeName: gpu-node-03containers:- name: cuda-containerimage: nvidia/cuda:11.0-base
---
apiVersion: v1
kind: Pod
metadata:name: ssd-pod
spec:nodeSelector:disktype: ssdstorage-tier: "premium"containers:- name: appimage: redis:alpine
1.4.運維實踐
- 風險提示:
nodeName
會跳過所有調度策略,可能導致Pod無法調度- 節點維護時需要手動遷移Pod
- 容易造成節點資源利用率不均衡
- 最佳實踐:
- 生產環境優先使用
nodeSelector
代替nodeName
- 配合節點自動伸縮組使用固定標簽(如
node-role/gpu=true
) - 定期審計節點標簽的準確性
2.節點親和性/反親和性調度(nodeAffinity)
2.1.基本原理
- 通過 節點標簽的復雜邏輯匹配 實現柔性調度策略,支持:
- 硬性約束(required):必須滿足的條件
- 軟性偏好(preferred):優先但不強制滿足的條件
- 基本原理
- nodeAffinity是對nodeSelector的增強,比nodeSelector擴展性更強,本質還是對根據Node Label,對Node做一個篩選或打分
- nodeAffinity同時支持親和/反親和的配置,比如通過NotIn Operator,就可以實現 Node反親和性 的功能
2.2.涉及Pod字段及解釋
affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬約束nodeSelectorTerms: # 要滿足的條件是什么,predicates階段- matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues: [zone-a, zone-b]preferredDuringSchedulingIgnoredDuringExecution: # 軟偏好- weight: 80 # 打分權重preference: # 要滿足的條件是什么,priority階段matchExpressions:- key: app-tieroperator: Invalues: [cache]
- nodeSelectorTerms:node的label滿足下面的條件
- preference:node的label滿足下面的條件
2.2.1.關鍵字段說明
字段路徑 | 類型 | 說明 |
---|---|---|
operator | enum | In / NotIn / Exists / DoesNotExist / Gt / Lt |
weight | int(1-100) | 軟策略的權重值,影響調度評分 |
topologyKey | string | 定義拓撲域的節點標簽鍵 |
2.2.2.Operator詳解
操作符 | 匹配條件 | 示例 |
---|---|---|
In | 標簽值在指定集合中 | values: [v1,v2] |
NotIn | 標簽值不在集合中 | values: [v3] |
Exists | 標簽鍵存在(忽略values) | key: “gpu” |
DoesNotExist | 標簽鍵不存在 | key: “temp” |
Gt | 數值大于(僅整數) | values: [“100”] |
Lt | 數值小于(僅整數) | values: [“50”] |
2.3.配置示例
apiVersion: v1
kind: Pod
metadata:name: ai-pod
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: acceleratoroperator: Invalues: [nvidia-tesla-v100]preferredDuringSchedulingIgnoredDuringExecution:- weight: 60preference:matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues: [zone-a]containers:- name: ai-containerimage: tensorflow/tensorflow:latest-gpu
2.4.運維實踐
- 注意事項:
- 避免在單個Pod中組合過多復雜規則
- 硬性約束可能導致Pod無法調度
- 節點標簽變更不會觸發已調度Pod的重新調度
- 優化建議:
- 對高頻變更的標簽使用軟性策略
- 使用
kubectl describe nodes
驗證節點標簽 - 結合HorizontalPodAutoscaler實現彈性調度
3.Pod親和性/反親和性調度(podAffinity/podAntiAffinity)
3.1.基本原理
- 根據已有Pod的分布情況決定新Pod的調度位置,支持:
- 親和性(Affinity):傾向于與指定Pod共置
- 反親和性(AntiAffinity):避免與指定Pod共置
- 基本原理
- 查看在指定范圍的node中,有沒有運行打了指定label的pod,然后做相應的親和/反親和
- 指定范圍的node,這個范圍是如何定義的?
- 由topologyKey決定,常用的如 region范圍、zone可用區范圍、rack機架范圍、hostname節點范圍等
- 舉例:topology.kubernetes.io/zone
- 含義:以可用區為基本單位,看同一個可用區內的所有node,是否有運行打了指定label的pod,如果存在,則當前pod可以調度/不能調度 到該可用區
- 已知的kubernetes預定義topologyKey列表
- https://kubernetes.io/docs/reference/labels-annotations-taints/
3.2.涉及Pod字段及解釋
affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues: [cache]topologyKey: topology.kubernetes.io/zonepodAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues: [web]topologyKey: kubernetes.io/hostname
-
配置解釋:
- 強親和:必須調度到 可用區范圍 內已經存在攜帶 app==cache label 的pod 的可用區中
- 弱反親和:盡量調度到 節點范圍 中,不存在 攜帶 app==web label 的pod 的node上去。
- 假如這個app==web就是本應用的label,那么實現的效果就是:我的應用必須都調度到一個可用區,但是盡量一個node上只有該應用一個實例
-
核心字段說明
字段 說明 topologyKey
定義拓撲域的節點標簽鍵(如hostname/zone) namespaces
指定命名空間(默認當前ns) namespaces.matchLabels
通過標簽選擇命名空間(v1.24+)
3.3.配置示例
apiVersion: apps/v1
kind: Deployment
metadata:name: web-server
spec:replicas: 3template:spec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues: [web-server]topologyKey: "kubernetes.io/hostname"containers:- name: webimage: nginx:alpine
3.4.運維實踐
- 性能警告:
- 集群規模超過500節點時慎用Pod親和性
- 每個調度周期需要掃描所有匹配Pod
- 反親和性規則可能顯著增加調度延遲
- 最佳實踐:
- 為關鍵業務服務設置反親和規則
- 使用
topologySpreadConstraints
替代簡單反親和 - 定期清理陳舊的Pod標簽
4.拓撲打散約束(topologySpreadConstraints)
4.1.基本原理
- 通過反親和性規則,實現Pod在拓撲域間的均勻分布,最大化容錯能力,屬于Pod反親和性的高級應用。
4.2.涉及Pod字段及解釋
spec:topologySpreadConstraints:- maxSkew: 1topologyKey: zonewhenUnsatisfiable: DoNotSchedulelabelSelector:matchLabels:app: payment-service
-
字段說明
字段 類型 說明 maxSkew
int 允許的最大分布偏差(≥1) whenUnsatisfiable
enum DoNotSchedule / ScheduleAnyway topologyKey
string 定義拓撲域的節點標簽 labelSelector
object 選擇需要打散的Pod組
4.3.配置示例
apiVersion: apps/v1
kind: Deployment
metadata:name: stateless-api
spec:replicas: 6template:spec:topologySpreadConstraints:- maxSkew: 2topologyKey: topology.kubernetes.io/zonewhenUnsatisfiable: ScheduleAnywaylabelSelector:matchLabels:app: stateless-apicontainers:- name: apiimage: my-api:v1.2.3
4.4.運維實踐
- 版本要求:
- 需要Kubernetes v1.18+
- 完整功能需要啟用EvenPodsSpread特性門控
- 調度策略:
- 強均衡:
whenUnsatisfiable=DoNotSchedule
+maxSkew=1
- 弱均衡:
whenUnsatisfiable=ScheduleAnyway
+ 較大maxSkew
- 監控指標:
kube_scheduler_pod_scheduling_duration_seconds
kube_scheduler_scheduling_attempts
5.容忍調度(taint/tolerations)
5.1.基本原理
- 允許Pod調度到帶有特定污點(Taint)的節點,實現:
- 專用節點隔離(如GPU節點)
- 節點維護前的Pod驅逐
- 特殊硬件資源分配
- 使用場景:
- 有些業務加入集群時,是帶資進組的,自帶了一些服務器,這些服務器他們希望自己用,不開放給其他應用,那么就可以打上一些污點,自己的應用容忍這個污點。但無法強限制,如果別人看到了,可能也會加容忍
5.2.涉及Pod字段及解釋
tolerations:- key: "node.kubernetes.io/disk-pressure"operator: "Exists"effect: "NoSchedule"tolerationSeconds: 3600
-
字段詳解
字段 必填 說明 key
是 污點標識符 operator
是 Exists 或 Equal value
條件 operator=Equal時必填 effect
否 NoSchedule/PreferNoSchedule/NoExecute tolerationSeconds
否 僅對NoExecute有效,驅逐pod前的容忍時間(秒) -
effect詳解
類型 新 Pod 調度 已運行 Pod 影響 容忍配置必要性 典型運維場景 NoSchedule 禁止 無影響 必須 專用硬件節點、控制平面隔離 PreferNoSchedule 盡量禁止 無影響 可選 資源優化、灰度環境 NoExecute 禁止 驅逐 必須 節點維護、故障隔離、安全修復
5.3.配置示例
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:tolerations:- key: "nvidia.com/gpu"operator: "Exists"effect: "NoSchedule"containers:- name: cuda-appimage: nvidia/cuda:11.0-base
5.4.運維實踐
- 安全建議:
- 避免使用全局容忍(operator: Exists)
- 生產環境應為專用節點設置獨特污點
- 結合NodeAffinity實現精確調度,因為如果有 不帶污點/帶污點 的2個node同時滿足調度需要,pod是不一定被調度到帶污點node上的,可以配合node調度策略來達到更精確的調度
- 維護場景:
# 設置維護污點
kubectl taint nodes node1 maintenance=true:NoExecute# 查看節點污點
kubectl describe node node1 | grep Taints
- 監控要點:
- 節點就緒狀態(Ready/DiskPressure等)
- Pod容忍時間窗口(tolerationSeconds)
5.5.Pod的2個默認容忍
- kubernetes默認會給pod添加兩個容忍,用于當node發生異常時自動驅逐pod完成故障轉移
- 二者區別
- not-ready:一般是節點上某些組件有問題,比如cni壞了
- unreachable:一般是節點斷開連接了,ping都不通了
- 達成的效果:當node進入異常狀態后,上面所有的pod被允許再存活300s==5min,之后就被驅逐了
- 默認5min,實際生產可能會更久,因為很多場景下node可能會有挺久的異常時間,比如集群升級,所以生產上有的集群久調整為15min
- 驅逐:其實就是把pod刪除了,如果這個pod有上層的deploy或者replicaSet,就會給他在其他ready node上拉起來
tolerations:- effect: NoExecutekey: node.kubernetes.io/not-readyoperator: ExiststolerationSeconds: 300- effect: NoExecutekey: node.kubernetes.io/unreachableoperator: ExiststolerationSeconds: 300
5.6.多租戶kubernetes集群-如何做到計算資源隔離
- 控制node的讀取權限,將node專用于某個團隊
5.7.生產經驗
6.優先級調度(PriorityClass)
6.1.基本原理
- Kubernetes優先級調度通過
PriorityClass
定義Pod的優先級權重,將所有待調度的pod進行優先級排序,保證高優先級優先調度。 - 優先級調度可以實現功能:
- 調度順序控制:高優先級Pod優先調度
- 資源搶占機制:當節點資源不足時,允許高優先級Pod驅逐低優先級Pod
- 系統穩定性保障:為關鍵系統組件保留資源
- 開啟方法:通過 --feature-gates打開
6.2.涉及資源及字段解釋
6.2.1.PriorityClass 資源
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:name: high-priority
value: 1000000 # 優先級數值,越大優先級越高(必填,范圍0-1e9)
globalDefault: false # 是否作為集群默認優先級(true/false)
description: "關鍵業務優先級"
preemptionPolicy: PreemptLowerPriority # 搶占策略(v1.24+)
-
關鍵字段說明
字段 類型 說明 value
int32 優先級數值,數值越大優先級越高 globalDefault
bool 當未指定priorityClassName時是否作為默認值(集群中只能有一個true) preemptionPolicy
enum Never
(不搶占)/PreemptLowerPriority
(默認)
6.2.2.在Pod中指定優先級
spec:priorityClassName: high-priority # 關聯的PriorityClass名稱priority: 1000000 # 自動填充字段(不可手動設置)
6.3.配置示例
6.3.1.創建PriorityClass
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:name: mission-critical
value: 10000000
globalDefault: false
description: "核心業務優先級,允許資源搶占"
- critical為關鍵的意思
6.3.2.Pod使用優先級
apiVersion: v1
kind: Pod
metadata:name: payment-service
spec:priorityClassName: mission-criticalcontainers:- name: paymentimage: payment-service:v1.8.0resources:requests:cpu: "2"memory: "4Gi"
6.4.運維實踐
6.4.1.風險提示
- 級聯搶占:多個高優先級Pod可能引發連鎖驅逐
- 資源碎片化:頻繁搶占導致節點資源利用率下降
- 死鎖風險:當所有節點都無法滿足高優先級Pod需求時,系統可能無法恢復
6.4.2.最佳實踐
- 優先級規劃:
0-999999 # 用戶應用(按業務重要性分級) 1000000-1999999 # 中間件/數據庫 2000000+ # 系統組件(kube-system)
- 搶占控制:
- 為關鍵Pod設置
preemptionPolicy: Never
避免被更高優先級Pod搶占 - 使用PodDisruptionBudget保護重要應用
- 為關鍵Pod設置
- 監控方案:
- 跟蹤
kube_scheduler_preemption_attempts
指標 - 使用
kubectl get event --field-selector reason=Preempted
- 跟蹤
6.4.3.注意事項
- 啟用要求:確保API Server開啟
PodPriority
特性門控 - 配額關聯:優先級與ResourceQuota獨立,需單獨設置配額限制
- 默認優先級:避免多個PriorityClass設置
globalDefault: true
- 升級兼容性:v1.14+版本才支持穩定版優先級調度
6.5.高級配置
6.5.1.非搶占式優先級
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:name: non-preempt
value: 500000
preemptionPolicy: Never # 禁止搶占其他Pod
6.5.2.系統保留優先級
# 系統組件示例(kube-apiserver)
apiVersion: v1
kind: Pod
metadata:name: kube-apiservernamespace: kube-system
spec:priorityClassName: system-cluster-critical # 內置最高優先級(2億)containers:- name: apiserverimage: k8s.gcr.io/kube-apiserver:v1.25.0
7.多調度器
- podSpec.schedulerName用于指定使用的調度器,不指定時默認為default-scheduler
- schedulerName: default-scheduler
- 對于web應用,一般啟動都是分鐘級,對調度效率要求沒有那么高,一般default-scheduler就夠用了
- 但是對于一些特殊領域,比如大數據場景、AI場景、batchJob場景,可能有特殊的調度需求,kubernetes defaultScheduler可能不太夠用,此時可以使用一些自定義的調度器
- batchJob場景,比如有3個作業,需要同時啟動運行,只要有一個沒有啟動,其他2個也運行不了,這種對調度效率就有要求。再比如在計算密集型任務中,一秒可能會有幾百上千的作業需要調度,就需要一些batchJob調度場景
- batchJob社區有不少調度器,比如華為的Volcano調度器,騰訊TKE也有類似的batch調度器
8.常見問題解析
8.1.required/preferredDuringSchedulingIgnoredDuringExecution 辨析
8.1.1.required/preferredDuringSchedulingIgnoredDuringExecution 名稱解析
- requiredDuringSchedulingIgnoredDuringExecution可以拆分為2部分
- requiredDuringScheduling??(調度階段規則)
- ??含義??:在 Pod 調度過程中 ??必須滿足?? 的條件,若不滿足,Pod 將無法被調度,持續處于 Pending 狀態。
- ??行為??:調度器(kube-scheduler)會嚴格過濾節點,僅選擇符合標簽匹配條件的節點。例如,要求節點必須帶有 disktype=ssd 標簽,否則拒絕調度。
- ??IgnoredDuringExecution??(執行階段規則)
- ??含義??:Pod ??運行后??,即使節點標簽或條件發生變化(如標簽被刪除),Kubernetes ??不會驅逐或重新調度?? Pod。
- ??行為??:僅影響調度階段的邏輯,運行階段的變化由用戶主動干預(如手動刪除 Pod 觸發重新調度)。
- requiredDuringScheduling??(調度階段規則)
- preferredDuringSchedulingIgnoredDuringExecution也可以拆分為2部分
- preferredDuringScheduling??(調度階段規則)
- ??含義??:在 Pod 調度過程中 ??盡量滿足?? 的條件,允許 Pod ??優先??(而非強制)被調度到滿足特定條件的節點。如果條件無法滿足,Pod 仍會被調度到其他可用節點,但調度器會盡量選擇最接近條件的節點
- ??IgnoredDuringExecution??(執行階段規則)
- 同上
- preferredDuringScheduling??(調度階段規則)
8.1.2.required/preferred應用的調度階段
- requiredDuringSchedulingIgnoredDuringExecution:是必須要滿足的條件,不滿足就不能調度,因此是 Predicates階段生效的
- preferredDuringSchedulingIgnoredDuringExecution:是盡量滿足的條件,配置時會攜帶一個權重用于打分,因此是 Priority階段生效的
8.2.kubectl apply/replace命令的區別
- 區別:
- apply是patch merge操作,不涉及的字段不會覆蓋
- replace是 put請求 全量替換資源,會直接用請求的資源替換環境資源
- 示例:
- 比如下面的pod,只有required…,沒有preferred…,那么我用一個只包含preferred…的yaml 去apply,會導致nodeAffinity下面的required…刪除嗎?答案是不會
- 但是如果使用 replace -f xxx.yaml,就會清理required…,因為是直接替換了
affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬約束nodeSelectorTerms: # 要滿足的條件是什么,predicates階段- matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues: [zone-a, zone-b]
8.3.Pod被驅逐的幾種情況
- 高優先級Pod搶占低優先級,低優Pod會被驅逐,此時不會看QoS的
- 集群資源承壓,kubelet可能會根據Qos對某些pod做驅逐
8.4.通過調度策略綁定邊緣節點
- 一般來說,應用需要通過外部elb暴露出去,但是有些公司 不想投入資金到elb,就可以通過nodeSelector或一些調度手段,將ingress綁定到某幾臺固定的node上,將這幾臺node作為邊緣節點,專用于接收用戶流量
- 屬于常規操作
8.5.生產上一些實踐經驗
- 總的來說,kubernetes調度器是最讓人省心的組件之一,穩定性比較好,一般不會有太大的維護上的問題