提高Kubernetes調度器的性能

jopen 8年前發布 | 20K 次閱讀 Kubernetes

【編者的話】本文是CoreOS近期對Kubernetes擴容性的一些針對性試驗、檢測和研究,分析并且得出了對K8S集群部署和pod吞吐量等K8S集群性能問題、擴容性問題上一系列的嘗試和見解。該文章回顧了從硬件到軟件層面采用縮小范圍以及使用K8S提供的端對端API性能指標和使用benchmarking作為基準工具等手段進行對建立不同規模集群過程中的pod吞吐量測試,從而發現K8S集群調度器性能的潛在瓶頸和潛在解決方案。

擴容性對任何一個分布式系統的成功而言都是一個重要的因素。在CoreOS,我們非常喜歡Kubernetes,希望能夠通過對上游的貢獻來推進這個項目。去年11月,我們建立了一個團隊,專門研究Kubernetes的擴容性。我們設定了一個目標來檢測和理解k8s集群的性能以獲取集群在宏大工作量之下的表現行為這方面的見解和知識,來學習K8S集群的性能問題大概在什么地方。

很快我們就開展了這方面的工作,我們發現了一系列的瓶頸。我們解決了這些瓶頸,把K8S調度基準在1千個節點上調度3萬個pod的時間從8780秒降到587秒。在這篇文章里,我們來分享一下我們是如何獲得這個超過了十倍的性能提升,并且希望拋磚引玉能讓社區進一步把K8S擴容性做進一步提升。

Kubernetes架構概覽

為了理解和提高Kubernetes的性能,我們首先需要建立一個目前能力的基線。一個Kubernetes集群是由一整套控制管理集群操作的API節點來組成的,API節點跑在工作節點上,應用的pods也跑在這些工作節點上。我們最初把目光聚集在控制層部件,因為它們參與了集群層面的每一個調度操作,這是我們理解性能最有可能提升的地方。Kubernetes的構架參見下圖。

提高Kubernetes調度器的性能

對Kubemark的試驗

K8S社區最近推出了Kubemark,用來測試控制層部件的性能。Kubemark模擬了工作節點,可以在一個單獨的CPU內核上模擬10個真實節點,讓我們以最低的復雜度和消耗來模擬大型集群。

我們對Kubemark的第一個使用,是進行一個密度測試來測量它需要多長時間在每一個節點上調度30個pod。對一個100個節點的集群來說,有3千個pod需要調度和跑起來;對于1000個節點的集群而言,那就有3萬個pod. 這些測試結果顯示了在不同時期的pod數量。我們所有的測試,集群都是連接在一個etcd集群上,etcd跑在一個單獨機器上。

Pods:  229 out of 3000 created,  211 running, 18 pending, 0 waiting

Pods: 429 out of 3000 created, 412 running, 17 pending, 0 waiting

Pods: 622 out of 3000 created, 604 running, 17 pending, 1 waiting

...

Pods: 2578 out of 3000 created, 2561 running, 17 pending, 0 waiting

Pods: 2779 out of 3000 created, 2761 running, 18 pending, 0 waiting

Pods: 2979 out of 3000 created, 2962 running, 16 pending, 1 waiting

Pods: 3000 out of 3000 created, 3000 running, 0 pending, 0 waiting</pre>

我們以一個100個節點的集群測試來開始我們的調查,它在150秒內完成了調度3千個節點的任務。pod的吞吐量在20個pod/秒。為了更好的理解這個日志,我們寫了一個 plot工具 來畫圖。

下面這個圖顯示了被 replication controller建立的但并非調度的pod,顯示了它們一旦被調度到集群的機器里跑起來的時候。

提高Kubernetes調度器的性能

我們很快注意到這個圖顯示了一個pod創建的線性情況, 在20個pod/秒,看上去很低,說明存在一個潛在的可以提高的瓶頸和目標。我們就從這里開始性能的旅程。

奔向更好吞吐量的旅程

我們腦海中想到的第一件事情是也許硬件資源吃緊了。如果是這樣的話,我們可以簡單地通過增加資源來提高調度的性能。我們又跑了測試,在監控CPU、內存、IO使用率到頂的情況。結果看上去如下:

提高Kubernetes調度器的性能

我們所觀察到的來看,沒有任何物理資源是完全被使用的。這說明問題出在軟件上。

在一個類似Kubernetes這樣龐大的代碼庫中去發現瓶頸,猶如大海撈針。我們需要一步步來縮小范圍。幸運的是,Kubernetes提供了大多數端對端API調用的性能指標(被 Prometheus 這個開源項目所收集)。我們第一步就是在這里面尋找。我們又跑了測試,監控了調度器的指標。結果如下:

# HELP scheduler_e2e_scheduling_latency_microseconds E2e scheduling latency (scheduling algorithm + binding)

TYPE scheduler_e2e_scheduling_latency_microseconds summary

scheduler_e2e_scheduling_latency_microseconds{quantile="0.5"} 23207

scheduler_e2e_scheduling_latency_microseconds{quantile="0.9"} 35112

scheduler_e2e_scheduling_latency_microseconds{quantile="0.99"} 40268

scheduler_e2e_scheduling_latency_microseconds_sum 7.1321295e+07

我們發現調度器端到端的延遲達到7ms,相當于140pod/秒的吞吐量,比我們之前測試看到的20pod/秒要高出很多。這說明并經在調度器自身以外。

我們繼續通過查看性能指標和日志來縮小問題的范圍。我們發現并平在replication controller里。看這里:

wait.Add(diff)

for i := 0; i < diff; i++ {

go func() {

defer wait.Done()

if err := rm.podControl.CreatePods(...); err != nil {

    ...

}

}()

}

wait.Wait()</pre>

我們打印了日志,發現等到500 CreatePods() 用了25秒。這正好是20pod/秒,但 CreatePods() 的延遲只有1ms.

不久之后,我們在客戶端用戶里發現了一個速率限制. 速率限制是用來保護API服務器不被過度使用。在我們的測試中,我們并不需要它。 我們提高了限制,想看看調度速度的邊界。它沒有完全解決問題。

結果,我們又發現了一個速率限制,并且修復了一個程序的非有效路徑( #17885 )。在那些變化之后,我們得以看到了最終的提高。在100個節點的建立中,平均建立速度從20pod/秒提高到超過300pod/秒,在50秒中完成,平均pod吞吐量在60pod/秒。

提高Kubernetes調度器的性能

目光轉向調度器

然后我們嘗試在1千個節點的集群上進行提高。然而,我們這次沒這么幸運。看看下圖:

提高Kubernetes調度器的性能

在一千個節點的集群上,花了5243秒來跑3萬個pod,平均吞吐量在5.72pod/秒。Creation rate持續增加,但running rate一直比較低。

我們然后又進行了測試,查看調度器的性能指標。這次,調度延遲變成了開始時的60ms,到結束時(即3萬個pod)增加到了200ms.結合我們在日志和圖表中所見,我們意識到以下兩點:

(1)60ms的延遲很高。調度器很可能變成了瓶頸。如果我們能把它降低,這樣就能增加調度速度,這樣pod的running rate就很有可能變高。

(2)調度延遲隨著被調度pod總體數量的增加而上升,這導致了集群pod的running rate的下降。這在調度器里面是個擴容問題。

調度器的代碼庫是很復雜的,我們需要很細致的歸檔才能理解調度器在哪塊花費了時間。但是,在Kubernetes上面重復同樣的過程是很耗時的,我們的測試用了超過兩個小時才完成。我們想要一個更加輕便一些的方法來做調度器組建測試來集中我們的精力和時間。這樣,我們就能寫一個像Go unit test那樣的調度器的benchmark做基準工具。這個工具測試調度器作為一個整體,而不用啟動不必要的部件。更多細節可以從我們的 幻燈片 中找到關于調度器性能測試以及在 Kubernetes pull request#18458 中看到。

通過使用benchmarking做基準的工具,我們能夠很有效地工作,來打破調度器在1千個節點集群的3萬個pod建立上的瓶頸。

例如,在 Kubernetes的issue#18126 中,我們有如下pprof (performance profiling)結果:

提高Kubernetes調度器的性能

我們看見 Round() 方法在總共的79秒鐘花費了18秒。我們覺得這對于取整而言是效率很低的。我們用更加有效的實現方式來調整了這個問題( PR #18170 )。結果,我們把對1千個節點上調度1千個pod的平均調度延遲時間從53秒降低到23秒。

這樣,我們能夠挖掘出更多沒有效率的且成為了瓶頸的代碼。我們給上游匯報并且開了很多issue:

? https://github.com/kubernetes/ ... 18170

? https://github.com/kubernetes/ ... 18255

? https://github.com/kubernetes/ ... 18413

? https://github.com/kubernetes/ ... 18831

通過使用這些變化,我們獲得了難以置信的新能提升——調度吞度量達到51pod/秒。我們又一次跑了調度器的基準為1千個節點集群上調度3萬個pod,拿它和之前的結果相比較,請看下圖:

提高Kubernetes調度器的性能

請注意這個過程比跑Kubemark時間更長,有可能是垃圾回收導致的。我們把所有東西放在同一個程序里,Kubemark在不同的進程里來跑調度器、API服務器和controller管理器。但這個區別不影響比較的結果,假設這樣是有可比性的。

現狀和未來的步驟

通過我們的優化,我們重新跑了kubemark“1千個節點/3萬個pod”測試。結果如下圖:

提高Kubernetes調度器的性能

現在,平均pod吞吐量為16.3pod/秒,在之前數據為5.72pod/秒。首先,我們看到了吞吐量的提高。第二,這個數字仍然比我們調度器基準的要低。我們確信在這點上,調度器本身不太可能是瓶頸。有很多其他因素,比如遠程調用延遲、API服務器中的垃圾回收等等。這可能是一個未來性能提升的下一個探索方向。

擴容Kubernetes

在這篇博文中,我們討論了我們是如何分析類似于性能指標和CPU歸檔的實驗結果來確定性能瓶頸和提高調度器的。

為了更好的理解調度器,我們提供了一個基準的工具,我們用它來驗證我們的性能提升。到我們目前的工作位置,我們把1千個節點上調度3萬個pod的時間從8780秒降低到了587秒,給Kubernetes發了4個PR.

盡管在這里描述的技術很簡單,把我們的想法過程分享給大家會幫助其他人通過把調查研究切分成能夠處理的一塊塊,從而來debug復雜的分布式系統。

這里重要的是以下幾點:

(1)性能指標提供了一個便利和非常亟需的觀察系統的視野

(2)使用基準是一個圖解性能和發現任何潛在問題的良好方式

(3)畫圖是一種在一段時間后觀察系統的更為良好的途徑

原文鏈接: Improving Kubernetes Scheduler Performance (翻譯:韓佳瑤)

===========================

譯者介紹

韓佳瑤 ,才云科技(Caicloud)聯合創始人。

來自: http://dockone.io/article/1050

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!