徐琛杰 周 翔 彭 鑫 趙文耘
(復(fù)旦大學(xué)軟件學(xué)院 上海 201203)(上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室(復(fù)旦大學(xué)) 上海 201203)
近年來,越來越多的公司如谷歌、亞馬遜[1]等開始采用微服務(wù)架構(gòu)。文獻(xiàn)[2]將微服務(wù)定義為可獨(dú)立開發(fā)、部署、測試和伸縮的應(yīng)用程序。基于微服務(wù)架構(gòu)的應(yīng)用系統(tǒng)(下文簡稱“微服務(wù)系統(tǒng)”)相較于單體應(yīng)用有易于開發(fā)、部署、維護(hù)和伸縮等優(yōu)點(diǎn)[3]。
微服務(wù)系統(tǒng)在線運(yùn)行時(shí),不同微服務(wù)擁有的實(shí)例數(shù)量不一,而微服務(wù)占有的資源量與實(shí)例數(shù)量呈正相關(guān)關(guān)系。微服務(wù)實(shí)例越多,該微服務(wù)占有的資源就越多,反之亦然[4]。進(jìn)而當(dāng)請(qǐng)求數(shù)量較多時(shí),若某個(gè)微服務(wù)實(shí)例數(shù)量過少,其占有的資源也會(huì)較少。這將導(dǎo)致該微服務(wù)出現(xiàn)平均響應(yīng)時(shí)間急劇變長的現(xiàn)象,嚴(yán)重影響用戶的使用。同時(shí),微服務(wù)系統(tǒng)線上運(yùn)行時(shí),其負(fù)載一直動(dòng)態(tài)變化,導(dǎo)致特定的部署方案無法有效降低系統(tǒng)的整體響應(yīng)時(shí)間。特別是在集群資源固定且有限的場景下,需要根據(jù)負(fù)載的變化動(dòng)態(tài)地調(diào)節(jié)各個(gè)微服務(wù)的實(shí)例數(shù)量,從而降低微服務(wù)系統(tǒng)的整體響應(yīng)時(shí)間。
Kubernetes[5]、Marathon[6]等當(dāng)下流行的微服務(wù)管理工具提供了微服務(wù)的自動(dòng)伸縮[7]功能。這些工具根據(jù)預(yù)先設(shè)定的條件為所有資源過少的微服務(wù)添加實(shí)例、為所有資源過多的微服務(wù)減少實(shí)例,從而調(diào)節(jié)微服務(wù)占有的資源量。但這些工具均假定在云計(jì)算環(huán)境下運(yùn)行,當(dāng)集群資源不足時(shí),可以動(dòng)態(tài)地向集群中添加新服務(wù)器。在集群資源固定且有限的場景下,當(dāng)集群資源不足時(shí),因無法添加新服務(wù)器,這些工具將無法如期調(diào)節(jié)微服務(wù)實(shí)例數(shù)量。此外,這些工具需要人工為每個(gè)微服務(wù)配置伸縮方案,配置工作相對(duì)繁重。
針對(duì)以上問題,本文提出了一個(gè)基于MAPE(Monitor、Analyze、Plan、Execute)環(huán)路[8]的自適應(yīng)部署優(yōu)化方法。在集群資源固定且有限的場景下,每輪MAPE循環(huán)時(shí),通過分析微服務(wù)調(diào)用鏈及各個(gè)微服務(wù)實(shí)例的資源使用情況,根據(jù)調(diào)用鏈上缺少資源的微服務(wù)數(shù)量,挑選出數(shù)量最多的一條調(diào)用鏈。當(dāng)集群資源充足時(shí),為挑選出的調(diào)用鏈上缺少資源的微服務(wù)各添加一個(gè)實(shí)例。當(dāng)集群資源不足時(shí),先為所有存在空閑實(shí)例的微服務(wù)各減少一個(gè)實(shí)例,再根據(jù)減少實(shí)例后集群資源空閑情況,為挑選出的調(diào)用鏈上部分或全部缺少資源的微服務(wù)各添加一個(gè)實(shí)例。最終達(dá)到降低微服務(wù)系統(tǒng)整體響應(yīng)時(shí)間的效果。在開源系統(tǒng)Spring Cloud Sleuth和Zipkin的基礎(chǔ)上,本文實(shí)現(xiàn)了一個(gè)面向微服務(wù)系統(tǒng)的運(yùn)行時(shí)可視化監(jiān)控工具,基于該工具實(shí)現(xiàn)了一個(gè)面向微服務(wù)系統(tǒng)的運(yùn)行時(shí)部署優(yōu)化工具,并通過實(shí)驗(yàn)驗(yàn)證了部署優(yōu)化方法的有效性。Train_ticket[9]是一個(gè)開源的、基于微服務(wù)架構(gòu)的火車票訂票系統(tǒng),擁有超過40個(gè)微服務(wù),本文基于該系統(tǒng)舉例說明相關(guān)方法和技術(shù),同時(shí)進(jìn)行實(shí)驗(yàn)驗(yàn)證。
谷歌開發(fā)的Dapper[10]是一個(gè)面向分布式系統(tǒng)的追蹤工具,通過向通信模塊、流程控制模塊和線程庫等被絕大多數(shù)應(yīng)用所使用的模塊中插入探針,使Dapper能在收集追蹤信息時(shí)保持對(duì)應(yīng)用層透明。Spring Cloud Sleuth是Spring Cloud提供的一個(gè)分布式追蹤解決方案。Zipkin是一個(gè)基于Dapper開發(fā)的分布式追蹤系統(tǒng),可以用來收集Spring Cloud Sleuth指導(dǎo)分布式系統(tǒng)產(chǎn)生的運(yùn)行時(shí)數(shù)據(jù)并可視化。
文獻(xiàn)[11]實(shí)現(xiàn)了一組監(jiān)控和管理微服務(wù)系統(tǒng)的靜態(tài)頁面,對(duì)單個(gè)微服務(wù)的平均響應(yīng)時(shí)間、吞吐量等進(jìn)行了可視化。本文不僅實(shí)現(xiàn)了單個(gè)微服務(wù)的平均響應(yīng)時(shí)間、吞吐量等的可視化,還實(shí)現(xiàn)了微服務(wù)調(diào)用鏈及線上部署情況的可視化,并結(jié)合收集到的數(shù)據(jù)進(jìn)行了可視化展示。
Kubernetes可以將單個(gè)微服務(wù)部署為一個(gè)Pod,通過自動(dòng)伸縮Pod的方式,實(shí)現(xiàn)微服務(wù)的自動(dòng)伸縮。但Kubernetes的自動(dòng)伸縮功能要求微服務(wù)系統(tǒng)運(yùn)行在云計(jì)算環(huán)境下,從而可以動(dòng)態(tài)地向集群中添加新服務(wù)器。Kubernetes會(huì)為所有資源利用率超過預(yù)先設(shè)定值的微服務(wù)啟動(dòng)新實(shí)例,但如果運(yùn)行的實(shí)例總數(shù)量過大,集群中的資源將不能啟動(dòng)全部新實(shí)例,Kubernetes就會(huì)添加新服務(wù)器到集群中,但是這種做法不適合集群資源固定且有限的場景。此外,Kubernetes還需人工為每個(gè)微服務(wù)單獨(dú)配置伸縮方案,如微服務(wù)實(shí)例數(shù)量的上下限、期望的CPU平均利用率等限制條件,配置過程相對(duì)繁瑣。文獻(xiàn)[12]將微服務(wù)調(diào)用鏈定義為請(qǐng)求到達(dá)微服務(wù)系統(tǒng)后,微服務(wù)之間的相互調(diào)用所形成的有向無環(huán)圖。本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法是在集群資源固定且有限的場景下,根據(jù)調(diào)用鏈來調(diào)節(jié)各個(gè)微服務(wù)的實(shí)例數(shù)量從而縮短微服務(wù)系統(tǒng)整體的平均響應(yīng)時(shí)間,不需要人工為每個(gè)微服務(wù)配置伸縮方案,降低了配置的復(fù)雜度。
文獻(xiàn)[13]提出了一個(gè)基于MAPE環(huán)路的自動(dòng)伸縮方法,但該方法僅關(guān)注了CPU資源利用率,且每輪調(diào)節(jié)只調(diào)節(jié)一個(gè)節(jié)點(diǎn),這導(dǎo)致在請(qǐng)求數(shù)量快速變化的場景下其調(diào)節(jié)速度較慢。本文提出的部署優(yōu)化方法除關(guān)注CPU資源利用率外,還關(guān)注內(nèi)存的使用情況,且每輪調(diào)節(jié)會(huì)根據(jù)調(diào)用鏈選擇出一組微服務(wù)進(jìn)行調(diào)節(jié)。當(dāng)請(qǐng)求數(shù)量快速變化時(shí),本文提出的部署優(yōu)化方法能快速調(diào)節(jié)微服務(wù)實(shí)例數(shù)量。
微服務(wù)系統(tǒng)屬于分布式系統(tǒng),因此可將基于Dapper開發(fā)的面向分布式系統(tǒng)的開源工具Spring Cloud Sleuth和Zipkin,用于研究微服務(wù)系統(tǒng)的行為。其中Spring Cloud Sleuth負(fù)責(zé)指導(dǎo)微服務(wù)產(chǎn)生trace信息,Zipkin負(fù)責(zé)收集trace信息并展示。
用戶對(duì)微服務(wù)系統(tǒng)的一次請(qǐng)求通常需要大量的微服務(wù)調(diào)用進(jìn)行處理。Dapper將針對(duì)一個(gè)特定請(qǐng)求的全部后臺(tái)調(diào)用定義為一個(gè)trace,將微服務(wù)實(shí)例間的一次遠(yuǎn)程過程調(diào)用定義為一個(gè)span。Dapper為每個(gè)trace分配一個(gè)唯一的ID,稱為trace id,從而區(qū)分開了來自前臺(tái)的不同請(qǐng)求;Dapper為每個(gè)span分配一個(gè)唯一的ID,稱為span id,從而區(qū)分開了不同微服務(wù)實(shí)例之間的相互調(diào)用。除最外層span,每個(gè)span均有一個(gè)span作為自己的父親,并將父親span的span id作為parent id記錄在span中。圖1展示了一個(gè)簡單的微服務(wù)系統(tǒng)請(qǐng)求處理流程(圖中,六邊形節(jié)點(diǎn)代表了微服務(wù)系統(tǒng)中的不同微服務(wù)實(shí)例) ,rpc1對(duì)應(yīng)的span1,是rpc3對(duì)應(yīng)的span3的父親,也是rpc4對(duì)應(yīng)的span4的父親;而微服務(wù)實(shí)例C沒有再調(diào)用其他微服務(wù)實(shí)例,因此rpc2對(duì)應(yīng)的span2沒有孩子。通過span之間的父子關(guān)系,同一trace下的眾多span構(gòu)成了一顆樹,記錄了微服務(wù)系統(tǒng)不同實(shí)例之間的調(diào)用關(guān)系。
圖1 微服務(wù)系統(tǒng)請(qǐng)求處理路徑圖
Span除記錄trace id、span id和parent id外,還記錄了span的name(通常為被調(diào)用的接口)、遠(yuǎn)程過程調(diào)用的時(shí)間節(jié)點(diǎn)、被調(diào)用方的接口、IP等眾多信息,為研究微服務(wù)系統(tǒng)的行為奠定了基礎(chǔ)。
為實(shí)現(xiàn)本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法,本文在開源系統(tǒng)Spring Cloud Sleuth和Zipkin的基礎(chǔ)上,實(shí)現(xiàn)了一個(gè)面向微服務(wù)系統(tǒng)的運(yùn)行時(shí)可視化監(jiān)控工具。該工具從微服務(wù)可用性、微服務(wù)調(diào)用鏈及部署情況三個(gè)維度監(jiān)控微服務(wù)系統(tǒng)的運(yùn)行狀態(tài),并將結(jié)果可視化。
通過分析trace信息可以得到微服務(wù)可用性信息,微服務(wù)可用性信息包括每秒鐘請(qǐng)求數(shù)量QPS(Query Per Second)、平均響應(yīng)時(shí)間、最短響應(yīng)時(shí)間、最長響應(yīng)時(shí)間和錯(cuò)誤率(請(qǐng)求錯(cuò)誤數(shù)量與請(qǐng)求總數(shù)量的比值)等信息。微服務(wù)系統(tǒng)的大量微服務(wù)在短時(shí)間內(nèi)可產(chǎn)生龐大的trace數(shù)據(jù)。例如,將Train_ticket部署在1臺(tái)服務(wù)器上,每個(gè)微服務(wù)擁有1個(gè)實(shí)例,以每秒50個(gè)請(qǐng)求的速度發(fā)送查票請(qǐng)求,持續(xù)一小時(shí)可以收集32.6 GB的trace數(shù)據(jù),數(shù)據(jù)量巨大。當(dāng)用戶查詢微服務(wù)可用性信息時(shí),如果直接從數(shù)據(jù)庫中取得數(shù)據(jù)進(jìn)行統(tǒng)計(jì),由于數(shù)據(jù)量巨大,往往需要很長的時(shí)間分析數(shù)據(jù)。為了提升系統(tǒng)性能,本文采用MapReduce編程模型[14]對(duì)trace數(shù)據(jù)進(jìn)行預(yù)處理。
MapReduce是一種用于處理大規(guī)模數(shù)據(jù)集的編程模型,該模型首先對(duì)數(shù)據(jù)進(jìn)行映射(Map),然后對(duì)映射后的數(shù)據(jù)進(jìn)行歸約(Reduce),最終獲得需要的結(jié)果。本文通過定時(shí)任務(wù),每分鐘統(tǒng)計(jì)一次微服務(wù)可用性信息。統(tǒng)計(jì)微服務(wù)可用性信息時(shí)先將span映射為統(tǒng)計(jì)微服務(wù)可用性的avaCount,再將具有相同微服務(wù)名稱、微服務(wù)實(shí)例IP地址和接口名稱的avaCount歸約,最終將歸約后得到的avaCount轉(zhuǎn)換為微服務(wù)可用性信息(即中間結(jié)果)。avaCount中包含如下信息:微服務(wù)名稱、微服務(wù)實(shí)例IP地址、接口名稱、函數(shù)名稱、查詢數(shù)量、總響應(yīng)時(shí)間、錯(cuò)誤數(shù)量、最短響應(yīng)時(shí)間和最長響應(yīng)時(shí)間。將預(yù)處理獲得的中間結(jié)果存儲(chǔ)在數(shù)據(jù)庫中。當(dāng)用戶查詢微服務(wù)可用性信息時(shí),只需分析數(shù)據(jù)庫中的中間結(jié)果即可獲得微服務(wù)可用性信息。
映射過程如圖2所示。將span與avaCount中邊框類型相同的項(xiàng)目進(jìn)行映射。將span的名稱映射為avaCount中的接口名稱。將span的持續(xù)時(shí)間分別映射為avaCount的最短響應(yīng)時(shí)間、最長響應(yīng)時(shí)間、總響應(yīng)時(shí)間。Span的類型有CLIENT、SERVER、PRODUCER、CONSUMER四種。其中CLIENT對(duì)應(yīng)同步或異步調(diào)用中的調(diào)用方,SERVER對(duì)應(yīng)同步或者異步調(diào)用中的被調(diào)用方;PRODUCER對(duì)應(yīng)消息隊(duì)列中的生產(chǎn)方,CONSUMER對(duì)應(yīng)消息隊(duì)列中的消費(fèi)方。當(dāng)span的類型為SERVER或者CONSUMER時(shí),span的localEndpoint中記錄了被調(diào)用微服務(wù)的信息,serviceName為微服務(wù)名稱,Ipv4為微服務(wù)實(shí)例IP地址,將以上兩項(xiàng)分別映射為avaCount中的微服務(wù)名稱和微服務(wù)實(shí)例IP地址。Span中的tags記錄了一些額外信息。如圖2中mvc.controller.method為微服務(wù)被調(diào)用的函數(shù)名稱,將其映射為avaCount中的函數(shù)名稱。當(dāng)被調(diào)用的微服務(wù)響應(yīng)出現(xiàn)錯(cuò)誤時(shí),tags中將包含error,此時(shí)avaCount中的錯(cuò)誤數(shù)量應(yīng)置為1。因1個(gè)span對(duì)應(yīng)1個(gè)請(qǐng)求,avaCount中的查詢數(shù)量應(yīng)置為1。
圖2 span映射為avaCount示意圖
歸約過程如圖3所示。avaCount1和avaCount2的微服務(wù)名稱均為ts-route-service、微服務(wù)實(shí)例IP地址均為10.0.0.12、函數(shù)名稱均為querybyid,因此可以將avaCount1和avaCount2歸約為reduced avaCount。接口名稱中可能包含參數(shù),因此取httpPath1和httpPath2的相同部分作為歸約后的httpPath。查詢數(shù)量、總響應(yīng)時(shí)間和錯(cuò)誤數(shù)量均采用相加的方式歸約。最小響應(yīng)時(shí)間取minResponseTime1和minResponseTime2中較小者。最大響應(yīng)時(shí)間取maxResponseTime1和maxResposneTime2中較大者。
圖3 avaCount歸約示意圖
圖4給出了一個(gè)基于Train_ticket收集的微服務(wù)可用性信息表格,表格共八列,從左到右依次為:微服務(wù)名稱、微服務(wù)實(shí)例ID(通常為IP地址)、調(diào)用接口名稱、查詢時(shí)間內(nèi)的QPS、查詢時(shí)間內(nèi)的平均響應(yīng)時(shí)間、查詢時(shí)間內(nèi)最短響應(yīng)時(shí)間、查詢時(shí)間內(nèi)最長響應(yīng)時(shí)間、查詢時(shí)間內(nèi)的錯(cuò)誤率。表格中第1~6行分別為微服務(wù)實(shí)例10.0.0.39的6個(gè)不同接口的可用性信息,該實(shí)例屬于微服務(wù)ts-order-service。
圖4 微服務(wù)可用性
圖5給出了一個(gè)Train_ticket中查票微服務(wù)的QPS隨時(shí)間動(dòng)態(tài)變化圖。橫軸為時(shí)間,縱軸為對(duì)應(yīng)時(shí)間一分鐘內(nèi)的平均QPS。圖6給出了一個(gè)Train_ticket中查票微服務(wù)的平均響應(yīng)時(shí)間隨時(shí)間變化圖。橫軸為時(shí)間,縱軸為對(duì)應(yīng)時(shí)間一分鐘內(nèi)的平均響應(yīng)時(shí)間。
圖5 QPS隨時(shí)間動(dòng)態(tài)變化圖
圖6 平均響應(yīng)時(shí)間隨時(shí)間動(dòng)態(tài)變化圖
本文基于Zipkin實(shí)現(xiàn)了微服務(wù)調(diào)用鏈的可視化。微服務(wù)調(diào)用鏈可視化展示了微服務(wù)間的調(diào)用關(guān)系,線條粗細(xì)表示了微服務(wù)間調(diào)用次數(shù)的多少,線條上標(biāo)注的數(shù)字表示了各微服務(wù)調(diào)用其余微服務(wù)的可能性。圖7給出了一個(gè)基于Train_ticket收集的微服務(wù)調(diào)用鏈的可視化界面,每個(gè)節(jié)點(diǎn)代表一個(gè)微服務(wù),節(jié)點(diǎn)上的字母代表微服務(wù)的名稱。例如:節(jié)點(diǎn)ts-travel-service代表微服務(wù)ts-travel-service,節(jié)點(diǎn)ts-ticketinfo-service代表微服務(wù)ts-ticketinfo-service。節(jié)點(diǎn)間的有向線段表示微服務(wù)間的遠(yuǎn)程調(diào)用關(guān)系,位于有向線段起點(diǎn)的微服務(wù)調(diào)用位于有向線段終點(diǎn)的微服務(wù)。有向線段的粗細(xì)表示調(diào)用次數(shù)的多少,越粗調(diào)用次數(shù)越多。線段上標(biāo)注了“數(shù)字1:數(shù)字2”,其中“數(shù)字1”代表查詢時(shí)間內(nèi)遠(yuǎn)程調(diào)用的次數(shù),“數(shù)字2”代表有向線段起點(diǎn)的微服務(wù)調(diào)用線段終點(diǎn)的微服務(wù)的概率。如ts-travel-service調(diào)用ts-ticketinfo-service共12次,ts-travel-service有0.400 0的概率調(diào)用ts-ticketinfo-service,且通過線段的粗細(xì)可以看出:ts-travel-service調(diào)用ts-ticketinfo-service和ts-route-service的次數(shù)較多、調(diào)用ts-seat-service和ts-train-service的次數(shù)較少。
圖7 微服務(wù)調(diào)用鏈可視化
本文實(shí)現(xiàn)的可視化監(jiān)控工具通過調(diào)用外部系統(tǒng)接口獲取微服務(wù)系統(tǒng)線上部署情況。Kubernetes和Docker Swarm等微服務(wù)管理工具通常提供了獲取微服務(wù)實(shí)例部署情況的接口。為使開發(fā)和運(yùn)維人員能更直觀地了解微服務(wù)系統(tǒng)的線上部署情況,可視化監(jiān)控工具通過3D圖形來表示微服務(wù)系統(tǒng)的在線部署情況。圖8給出了一個(gè)基于Train_ticket收集的部署情況的可視化界面,圖中每個(gè)小球代表一個(gè)微服務(wù)實(shí)例,顏色相同的小球?qū)儆谕粋€(gè)微服務(wù)。單擊小球可看到對(duì)應(yīng)微服務(wù)的名稱,如圖中的ts-food-map-service??梢悦黠@看到小球聚集成兩堆,聚集在一起的小球表示對(duì)應(yīng)微服務(wù)實(shí)例部署在同一臺(tái)服務(wù)器上,橫坐標(biāo)server2和server1則代表著對(duì)應(yīng)服務(wù)器的名稱。
圖8 微服務(wù)系統(tǒng)線上部署可視化
本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法,由監(jiān)控模塊、分析模塊、規(guī)劃模塊和執(zhí)行模塊四部分組成。監(jiān)控模塊負(fù)責(zé)監(jiān)控微服務(wù)系統(tǒng)中各個(gè)微服務(wù)的運(yùn)行情況及服務(wù)器的資源使用情況;分析模塊根據(jù)監(jiān)控模塊收集到的信息,分析各個(gè)微服務(wù)實(shí)例是否過載或空閑;規(guī)劃模塊根據(jù)微服務(wù)調(diào)用鏈、微服務(wù)實(shí)例資源使用情況、集群資源使用情況等信息,從分析模塊找出的過載和空閑的微服務(wù)實(shí)例中挑選出需要進(jìn)行動(dòng)態(tài)伸縮的一組微服務(wù),并生成最終的自動(dòng)伸縮方案;執(zhí)行模塊根據(jù)規(guī)劃模塊生成的自動(dòng)伸縮方案,通過調(diào)用外部系統(tǒng)接口實(shí)現(xiàn)動(dòng)態(tài)伸縮微服務(wù)。
基于所實(shí)現(xiàn)的面向微服務(wù)系統(tǒng)的運(yùn)行時(shí)可視化監(jiān)控工具,本文實(shí)現(xiàn)了一個(gè)運(yùn)行時(shí)部署優(yōu)化工具。本文將微服務(wù)部署在Docker容器中,并由Docker Swarm管理微服務(wù)集群。Docker Swarm提供了spread、binpack、random三種容器放置策略[15]。Spread策略將容器盡可能平均地置于集群中的服務(wù)器上;binpack策略將容器盡可能地置于已放置了容器的服務(wù)器上,以便將空余服務(wù)器用于放置未來較大的容器;random策略隨機(jī)選擇服務(wù)器放置容器上。本文采用spread策略,使容器盡可能平均地分布在服務(wù)器上。
監(jiān)控模塊主要監(jiān)控以下三項(xiàng)信息:trace信息、服務(wù)器上CPU的平均利用率和內(nèi)存的平均使用情況、各個(gè)微服務(wù)實(shí)例使用服務(wù)器CPU百分比。
本文實(shí)現(xiàn)的部署優(yōu)化工具中,監(jiān)控模塊通過可視化監(jiān)控工具收集對(duì)應(yīng)時(shí)間內(nèi)的trace信息;通過定時(shí)采樣的方式,收集對(duì)應(yīng)時(shí)間內(nèi)服務(wù)器上CPU平均利用率和內(nèi)存平均使用情況;通過Docker Swarm提供的Docker Stats指令[16],以定時(shí)采樣的方式收集對(duì)應(yīng)時(shí)間內(nèi)各個(gè)微服務(wù)實(shí)例使用服務(wù)器CPU百分比。
分析模塊通過分析監(jiān)控模塊收集到的各個(gè)微服務(wù)實(shí)例使用服務(wù)器CPU百分比,判斷各個(gè)微服務(wù)實(shí)例的運(yùn)行狀態(tài),進(jìn)而判斷各個(gè)微服務(wù)是否獲得了足夠的資源。
將各個(gè)微服務(wù)實(shí)例使用服務(wù)器CPU百分比超過上限的次數(shù)稱為upper、低于下限的次數(shù)稱為lower。若某個(gè)微服務(wù)實(shí)例的upper高于閾值,則認(rèn)為該微服務(wù)實(shí)例過載;若某個(gè)微服務(wù)實(shí)例的lower高于閾值,則認(rèn)為該微服務(wù)實(shí)例空閑。若某個(gè)微服務(wù)的全部實(shí)例過載,則認(rèn)為該微服務(wù)處于資源過少的狀態(tài);若某個(gè)微服務(wù)的部分或者全部實(shí)例空閑,則認(rèn)為該微服務(wù)處于資源過多的狀態(tài)。
在判斷微服務(wù)運(yùn)行狀態(tài)時(shí),本文只通過微服務(wù)實(shí)例使用服務(wù)器CPU百分比來判斷,而沒有考慮請(qǐng)求數(shù)量。因?yàn)檎?qǐng)求數(shù)量會(huì)直接表現(xiàn)在CPU利用率上,通過觀察CPU利用率便可知請(qǐng)求數(shù)量的多少。如果CPU利用率過高,說明該微服務(wù)實(shí)例正在處理數(shù)量較多的請(qǐng)求;如果CPU利用率過低,則說明該微服務(wù)實(shí)例正在處理數(shù)量較少的請(qǐng)求。
本文根據(jù)調(diào)用鏈和微服務(wù)實(shí)例資源使用情況,每次挑選一條調(diào)用鏈上需要添加實(shí)例的微服務(wù)及全部微服務(wù)中資源利用率過低需要減少實(shí)例的微服務(wù)進(jìn)行動(dòng)態(tài)伸縮。在挑選需要減少實(shí)例的微服務(wù)時(shí),本文采取的方法類似于Kubernetes,將該輪MAPE循環(huán)中資源利用率過低的微服務(wù)全部減少一個(gè)實(shí)例,而不是從一條調(diào)用鏈上挑選。因?yàn)橥ㄟ^實(shí)驗(yàn)發(fā)現(xiàn),在集群資源固定且有限的場景下,能更快地減少實(shí)例就意味著能更快地為需要資源的微服務(wù)空出啟動(dòng)新實(shí)例所需的資源。
通過分析監(jiān)控模塊收集到的trace信息,可以獲得對(duì)應(yīng)的調(diào)用鏈信息。用戶對(duì)微服務(wù)系統(tǒng)的一次請(qǐng)求,通常需要大量的微服務(wù)調(diào)用來進(jìn)行處理。例如,網(wǎng)站amazon.com構(gòu)建每個(gè)頁面平均需要調(diào)用100~150個(gè)微服務(wù)[17]。被請(qǐng)求直接調(diào)用的最外層接口,我們稱之為調(diào)用鏈的名稱。調(diào)用鏈中包括了調(diào)用鏈的名稱及所有被調(diào)用微服務(wù)的名稱組成的列表。
獲得了調(diào)用鏈信息后,就可以根據(jù)調(diào)用鏈信息和分析模塊獲得的微服務(wù)實(shí)例資源使用率超過上限的次數(shù),判斷該為哪條調(diào)用鏈上缺少資源的微服務(wù)添加實(shí)例;根據(jù)調(diào)用鏈信息和微服務(wù)實(shí)例資源使用率低于下限的次數(shù),判斷該為哪些微服務(wù)減少實(shí)例。算法1給出了基于調(diào)用鏈的微服務(wù)選取算法。
算法1基于調(diào)用鏈的微服務(wù)選取算法
01:輸入:監(jiān)控模塊采樣次數(shù)n,微服務(wù)實(shí)例超過資源使用率上限的次數(shù)microserviceId2Upper,微服務(wù)實(shí)例資源使用率低于下限的次數(shù)microserviceId2Lower,微服務(wù)實(shí)例id列表microserviceIdList
02:輸出:需要添加實(shí)例的微服務(wù)列表increaseList,需要減少實(shí)例的微服務(wù)列表decreaseList
03:threshold ← n/2,微服務(wù)實(shí)例資源使用率超過上限次數(shù)大于threshold即為過載,微服務(wù)的全部實(shí)例均過載則該微服務(wù)過載
04:overloadList初始化,過載微服務(wù)組成的列表
05:increaseList初始化
06:decreaseList初始化
07:for id in microserviceId2Upper.keySet()
08: if microserviceId2Upper.get(id) > threshold then
09: name ← toServiceName(id)
10: find ← false
11: for id2 in microserviceId2Upper.keySet()
12: if !id2.equals(id) &&
name.equals(toServiceName(id2)) &&
microserviceId2Upper.get(id2) < threshold then
13: find ← true
14: end if
15: end for
16: if !find then
17: overloadList.add(toServiceName(id))
18: end if
19: end if
20:end for
21:callChainOverload ← 挑選出包含缺少資源的微服務(wù)數(shù)量最多的一條調(diào)用鏈
22: for microservice in callChainOverload
23: if overloadList.contains(microservice)
24: increaseList.add(microservice)
25: end if
26:end for
27:for id in microserviceId2Lower.keySet()
28: if microserviceId2Lower.get(id) > threshold then
29: name ← toServiceName(id)
30: find ← false
31: for id2 in microserviceId2Lower.keySet()
32: if !id2.equals(id) &&
name.equals(toServiceName(id2)) then
33: find ← true
34: end if
35: end for
36: if find then
37: decreaseList.add(name)
38: end if
39:end if
40:end for
算法1第3行,計(jì)算threshold。第4~6行負(fù)責(zé)初始化。第7~20行,針對(duì)每個(gè)微服務(wù)的實(shí)例判斷所屬微服務(wù)是否過載。第8行,判斷該實(shí)例是否過載。第9行,獲取該實(shí)例對(duì)應(yīng)的微服務(wù)名稱。第10~18行,查找是否存在屬于同一微服務(wù)的未過載的不同實(shí)例。若存在,說明這個(gè)實(shí)例過載是負(fù)載不均衡導(dǎo)致的,不應(yīng)該通過加實(shí)例的方式解決該實(shí)例過載的問題;若不存在,說明該微服務(wù)全部實(shí)例過載,則此微服務(wù)應(yīng)該增加實(shí)例,將其添加到過載微服務(wù)列表overloadList中。第21行,針對(duì)調(diào)用鏈緩存中的每條調(diào)用鏈,分別計(jì)算該調(diào)用鏈上需要添加實(shí)例的微服務(wù)數(shù)量,選擇數(shù)量最多的一條調(diào)用鏈。第22~26行,將該調(diào)用鏈上需要添加實(shí)例的微服務(wù)添加到increaseList中。第27~40行,針對(duì)每個(gè)微服務(wù)實(shí)例判斷是否應(yīng)該將對(duì)應(yīng)的微服務(wù)加入decreaseList中。第28行,判斷該微服務(wù)實(shí)例是否空閑。第29行,根據(jù)微服務(wù)實(shí)例id獲得微服務(wù)名稱。30~35行,判斷是否存在屬于同一微服務(wù)的不同實(shí)例,若存在find為true,不存在find為false。第36~38行,將實(shí)例數(shù)量大于1的微服務(wù)加入decreaseList中。
獲得了需要增加實(shí)例的微服務(wù)列表和需要減少實(shí)例的微服務(wù)列表后,根據(jù)集群資源使用情況,綜合判斷生成最終的部署調(diào)節(jié)方案。算法2給出了部署調(diào)節(jié)方案生成算法。
算法2部署調(diào)節(jié)方案生成算法
01:輸入:集群資源利用情況serverResources,服務(wù)器CPU利用上限cpu_threshold,服務(wù)器內(nèi)存利用上限memory_threshold、需要添加實(shí)例的微服務(wù)列表increaseList、需要減少實(shí)例的微服務(wù)列表decreaseList
02:輸出:最終的部署調(diào)節(jié)方案changes
03:enough ← false
04:for serverResource in serverResources
05: if serverResource.CPU < cpu_threshold &&
serverResource.memeory < memory_threshold then
06: enough ← true
07:end if
08:end for
09:if enough then
10: if increaseList !=null then
11: changes.add(increaseList)
12: end if
13:else
14: if decreaseList !=null then
15: changes.add(decreaseList)
16: if increaseList !=null then
17: if increaseList.size() > decreaseList.size() then
18: list ← 從 increaseList中隨機(jī)選擇m個(gè)微服務(wù),其中m=decreaseList.size()
19: changes.add(list)
20: end if
21: else
22: changes.add(increaseList)
23: end if
24:end if
25:end if
算法2第3行負(fù)責(zé)初始化。第4行,針對(duì)每臺(tái)服務(wù)器的資源使用情況進(jìn)行判斷。第5行,如服務(wù)器CPU和內(nèi)存的使用率分別低于cpu_threshold和memory_threshold,則認(rèn)為該服務(wù)器資源充足。只要存在一臺(tái)服務(wù)器資源充足,即認(rèn)為整個(gè)集群資源充足。第9~12行,若集群資源充足,則僅僅為需要添加實(shí)例的微服務(wù)各添加一個(gè)實(shí)例。原因在于,在集群資源固定且有空余時(shí),不需要通過減少空閑微服務(wù)實(shí)例的方式來降低資源消耗。此外,這種做法還可以應(yīng)對(duì)請(qǐng)求突增的情況。第13~25行,對(duì)應(yīng)集群資源不足的情況。第14~24行,對(duì)應(yīng)decreaseList不為空的情況,即存在微服務(wù)可以減少實(shí)例。第15行,將這些微服務(wù)加入changes中,為這些微服務(wù)各減少一個(gè)實(shí)例。第16行,若increaseList不為空,即存在微服務(wù)可以增加實(shí)例。第17~20行,若increaseList中微服務(wù)個(gè)數(shù)大于decreaseList中微服務(wù)個(gè)數(shù),則從increaseList中隨機(jī)選擇m個(gè)微服務(wù)加入changes中,為這些微服務(wù)各添加一個(gè)實(shí)例,其中m為decreaseList中微服務(wù)的個(gè)數(shù)。因此時(shí)集群資源不足,當(dāng)減少m個(gè)實(shí)例后,粗略認(rèn)為可以增加m個(gè)實(shí)例到集群中。第21~23行,若increaseList中微服務(wù)個(gè)數(shù)小于等于decreaseList中微服務(wù)個(gè)數(shù),則將increaseList中微服務(wù)全部加入changes中,為這些微服務(wù)各添加一個(gè)實(shí)例。但如果集群資源不足,且沒有微服務(wù)可以減少實(shí)例時(shí),不添加也不減少任何實(shí)例。
執(zhí)行模塊根據(jù)規(guī)劃模塊生成的自動(dòng)伸縮方案,通過調(diào)用外部系統(tǒng)提供的接口來增加或減少某些微服務(wù)的實(shí)例數(shù)量,從而實(shí)現(xiàn)動(dòng)態(tài)地伸縮微服務(wù)。Kubernetes、Docker Swarm等當(dāng)下流行的微服務(wù)管理工具均提供了動(dòng)態(tài)調(diào)節(jié)微服務(wù)實(shí)例數(shù)量的功能。本文實(shí)現(xiàn)的部署優(yōu)化工具中,執(zhí)行模塊通過遠(yuǎn)程調(diào)用Docker Swarm提供的伸縮微服務(wù)的Docker update指令[18]來添加或刪除微服務(wù)實(shí)例。
為了驗(yàn)證本文所提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化工具的有效性,本文進(jìn)行了仿真實(shí)驗(yàn)。本文使用Train_ticket作為微服務(wù)系統(tǒng),將其和本文開發(fā)的部署優(yōu)化工具共同部署,從而進(jìn)行仿真實(shí)驗(yàn)。
服務(wù)器為四臺(tái)虛擬機(jī)(操作系統(tǒng)Centos 7、8 * Intel Xeon E5649@2.53 GHz CPU、24 GB內(nèi)存、50 GB硬盤、10 M/s帶寬)。微服務(wù)系統(tǒng)和部署優(yōu)化工具部署在兩臺(tái)服務(wù)器上,另外兩臺(tái)服務(wù)器部署壓力測試工具Jmeter。微服務(wù)系統(tǒng)部署在Docker Swarm集群中,每個(gè)微服務(wù)部署兩個(gè)實(shí)例,每個(gè)實(shí)例部署在一個(gè)Docker容器中,且一個(gè)Docker容器中只部署一個(gè)實(shí)例。
使用Jmeter模擬400個(gè)用戶的并發(fā)訪問,請(qǐng)求分別為登錄和查票。如表1所示,登錄對(duì)應(yīng)調(diào)用鏈包含3個(gè)微服務(wù),查票對(duì)應(yīng)調(diào)用鏈包含10個(gè)微服務(wù),且兩條調(diào)用鏈包含的微服務(wù)沒有交集。請(qǐng)求到達(dá)速度如圖9所示,持續(xù)時(shí)間為35分鐘。
表1 登錄和查票對(duì)應(yīng)調(diào)用鏈包含微服務(wù)列表
圖9 請(qǐng)求到達(dá)速度圖
登錄和查票的平均響應(yīng)時(shí)間隨時(shí)間變化圖像如圖10所示。前2分鐘登錄和查票的平均響應(yīng)時(shí)間均較長,這與訂票系統(tǒng)Train_ticket本身相關(guān)。在使用Train_ticket過程中發(fā)現(xiàn),系統(tǒng)初啟動(dòng)時(shí)明顯慢于啟動(dòng)一段時(shí)間之后。查看部署優(yōu)化工具的日志發(fā)現(xiàn),第4分鐘由于集群資源不足,觸發(fā)了擴(kuò)縮容操作,共有31個(gè)微服務(wù)縮容、4個(gè)微服務(wù)擴(kuò)容。登錄相關(guān)的3個(gè)微服務(wù)保持不變,查票相關(guān)的4個(gè)微服務(wù)執(zhí)行了擴(kuò)容操作、5個(gè)微服務(wù)執(zhí)行了縮容操作、1個(gè)微服務(wù)保持不變,使得查票的平均響應(yīng)時(shí)間在第4~7分鐘變短。
圖10 平均響應(yīng)時(shí)間變化圖
從第5分鐘開始,查票請(qǐng)求數(shù)量逐步增加,登錄請(qǐng)求數(shù)量逐步減少。第7分鐘處,查票相關(guān)的2個(gè)微服務(wù)執(zhí)行了擴(kuò)容操作,使查票平均響應(yīng)時(shí)間在第7~8分鐘略微降低。
第8~15分鐘,查票平均響應(yīng)時(shí)間隨著請(qǐng)求數(shù)量遞增,在整體上呈變長的趨勢。但在第11分鐘處,查票相關(guān)的7個(gè)微服務(wù)執(zhí)行了擴(kuò)容操作,使得平均響應(yīng)時(shí)間明顯縮短。
第16分鐘處,集群資源不足導(dǎo)致同時(shí)觸發(fā)了縮容和擴(kuò)容操作。登錄相關(guān)的3個(gè)微服務(wù)縮容,查票相關(guān)的微服務(wù)中5個(gè)需要添加實(shí)例。但由于此時(shí)集群資源不足,且需要執(zhí)行擴(kuò)容操作的微服務(wù)數(shù)量多于執(zhí)行縮容操作的微服務(wù)數(shù)量。因此從需要擴(kuò)容的5個(gè)微服務(wù)中隨機(jī)挑選了3個(gè)進(jìn)行擴(kuò)容,分別為ts-seat-service、ts-route-service和ts-station-service。擴(kuò)容操作在帶來查票平均響應(yīng)時(shí)間縮短的同時(shí),縮容操作也使得登錄平均響應(yīng)時(shí)間在短時(shí)間內(nèi)急劇變長。隨后登錄平均響應(yīng)時(shí)間降至正常水平,這與登錄的請(qǐng)求數(shù)量減少有關(guān)。登錄相關(guān)的3個(gè)微服務(wù)初始部署時(shí)各有2個(gè)實(shí)例,可正常響應(yīng)100 req/s(request/s,每秒鐘請(qǐng)求數(shù)量)的請(qǐng)求到達(dá)速度而不觸發(fā)擴(kuò)容操作。16分鐘后,雖然登錄相關(guān)的3個(gè)微服務(wù)實(shí)例數(shù)量均變成了1個(gè),但登錄的請(qǐng)求到達(dá)速度已變?yōu)?0 req/s。
查票的請(qǐng)求到達(dá)速度在15~20分鐘一直維持在50 req/s。從21分鐘開始,查票的請(qǐng)求到達(dá)速度開始下降。而在21分鐘時(shí),恰好因?yàn)?0分鐘時(shí)較高請(qǐng)求到達(dá)速度執(zhí)行了擴(kuò)容操作,使得第21和22分鐘平均響應(yīng)時(shí)間在擴(kuò)容和請(qǐng)求數(shù)量變少的雙重作用下顯著縮短。
第27分鐘處,查票相關(guān)的6個(gè)微服務(wù)觸發(fā)了縮容操作,導(dǎo)致平均響應(yīng)時(shí)間短暫變長,而后又隨請(qǐng)求數(shù)量的降低而縮短。
綜合以上分析,本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化工具,在集群資源固定且有限的場景下,能有效地調(diào)節(jié)微服務(wù)的實(shí)例數(shù)量、降低系統(tǒng)整體響應(yīng)時(shí)間。但同時(shí)也發(fā)現(xiàn)了一些問題,第15~20分鐘,查票請(qǐng)求到達(dá)速度維持在50 req/s,此時(shí)為相應(yīng)微服務(wù)執(zhí)行擴(kuò)容操作,雖能夠降低平均響應(yīng)時(shí)間,但仍然無法將平均響應(yīng)時(shí)間縮短至第5~9分鐘的水平。經(jīng)分析Train_ticket發(fā)現(xiàn),存在單個(gè)微服務(wù)的多個(gè)實(shí)例使用同一數(shù)據(jù)庫服務(wù)的情況,如ts-station-service 的四個(gè)實(shí)例均使用ts-station-mongo。這使得系統(tǒng)的瓶頸轉(zhuǎn)移至數(shù)據(jù)庫處,此時(shí)再為ts-station-service增加實(shí)例,不能有效降低平均響應(yīng)時(shí)間。另外,從第10~13分鐘可以看出,本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法有一定滯后性,平均響應(yīng)時(shí)間隨請(qǐng)求數(shù)量增長一小段時(shí)間后,才會(huì)觸發(fā)自適應(yīng)調(diào)節(jié)操作。
本文提出一個(gè)基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法,在開源系統(tǒng)Spring Cloud Sleuth和Zipkin的基礎(chǔ)上,實(shí)現(xiàn)了面向微服務(wù)系統(tǒng)的運(yùn)行時(shí)監(jiān)控和部署優(yōu)化工具,并通過實(shí)驗(yàn)驗(yàn)證了部署優(yōu)化方法的有效性。在集群資源固定且有限的場景下,部署優(yōu)化方法能有效調(diào)節(jié)微服務(wù)實(shí)例數(shù)量、降低系統(tǒng)整體響應(yīng)時(shí)間。
本文提出的基于MAPE環(huán)路的自適應(yīng)部署優(yōu)化方法還存在一些不足。不同微服務(wù)實(shí)例對(duì)服務(wù)器資源需求不同,當(dāng)集群資源不足時(shí)需要先減少一些實(shí)例再啟動(dòng)相同數(shù)量的實(shí)例,但這是建立在不同微服務(wù)實(shí)例需要資源相當(dāng)?shù)幕A(chǔ)上的。在一定程度上,為微服務(wù)添加實(shí)例可降低平均響應(yīng)時(shí)間,但當(dāng)實(shí)例數(shù)量到達(dá)一定程度時(shí),系統(tǒng)的性能瓶頸可能轉(zhuǎn)移到數(shù)據(jù)庫等部分,此時(shí)繼續(xù)添加實(shí)例無法有效降低平均響應(yīng)時(shí)間。通過實(shí)驗(yàn)發(fā)現(xiàn),自適應(yīng)調(diào)節(jié)仍有一定的滯后性。