以下是整個(gè)PaaS平臺(tái)的架構(gòu)
其中主要包括這些子系統(tǒng):
微服務(wù)治理框架:為應(yīng)用提供自動(dòng)注冊(cè)、發(fā)現(xiàn)、治理、隔離、調(diào)用分析等一系列分布式/微服務(wù)治理能力,屏蔽分布式系統(tǒng)的復(fù)雜度。
應(yīng)用調(diào)度與資源管理框架:打通從應(yīng)用建模、編排部署到資源調(diào)度、彈性伸縮、監(jiān)控自愈的生命周期管理自動(dòng)化。
應(yīng)用開(kāi)發(fā)流水線框架:打通從編寫代碼提交到自動(dòng)編譯打包、持續(xù)集成、自動(dòng)部署上線的一系列CI/CD全流程自動(dòng)化。
云中間件服務(wù):應(yīng)用云化所需的數(shù)據(jù)庫(kù)、大數(shù)據(jù)、通信和應(yīng)用中間件服務(wù);通過(guò)服務(wù)集成管控可集成傳統(tǒng)非云化的中間件能力。
面對(duì)一個(gè)如此復(fù)雜的系統(tǒng),性能優(yōu)化工作是一個(gè)非常艱巨的挑戰(zhàn),這里有這么一些痛點(diǎn):
源代碼及開(kāi)發(fā)組件多,100+ git repo,整體構(gòu)建超過(guò)1天
運(yùn)行架構(gòu)復(fù)雜,全套安裝完需要30+VM,200+進(jìn)程
軟件棧深,網(wǎng)絡(luò)平面復(fù)雜
集群規(guī)模大,5k — 10k節(jié)點(diǎn)環(huán)境搭建非常困難
系統(tǒng)操作會(huì)經(jīng)過(guò)分布式的多個(gè)組件,無(wú)法通過(guò)單一組件診斷發(fā)現(xiàn)系統(tǒng)瓶頸
無(wú)法追蹤上千個(gè)處于不同層次的API的時(shí)延和吞吐
大部分開(kāi)發(fā)人員專注于功能開(kāi)發(fā),無(wú)法意識(shí)到自己的代碼可能造成性能問(wèn)題
那么,對(duì)于這么一個(gè)大的、復(fù)雜的系統(tǒng),從方法論的角度來(lái)講,應(yīng)該怎么去優(yōu)化呢?基本思路就是做拆分,把一個(gè)大的問(wèn)題分解為多個(gè)互相不耦合的維度,進(jìn)行各個(gè)擊破。從大的維度來(lái)講,一個(gè)PaaS容器集群,可以分為3個(gè)大的子系統(tǒng)。
控制子系統(tǒng):控制指令的下發(fā)和運(yùn)行(k8s),例如創(chuàng)建pod
業(yè)務(wù)流量子系統(tǒng):容器網(wǎng)絡(luò)(flannel)、負(fù)載均衡(ELB/kube-proxy)
監(jiān)控子系統(tǒng):監(jiān)控告警數(shù)據(jù)的采集(kafka, Hadoop)
這個(gè)看起來(lái)僅僅是一個(gè)架構(gòu)上的劃分,那么如何和具體的業(yè)務(wù)場(chǎng)景對(duì)應(yīng)起來(lái)呢?我們可以考慮如下一個(gè)場(chǎng)景,在PaaS平臺(tái)上大批量的部署應(yīng)用??纯丛诓渴饝?yīng)用的過(guò)程中,會(huì)對(duì)各個(gè)子系統(tǒng)產(chǎn)生什么壓力。
應(yīng)用軟件包大?。?00M
應(yīng)用模板大?。?0M
1000個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)一個(gè)POD,一個(gè)實(shí)例
10種類型的軟件包,依賴長(zhǎng)度為3,10GB 網(wǎng)絡(luò)
調(diào)度及資源管理 3VM
這是一個(gè)典型的部署應(yīng)用的一些規(guī)格,那么對(duì)于這樣的一個(gè)輸入,我們可以按照架構(gòu)把壓力分解到每個(gè)子系統(tǒng)上,這樣得出的子系統(tǒng)需要支撐的指標(biāo)是:
控制子系統(tǒng): kubernetes調(diào)度速度 > 50 pods/s,倉(cāng)庫(kù)支持300并發(fā)下載,>40M/s
數(shù)據(jù)子系統(tǒng):overlay容器網(wǎng)絡(luò)TCP收發(fā)性能損耗 <5%
監(jiān)控子系統(tǒng):在上面這個(gè)場(chǎng)景中不涉及,但可以從別的場(chǎng)景大致告警處理能力100條/秒
這里的業(yè)務(wù)場(chǎng)景:架構(gòu)分析:子系統(tǒng)指標(biāo),這三者是m:1:n的,也就是說(shuō)在不同場(chǎng)景下對(duì)不同的組件的性能要求不同,最后每個(gè)組件需要取自己指標(biāo)的大值。
指標(biāo)決定了后續(xù)怎么進(jìn)行實(shí)驗(yàn)測(cè)試,而測(cè)試是要花較大時(shí)間成本的,所以在指標(biāo)的選取上要求少求精,盡量力圖用2-3個(gè)指標(biāo)衡量子系統(tǒng)。
上面講的還是偏紙上的推演和分析,接下來(lái)進(jìn)入實(shí)戰(zhàn)階段
對(duì)于服務(wù)器后端的程序來(lái)講,推薦使用Promtheus這個(gè)工具來(lái)做指標(biāo)的定義和采集。Promtheus的基本工作原理是:后端程序引入Promtheus的SDK,自定義所有需要的測(cè)量的指標(biāo),然后開(kāi)啟一個(gè)http的頁(yè)面,定期刷新數(shù)據(jù)。Promtheus服務(wù)器會(huì)定期抓取這個(gè)頁(yè)面上的數(shù)據(jù),并存在內(nèi)部的時(shí)間序列數(shù)據(jù)庫(kù)內(nèi)。這種抓而非推的方式減少了對(duì)被測(cè)試程序的壓力,避免了被測(cè)程序要頻繁往外發(fā)送大量數(shù)據(jù),導(dǎo)致自身性能反而變差而導(dǎo)致測(cè)量不準(zhǔn)確。Promtheus支持這幾種數(shù)據(jù)類型:
計(jì)數(shù)(對(duì)應(yīng)收集器初始化方法NewCounter、NewCounterFunc、NewCounterVec,單一數(shù)值,數(shù)值一直遞增,適合請(qǐng)求數(shù)量統(tǒng)計(jì)等)
測(cè)量(對(duì)應(yīng)收集器初始化方法NewGauge、NewGaugeFunc、NewGaugeVec,單一數(shù)值,數(shù)值增減變動(dòng),適合CPU、Mem等的統(tǒng)計(jì))
直方圖測(cè)量(對(duì)應(yīng)收集器初始化方法NewHistogram、NewHistogramVec,比較適合時(shí)長(zhǎng)等的統(tǒng)計(jì))
概要測(cè)量(對(duì)應(yīng)收集器初始化方法NewSummary、NewSummaryVec,比較適合請(qǐng)求時(shí)延等的統(tǒng)計(jì))
我們可以看看在kubernetes項(xiàng)目里面是怎么用的:
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"}, ) )
在這里,一個(gè)http請(qǐng)求被分為verb, resource, client, contentType, code這五個(gè)維度,那么后面在PromDash上就能圖形化的畫出這些請(qǐng)求的數(shù)量。 從而分析哪種類型的請(qǐng)求是最多,對(duì)系統(tǒng)造成大壓力的,如圖
除了Promtheus,還可以引入其他的測(cè)量手段,對(duì)系統(tǒng)進(jìn)行分析。
在kubernetes調(diào)度過(guò)程中,各個(gè)狀態(tài)Pod的數(shù)量,看哪一步是最卡的
go pprof分析,哪些函數(shù)是最耗CPU的
發(fā)現(xiàn)了瓶頸之后,下一步就是解決瓶頸,和具體業(yè)務(wù)邏輯有關(guān),本文在這里就不做過(guò)多的闡釋。需要對(duì)相關(guān)代碼非常熟悉,在不改變功能的情況下增強(qiáng)性能,基本思路為并發(fā)/緩存/去除無(wú)用步驟等。
在上面的優(yōu)化過(guò)程當(dāng)中,基本上工程師要做幾百次優(yōu)化的測(cè)試和開(kāi)發(fā)。這里會(huì)產(chǎn)生一個(gè)循環(huán):
測(cè)試尋找瓶頸點(diǎn)
修改代碼突破這個(gè)瓶頸點(diǎn)
重新測(cè)試驗(yàn)證這段代碼是否有效,是否需要改優(yōu)化思路
這就是一個(gè)完整的優(yōu)化的迭代過(guò)程,在這個(gè)過(guò)程當(dāng)中,大部分時(shí)間被浪費(fèi)在構(gòu)建代碼、搭建環(huán)境、輸出報(bào)告上。開(kāi)發(fā)人員真正思考和寫代碼的時(shí)間比較短。為了解決這個(gè)問(wèn)題,就需要做很多自動(dòng)化的工作。在kubernetes優(yōu)化的過(guò)程中,有這么幾項(xiàng)方法可以節(jié)省時(shí)間:
5.PNG
kubemark模擬器 :社區(qū)項(xiàng)目,使用容器模擬虛擬機(jī),在測(cè)試中模擬比達(dá)到1:20,也就是一臺(tái)虛擬機(jī)可以模擬20臺(tái)虛擬機(jī)對(duì)apiserver產(chǎn)生的壓力。在測(cè)試過(guò)程當(dāng)中,我們使用了500臺(tái)虛擬機(jī),模擬了10000節(jié)點(diǎn)的控制面行為。
CI集成:提交PR后自動(dòng)拉性能優(yōu)化分支并開(kāi)始快速構(gòu)建
CD集成:使用I層的快照機(jī)制,快速搭建集群并執(zhí)行測(cè)試案例輸出測(cè)試報(bào)告
以上都是在實(shí)踐過(guò)程中總結(jié)的一些點(diǎn),對(duì)于不同的項(xiàng)目工程應(yīng)該有很多點(diǎn)可以做進(jìn)一步的優(yōu)化,提升迭代效率。
在搭建完這套系統(tǒng)后,我們發(fā)現(xiàn)這個(gè)系統(tǒng)可以從源頭上預(yù)防降低系統(tǒng)性能的代碼合入主線。如果一項(xiàng)特性代碼造成了性能下降,在CI的過(guò)程當(dāng)中,功能開(kāi)發(fā)者就能收到性能報(bào)告,這樣開(kāi)發(fā)者就能自助式的去查找自己代碼的性能問(wèn)題所在,減少性能工程師的介入。