解決k8s調度不均衡問題

前言

在近期的工作中,我們發現 k8s 集群中有些節點資源使用率很高,有些節點資源使用率很低,我們嘗試重新部署應用和驅逐 Pod,發現并不能有效解決負載不均衡問題。在學習了 Kubernetes 調度原理之后,重新調整了 Request 配置,引入了調度插件,才最終解決問題。這篇就來跟大家分享 Kubernetes 資源和調度相關知識,以及如何解決k8s調度不均衡問題。

Kubernetes 的資源模型

在 Kubernetes 里,Pod 是最小的原子調度單位。這也就意味著,所有跟調度和資源管理相關的屬性都應該是屬于 Pod 對象的字段。而這其中最重要的部分,就是 Pod 的 CPU 和內存配置。
像 CPU 這樣的資源被稱作“可壓縮資源”(compressible resources)。它的典型特點是,當可壓縮資源不足時,Pod 只會“饑餓”,但不會退出。
而像內存這樣的資源,則被稱作“不可壓縮資源(incompressible resources)。當不可壓縮資源不足時,Pod 就會因為 OOM(Out-Of-Memory)被內核殺掉。
Pod 可以由多個 Container 組成,所以 CPU 和內存資源的限額,是要配置在每個 Container 的定義上的。這樣,Pod 整體的資源配置,就由這些 Container 的配置值累加得到。
Kubernetes 里 Pod 的 CPU 和內存資源,實際上還要分為 limits 和 requests 兩種情況:

spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory

這兩者的區別其實非常簡單:在調度的時候,kube-scheduler 只會按照 requests 的值進行調度。而在真正設置 Cgroups 限制的時候,kubelet 則會按照 limits 的值來進行設置。
這是因為在實際場景中,大多數作業使用到的資源其實遠小于它所請求的資源限額,這種策略能有效的提高整體資源的利用率。

Kubernetes 的服務質量

服務質量 QoS 的英文全稱為 Quality of Service。在 Kubernetes 中,每個 Pod 都有個 QoS 標記,通過這個 Qos 標記來對 Pod 進行服務質量管理,它確定 Pod 的調度和驅逐優先級。在 Kubernetes 中,Pod 的 QoS 服務質量一共有三個級別:

  • Guaranteed:當 Pod 里的每一個 Container 都同時設置了 requests 和 limits,并且 requests 和 limits 值相等的時候,這個 Pod 就屬于 Guaranteed 類別 。
  • Burstable:而當 Pod 不滿足 Guaranteed 的條件,但至少有一個 Container 設置了 requests。那么這個 Pod 就會被劃分到 Burstable 類別。
  • BestEffort:而如果一個 Pod 既沒有設置 requests,也沒有設置 limits,那么它的 QoS 類別就是 BestEffort。

具體地說,當 Kubernetes 所管理的宿主機上不可壓縮資源短缺時,就有可能觸發 Eviction 驅逐。目前,Kubernetes 為你設置的 Eviction 的默認閾值如下所示:

memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%

當宿主機的 Eviction 閾值達到后,就會進入 MemoryPressure 或者 DiskPressure 狀態,從而避免新的 Pod 被調度到這臺宿主機上,然后 kubelet 會根據 QoS 的級別來挑選 Pod 進行驅逐,具體驅逐優先級是:BestEffort -> Burstable -> Guaranteed。
QoS 的級別是通過 Linux 內核 OOM 分數值來實現的,OOM 分數值取值范圍在-1000 ~1000之間。在 Kubernetes 中,常用服務的 OOM 的分值如下:

-1000  => sshd等進程   
-999   => Kubernetes 管理進程
-998   => Guaranteed Pod
0      => 其他進程  0
2~999  => Burstable Pod     
1000   => BestEffort Pod    

OOM 分數越高,就代表這個 Pod 的優先級越低,在出現資源競爭的時候,就越早被殺掉,分數為-999和-1000的進程永遠不會因為 OOM 而被殺掉。

劃重點:如果期望 Pod 盡可能的不被驅逐,就應當把 Pod 里的每一個 Container 的 requests 和 limits 都設置齊全,并且 requests 和 limits 值要相等。

Kubernetes 的調度策略

kube-scheduler 是 Kubernetes 集群的默認調度器,它的主要職責是為一個新創建出來的 Pod,尋找一個最合適的 Node。kube-scheduler 給一個 Pod 做調度選擇包含三個步驟:

  • 過濾:調用一組叫作 Predicate 的調度算法,將所有滿足 Pod 調度需求的 Node 選出來;
  • 打分:調用一組叫作 Priority 的調度算法,給每一個可調度 Node 進行打分;
  • 綁定:調度器將 Pod 對象的 nodeName 字段的值,修改為得分最高的 Node。

Kubernetes 官方過濾和打分編排源碼如下:
https://github.com/kubernetes/kubernetes/blob/281023790fd27eec7bfaa7e26ff1efd45a95fb09/pkg/scheduler/framework/plugins/legacy_registry.go

過濾(Predicate)

過濾階段,首先遍歷全部節點,過濾掉不滿足條件的節點,屬于強制性規則,這一階段輸出的所有滿足要求的 Node 將被記錄并作為第二階段的輸入,如果所有的節點都不滿足條件,那么 Pod 將會一直處于 Pending 狀態,直到有節點滿足條件,在這期間調度器會不斷的重試。
調度器會根據限制條件和復雜性依次進行以下過濾檢查,檢查順序存儲在一個名為 PredicateOrdering() 的函數中,具體如下表格:

算法名稱 默認 順序 詳細說明
CheckNodeUnschedulablePred 強制 1 檢查節點是否可調度;
GeneralPred 2 是一組聯合檢查,包含了:HostNamePred、PodFitsResourcesPred、PodFitsHostPortsPred、MatchNodeSelectorPred 4個檢查
HostNamePred 3 檢查 Pod 指定的 Node 名稱是否和 Node 名稱相同;
PodFitsHostPortsPred 4 檢查 Pod 請求的端口(網絡協議類型)在節點上是否可用;
MatchNodeSelectorPred 5 檢查是否匹配 NodeSelector 節點選擇器的設置;
PodFitsResourcesPred 6 檢查節點的空閑資源(例如,CPU 和內存)是否滿足 Pod 的要求;
NoDiskConflictPred 7 根據 Pod 請求的卷是否在節點上已經掛載,評估 Pod 和節點是否匹配;
PodToleratesNodeTaintsPred 強制 8 檢查 Pod 的容忍是否能容忍節點的污點;
CheckNodeLabelPresencePred 9 檢測 NodeLabel 是否存在;
CheckServiceAffinityPred 10 檢測服務的親和;
MaxEBSVolumeCountPred 11 已廢棄,檢測 Volume 數量是否超過云服務商 AWS 的存儲服務的配置限制;
MaxGCEPDVolumeCountPred 12 已廢棄,檢測 Volume 數量是否超過云服務商 Google Cloud 的存儲服務的配置限制;
MaxCSIVolumeCountPred 13 Pod 附加 CSI 卷的數量,判斷是否超過配置的限制;
MaxAzureDiskVolumeCountPred 14 已廢棄,檢測 Volume 數量是否超過云服務商 Azure 的存儲服務的配置限制;
MaxCinderVolumeCountPred 15 已廢棄,檢測 Volume 數量是否超過云服務商 OpenStack 的存儲服務的配置限制;
CheckVolumeBindingPred 16 基于 Pod 的卷請求,評估 Pod 是否適合節點,這里的卷包括綁定的和未綁定的 PVC 都適用;
NoVolumeZoneConflictPred 17 給定該存儲的故障區域限制, 評估 Pod 請求的卷在節點上是否可用;
EvenPodsSpreadPred 18 檢測 Node 是否滿足拓撲傳播限制;
MatchInterPodAffinityPred 19 檢測是否匹配 Pod 的親和與反親和的設置;

可以看出,Kubernetes 正在逐步移除某個具體云服務商的服務的相關代碼,而使用接口(Interface)來擴展功能。

打分(Priority)

打分階段,通過 Priority 策略對可用節點進行評分,最終選出最優節點。具體是用一組打分函數處理每一個可用節點,每一個打分函數會返回一個 0~100 的分數,分數越高表示節點越優, 同時每一個函數也會對應一個權重值。將每個打分函數的計算得分乘以權重,然后再將所有打分函數的得分相加,從而得出節點的最終優先級分值。權重可以讓管理員定義優選函數傾向性的能力,其計算優先級的得分公式如下:

finalScoreNode = (weight1 * priorityFunc1) + (weight2 * priorityFunc2) + … + (weightn * priorityFuncn)

全部打分函數如下表格所示:

算法名稱 默認 權重 詳細說明
EqualPriority - 給予所有節點相等的權重;
MostRequestedPriority - 支持最多請求資源的節點。 該策略將 Pod 調度到整體工作負載所需的最少的一組節點上;
RequestedToCapacityRatioPriority - 使用默認的打分方法模型,創建基于 ResourceAllocationPriority 的 requestedToCapacity;
SelectorSpreadPriority 1 屬于同一 Service、 StatefulSet 或 ReplicaSet 的 Pod,盡可能地跨 Node 部署(雞蛋不要只放在一個籃子里,分散風險,提高可用性);
ServiceSpreadingPriority - 對于給定的 Service,此策略旨在確保該 Service 關聯的 Pod 在不同的節點上運行。 它偏向把 Pod 調度到沒有該服務的節點。 整體來看,Service 對于單個節點故障變得更具彈性;
InterPodAffinityPriority 1 實現了 Pod 間親和性與反親和性的優先級;
LeastRequestedPriority 1 偏向最少請求資源的節點。 換句話說,節點上的 Pod 越多,使用的資源就越多,此策略給出的排名就越低;
BalancedResourceAllocation 1 CPU和內存使用率越接近的節點權重越高,該策略不能單獨使用,必須和 LeastRequestedPriority 組合使用,盡量選擇在部署Pod后各項資源更均衡的機器。
NodePreferAvoidPodsPriority 10000 根據節點的注解 scheduler.alpha.kubernetes.io/preferAvoidPods 對節點進行優先級排序。 你可以使用它來暗示兩個不同的 Pod 不應在同一節點上運行;
NodeAffinityPriority 1 根據節點親和中 PreferredDuringSchedulingIgnoredDuringExecution 字段對節點進行優先級排序;
TaintTolerationPriority 1 根據節點上無法忍受的污點數量,給所有節點進行優先級排序。 此策略會根據排序結果調整節點的等級;
ImageLocalityPriority 1 如果Node上存在Pod容器部分所需鏡像,則根據這些鏡像的大小來決定分值,鏡像越大,分值就越高;
EvenPodsSpreadPriority
2 實現了 Pod 拓撲擴展約束的優先級排序;

我自己遇到的是“多節點調度資源不均衡問題”,所以跟節點資源相關的打分算法是我關注的重點。
1、BalancedResourceAllocation(默認開啟),它的計算公式如下所示:

score = 10 - variance(cpuFraction,memoryFraction,volumeFraction)*10

其中,每種資源的 Fraction 的定義是 :Pod 的 request 資源 / 節點上的可用資源。而 variance 算法的作用,則是計算每兩種資源 Fraction 之間的“距離”。而最后選擇的,則是資源 Fraction 差距最小的節點。
所以說,BalancedResourceAllocation 選擇的,其實是調度完成后,所有節點里各種資源分配最均衡的那個節點,從而避免一個節點上 CPU 被大量分配、而 Memory 大量剩余的情況。
2、LeastRequestedPriority(默認開啟),它的計算公式如下所示:

score = (cpu((capacity-sum(requested))10/capacity) + memory((capacity-sum(requested))10/capacity))/2

可以看到,這個算法實際上是根據 request 來計算出空閑資源(CPU 和 Memory)最多的宿主機。
3、MostRequestedPriority(默認不開啟),它的計算公式如下所示:

score = (cpu(10 sum(requested) / capacity) + memory(10 sum(requested) / capacity)) / 2

在 ClusterAutoscalerProvider 中替換 LeastRequestedPriority,給使用多資源的節點更高的優先級。

你可以修改 /etc/kubernetes/manifests/kube-scheduler.yaml 配置,新增 v=10 參數來開啟調度打分日志。

自定義配置

如果官方默認的過濾和打分策略,無法滿足實際業務,我們可以自定義配置:

  • 調度策略:允許你修改默認的過濾 斷言(Predicates) 和打分 優先級(Priorities) 。
  • 調度配置:允許你實現不同調度階段的插件, 包括:QueueSort, Filter, Score, Bind, Reserve, Permit 等等。 你也可以配置 kube-scheduler 運行不同的配置文件。

解決k8s調度不均衡問題

一、按實際用量配置 Pod 的 requeste

從上面的調度策略可以得知,資源相關的打分算法 LeastRequestedPriority 和 MostRequestedPriority 都是基于 request 來進行評分,而不是按 Node 當前資源水位進行調度(在沒有安裝 Prometheus 等資源監控相關組件之前,kube-scheduler 也無法實時統計 Node 當前的資源情況),所以可以動態采 Pod 過去一段時間的資源使用率,據此來設置 Pod 的Request,才能契合 kube-scheduler 默認打分算法,讓 Pod 的調度更均衡。

二、為資源占用較高的 Pod 設置反親和

對一些資源使用率較高的 Pod ,進行反親和,防止這些項目同時調度到同一個 Node,導致 Node 負載激增。

三、引入實時資源打分插件 Trimaran

但在實際項目中,并不是所有情況都能較為準確的估算出 Pod 資源用量,所以依賴 request 配置來保障 Pod 調度的均衡性是不準確的。那有沒有一種通過 Node 當前實時資源進行打分調度的方案呢?Kubernetes 官方社區 SIG 小組提供的調度插件 Trimaran 就具備這樣的能力。

Trimaran 官網地址:https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/pkg/trimaran

Trimaran 是一個實時負載感知調度插件,它利用 load-watcher 獲取程序資源利用率數據。目前,load-watcher支持三種度量工具:Metrics Server、Prometheus 和 SignalFx。

  • Kubernetes Metrics Server:是 kubernetes 監控體系中的核心組件之一,它負責從 kubelet 收集資源指標,然后對這些指標監控數據進行聚合(依賴kube-aggregator),并在 Kubernetes Apiserver 中通過 Metrics API( /apis/metrics.k8s.io/) 公開暴露它們;
  • Prometheus Server: 是一款基于時序數據庫的開源監控告警系統,非常適合 Kubernetes 集群的監控。基本原理是通過 Http 協議周期性抓取被監控組件的狀態,任意組件只要提供對應的 Http 接口就可以接入監控。不需要任何 SDK 或者其他的集成過程。這樣做非常適合做虛擬化環境監控系統,比如 VM、Docker、Kubernetes 等。官網地址:https://prometheus.io/
  • SignalFx:是一家基礎設施及應用實時云監控服務商,它采用了一個低延遲、可擴展的流式分析引擎,以監視微服務(松散耦合、獨立部署的應用組件集合)和協調的容器環境(如Kubernetes和Docker)。官網地址:https://www.signalfx.com/

Trimaran 的架構如下:


image.png

可以看到在 kube-scheduler 打分的過程中,Trimaran 會通過 load-watcher 獲取當前 node 的實時資源水位,然后據此打分從而干預調度結果。

Trimaran 打分原理:https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/kep/61-Trimaran-real-load-aware-scheduling

四、引入重平衡工具 descheduler

從 kube-scheduler 的角度來看,調度程序會根據其當時對 Kubernetes 集群的資源描述做出最佳調度決定,但調度是靜態的,Pod 一旦被綁定了節點是不會觸發重新調度的。雖然打分插件可以有效的解決調度時的資源不均衡問題,但每個 Pod 在長期的運行中所占用的資源也是會有變化的(通常內存會增加)。假如一個應用在啟動的時候只占 2G 內存,但運行一段時間之后就會占用 4G 內存,如果這樣的應用比較多的話,Kubernetes 集群在運行一段時間后就可能會出現不均衡的狀態,所以需要重新平衡集群。
除此之外,也還有一些其他的場景需要重平衡:

  • 集群添加新節點,一些節點不足或過度使用;
  • 某些節點發生故障,其pod已移至其他節點;
  • 原始調度決策不再適用,因為在節點中添加或刪除了污點或標簽,不再滿足 pod/node 親和性要求。

當然我們可以去手動做一些集群的平衡,比如手動去刪掉某些 Pod,觸發重新調度就可以了,但是顯然這是一個繁瑣的過程,也不是解決問題的方式。為了解決實際運行中集群資源無法充分利用或浪費的問題,可以使用 descheduler 組件對集群的 Pod 進行調度優化,descheduler 可以根據一些規則和配置策略來幫助我們重新平衡集群狀態,其核心原理是根據其策略配置找到可以被移除的 Pod 并驅逐它們,其本身并不會進行調度被驅逐的 Pod,而是依靠默認的調度器來實現,descheduler 重平衡原理可參見官網。

descheduler 官網地址:https://github.com/kubernetes-sigs/descheduler

參考資料

本文由mdnice多平臺發布

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容