PaaS容器叢集優化之路
1. 效能優化面對的挑戰
以下是整個PaaS平臺的架構
其中主要包括這些子系統:
- 微服務治理框架:為應用提供自動註冊、發現、治理、隔離、呼叫分析等一系列分散式/微服務治理能力,遮蔽分散式系統的複雜度。
- 應用排程與資源管理框架:打通從應用建模、編排部署到資源排程、彈性伸縮、監控自愈的生命週期管理自動化。
- 應用開發流水線框架:打通從編寫程式碼提交到自動編譯打包、持續整合、自動部署上線的一系列CI/CD全流程自動化。
- 雲中間件服務:應用雲化所需的資料庫、大資料、通訊和應用中介軟體服務;通過服務整合管控可整合傳統非雲化的中介軟體能力。
面對一個如此複雜的系統,效能優化工作是一個非常艱鉅的挑戰,這裡有這麼一些痛點:
- 原始碼及開發元件多,100+ git repo,整體構建超過1天
- 執行架構複雜,全套安裝完需要30+VM,200+程序
- 軟體棧深,網路平面複雜
- 叢集規模大,5k — 10k節點環境搭建非常困難
- 系統操作會經過分散式的多個元件,無法通過單一元件診斷髮現系統瓶頸
- 無法追蹤上千個處於不同層次的API的時延和吞吐
- 大部分開發人員專注於功能開發,無法意識到自己的程式碼可能造成效能問題
2. 優化分析
那麼,對於這麼一個大的、複雜的系統,從方法論的角度來講,應該怎麼去優化呢?基本思路就是做拆分,把一個大的問題分解為多個互相不耦合的維度,進行各個擊破。從大的維度來講,一個PaaS容器叢集,可以分為3個大的子系統。
- 控制子系統:控制指令的下發和執行(k8s),例如建立pod
- 業務流量子系統:容器網路(flannel)、負載均衡(ELB/kube-proxy)
- 監控子系統:監控告警資料的採集(kafka, Hadoop)
這個看起來僅僅是一個架構上的劃分,那麼如何和具體的業務場景對應起來呢?我們可以考慮如下一個場景,在PaaS平臺上大批量的部署應用。看看在部署應用的過程中,會對各個子系統產生什麼壓力。
- 應用軟體包大小:400M
- 應用模板大小:10M
- 1000個節點,每個節點一個POD,一個例項
- 10種類型的軟體包,依賴長度為3,10GB 網路
- 排程及資源管理 3VM
這是一個典型的部署應用的一些規格,那麼對於這樣的一個輸入,我們可以按照架構把壓力分解到每個子系統上,這樣得出的子系統需要支撐的指標是:
- 控制子系統: kubernetes排程速度 > 50 pods/s,倉庫支援300併發下載,>40M/s
- 資料子系統:overlay容器網路TCP收發效能損耗 <5%
- 監控子系統:在上面這個場景中不涉及,但可以從別的場景大致告警處理能力100條/秒
這裡的業務場景:架構分析:子系統指標,這三者是m:1:n的,也就是說在不同場景下對不同的元件的效能要求不同,最後每個元件需要取自己指標的最大值。
指標決定了後續怎麼進行實驗測試,而測試是要花較大時間成本的,所以在指標的選取上要求少求精,儘量力圖用2-3個指標衡量子系統。
3. 優化測試 & 工具
上面講的還是偏紙上的推演和分析,接下來進入實戰階段
對於伺服器後端的程式來講,推薦使用Promtheus這個工具來做指標的定義和採集。Promtheus的基本工作原理是:後端程式引入Promtheus的SDK,自定義所有需要的測量的指標,然後開啟一個http的頁面,定期重新整理資料。Promtheus伺服器會定期抓取這個頁面上的資料,並存在內部的時間序列資料庫內。這種抓而非推的方式減少了對被測試程式的壓力,避免了被測程式要頻繁往外發送大量資料,導致自身效能反而變差而導致測量不準確。Promtheus支援這幾種資料型別:
- 計數(對應收集器初始化方法NewCounter、NewCounterFunc、NewCounterVec,單一數值,數值一直遞增,適合請求數量統計等)
- 測量(對應收集器初始化方法NewGauge、NewGaugeFunc、NewGaugeVec,單一數值,數值增減變動,適合CPU、Mem等的統計)
- 直方圖測量(對應收集器初始化方法NewHistogram、NewHistogramVec,比較適合時長等的統計)
- 概要測量(對應收集器初始化方法NewSummary、NewSummaryVec,比較適合請求時延等的統計)
我們可以看看在kubernetes專案裡面是怎麼用的:
var (
// TODO(a-robinson): Add unit tests for the handling of these metrics once
// the upstream library supports it.
requestCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "apiserver_request_count",
Help: "Counter of apiserver requests broken out for each verb, API resource, client, and HTTP response contentType and code.",
},
[]string{"verb", "resource", "client", "contentType", "code"},
)
requestLatencies = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "apiserver_request_latencies",
Help: "Response latency distribution in microseconds for each verb, resource and client.",
// Use buckets ranging from 125 ms to 8 seconds.
Buckets: prometheus.ExponentialBuckets(125000, 2.0, 7),
},
[]string{"verb", "resource"},
)
requestLatenciesSummary = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "apiserver_request_latencies_summary",
Help: "Response latency summary in microseconds for each verb and resource.",
// Make the sliding window of 1h.
MaxAge: time.Hour,
},
[]string{"verb", "resource"},
)
)
在這裡,一個http請求被分為verb, resource, client, contentType, code
這五個維度,那麼後面在PromDash上就能圖形化的畫出這些請求的數量。 從而分析哪種型別的請求是最多,對系統造成最大壓力的,如圖
除了Promtheus,還可以引入其他的測量手段,對系統進行分析。
- 在kubernetes排程過程中,各個狀態Pod的數量,看哪一步是最卡的
- go pprof分析,哪些函式是最耗CPU的
4. 優化開發
發現了瓶頸之後,下一步就是解決瓶頸,和具體業務邏輯有關,本文在這裡就不做過多的闡釋。需要對相關程式碼非常熟悉,在不改變功能的情況下增強效能,基本思路為併發/快取/去除無用步驟等。
5. 優化成果
這是我們在kubernetes專案上控制面優化的成果
控制面指標 | 華為分支資料 | 社群版資料 |
---|---|---|
Master節點數量 | 5 | 無明確資料 |
Node節點(kubemark模擬)數量 | 10000節點 | 5000節點 |
部署吞吐率 | >100 pod/s | 約為50 pod/s |
Pod端到端延時 | <2s | <5s |
API延時 | <79.321ms | <1s |
這裡僅僅顯示了控制子系統的指標,其他子系統還沒有支援那麼大的叢集,尤其在網路方面,不同使用者的網路架構差別很大。所以資料僅供參考。
6. 優化的優化
在上面的優化過程當中,基本上工程師要做幾百次優化的測試和開發。這裡會產生一個迴圈:
- 測試尋找瓶頸點
- 修改程式碼突破這個瓶頸點
- 重新測試驗證這段程式碼是否有效,是否需要改優化思路
這就是一個完整的優化的迭代過程,在這個過程當中,大部分時間被浪費在構建程式碼、搭建環境、輸出報告上。開發人員真正思考和寫程式碼的時間比較短。為了解決這個問題,就需要做很多自動化的工作。在kubernetes優化的過程中,有這麼幾項方法可以節省時間:
- kubemark模擬器 :社群專案,使用容器模擬虛擬機器,在測試中模擬比達到1:20,也就是一臺虛擬機器可以模擬20臺虛擬機器對apiserver產生的壓力。在測試過程當中,我們使用了500臺虛擬機器,模擬了10000節點的控制面行為。
- CI整合:提交PR後自動拉效能優化分支並開始快速構建
- CD整合:使用I層的快照機制,快速搭建叢集並執行測試案例輸出測試報告
以上都是在實踐過程中總結的一些點,對於不同的專案工程應該有很多點可以做進一步的優化,提升迭代效率。
在搭建完這套系統後,我們發現這個系統可以從源頭上預防降低系統性能的程式碼合入主線。如果一項特性程式碼造成了效能下降,在CI的過程當中,功能開發者就能收到效能報告,這樣開發者就能自助式的去查詢自己程式碼的效能問題所在,減少效能工程師的介入。