蘇寧大數(shù)據(jù)平臺(tái)的計(jì)算引擎主要包括三個(gè)組成部分:離線計(jì)算、流式計(jì)算、OLAP引擎。
在我們整個(gè)架構(gòu)中,Spark處于一個(gè)非常重要的位置。同時(shí)我們也為了Spark的平臺(tái)化服務(wù)化,做了很多平臺(tái)級(jí)工具。
這個(gè)是CBT任務(wù)流調(diào)度平臺(tái)。它針對目前包括Spark、Spark SQL、數(shù)據(jù)交換在內(nèi)多種類型任務(wù)提供一個(gè)任務(wù)和任務(wù)流管理以及調(diào)度的能力。目前我們CBT平臺(tái)集群規(guī)模在98臺(tái)虛擬機(jī),每天完成5W+任務(wù)的調(diào)度和執(zhí)行。
這是SSMP平臺(tái)。專門針對Spark Streaming任務(wù)提供的一個(gè)任務(wù)管理和調(diào)度的平臺(tái),為任務(wù)提供24小時(shí)LongRunning的保障。
這是我們在線機(jī)器學(xué)習(xí)平臺(tái)。目前該平臺(tái)主要是基于Spark MLlib實(shí)現(xiàn)的,對GPU環(huán)境下深度學(xué)習(xí)算法的支持我們也正在開發(fā)。目前我們支持業(yè)務(wù)在線的進(jìn)行Pipeline構(gòu)建、模型訓(xùn)練、調(diào)優(yōu),并且支持對訓(xùn)練后的模型一鍵發(fā)布到Spark Streaming應(yīng)用環(huán)境。
這是我們離線大集群的相關(guān)數(shù)據(jù)。目前我們離線這塊集群節(jié)點(diǎn)數(shù)為700多個(gè),每天通過CBT調(diào)度任務(wù)大概在5W+,每天處理的數(shù)據(jù)量在300T左右。
上面表格是我們2016年Q4中期以及近統(tǒng)計(jì)的《Spark和Hive任務(wù)分布情況》。當(dāng)前我們蘇寧業(yè)務(wù)對Hive的依賴還是比較重,對Hive遷移到Spark SQL的工作我們也在逐步推進(jìn)。另外我們單看Spark 任務(wù)變化情況:在這半年時(shí)間里,Spark任務(wù)數(shù)增速非常快,Spark任務(wù)新增3000+,Spark Streaming任務(wù)從0增長到29個(gè)。這里要強(qiáng)調(diào)一下,目前這3000個(gè)Spark任務(wù)里面,只有少少的200個(gè)任務(wù)是Spark SQL任務(wù),在我們后續(xù)Hive遷移過程,Spark SQL任務(wù)數(shù)增速是會(huì)遠(yuǎn)遠(yuǎn)超過當(dāng)前這個(gè)數(shù)字。
整體上來說,通過我們平臺(tái)化以及服務(wù)化工作的開展,我們業(yè)務(wù)已經(jīng)接受Spark作為它們數(shù)據(jù)分析鏈路上一個(gè)核心引擎。
但是在我們整個(gè)平臺(tái)化和服務(wù)化的過程中,也遇到很多很多的問題。這些問題一部分是因?yàn)闃I(yè)務(wù)自身對Spark理解和應(yīng)用經(jīng)驗(yàn)不夠,還有一部分是因?yàn)槲覀兎?wù)化做的不夠好。
在業(yè)務(wù)推廣中,一般情況下業(yè)務(wù)遇到性能問題和故障時(shí),都是直接反饋到平臺(tái)組這邊,由我們平臺(tái)配合業(yè)務(wù)去定位和解決這些問題。
我們平臺(tái)解決這些問題的思路:利用經(jīng)驗(yàn)對任務(wù)執(zhí)行過程和日志進(jìn)行分析,盡大可能去收集有效數(shù)據(jù)(但由于任務(wù)已經(jīng)結(jié)束,一些運(yùn)行時(shí)的數(shù)據(jù)可能無法收集),并且利用這些數(shù)據(jù)來定位和解決問題。但是這整個(gè)工作的效率非常低,而且存在很多同質(zhì)化問題。
從服務(wù)化角度出發(fā),我們希望可以利用平臺(tái)化的思路去解決這些問題,因此我們就做了這個(gè)Spark自動(dòng)化分析和故障診斷系統(tǒng),內(nèi)部代號(hào)-華佗。
系統(tǒng)架構(gòu)主要包括數(shù)據(jù)采集、華佗server、數(shù)據(jù)存儲(chǔ)以及監(jiān)控分析平臺(tái)幾個(gè)模塊。數(shù)據(jù)采集目前主要采集了Spark,Yarn、宿主機(jī)器等數(shù)據(jù)。其中Spark這塊,我們擴(kuò)展了Spark的Metric System以及Event System,并通過新增MetricSource來收集我們需要的信息;HDFS和Yarn是通過JMX-Collect來收集Metric信息;宿主機(jī)器利用Service-Agent來收集機(jī)器的CPU,內(nèi)存,IO等Metric信息。數(shù)據(jù)通過華佗Server分別落地到Druid和ES兩個(gè)存儲(chǔ)中,其中Druid用來存儲(chǔ)指標(biāo)數(shù)據(jù),ES用來存儲(chǔ)事件數(shù)據(jù)。華佗監(jiān)控平臺(tái),通過這兩類數(shù)據(jù)來實(shí)現(xiàn)平臺(tái)的指標(biāo)分析,事件分析,故障診斷,異常報(bào)警以及任務(wù)報(bào)表等功能。
Druid是一種適用于時(shí)序化數(shù)據(jù)的OLAP分析引擎,特別適合于統(tǒng)計(jì)分析、系統(tǒng)監(jiān)控等業(yè)務(wù)場景。而我們這里場景就是系統(tǒng)監(jiān)控。
在Druid里面,數(shù)據(jù)是按照時(shí)間、維度、指標(biāo)三種元素進(jìn)行組織,支持TopN、GroupBy等聚合查詢以及簡單明細(xì)查詢。關(guān)于明細(xì)查詢這塊,Druid的索引可以實(shí)現(xiàn)快速記錄定位。但是相比ES,它的可控性要差點(diǎn),所以我們目前整體OLAP這塊是計(jì)劃使用Druid+ES組合來為業(yè)務(wù)提供服務(wù)。其中Druid目前是作為主要的OLAP引擎進(jìn)行推廣,支撐銷售報(bào)表、金融自助分析、風(fēng)控平臺(tái)以及平臺(tái)監(jiān)控等十多個(gè)業(yè)務(wù)場景。
下面我們具體看一下,我們系統(tǒng)針對Spark提供哪些分析和故障診斷的能力,主要是從資源、性能、故障三個(gè)角度出發(fā)。
首先看一下資源方面,我們對Spark的資源把控分為三個(gè)層面:1)站在Spark外部, 任務(wù)所使用的Yarn、HDFS以及宿主機(jī)器等外部資源的穩(wěn)定性;2)站在Spark Linux進(jìn)程本身,來分析任務(wù)進(jìn)程資源的利用率;3)站在Spark內(nèi)部,主要考慮Cache以及Shuffle的資源使用情況。
如果站在Spark服務(wù)使用角度來說:我們希望我們從Yarn上申請到的虛擬資源和實(shí)際運(yùn)行的物理資源是匹配的。實(shí)際運(yùn)行過程中,不應(yīng)該出現(xiàn)Driver和Executor的宿主機(jī)器存在性能瓶頸,比如系統(tǒng)負(fù)載過高,網(wǎng)卡打滿,甚至丟包。因?yàn)槲锢憝h(huán)境穩(wěn)定性對Spark App的穩(wěn)定性和性能是有非常大的影響的。
如果站在App進(jìn)程角度,可以通過分析Driver和Executor的Linux進(jìn)程是否存在瓶頸來發(fā)現(xiàn)App的性能和穩(wěn)定性情況,比如Executor CPU利用率是否達(dá)到100%,或者Executor的FD是否保持持續(xù)增長,是否存在句柄泄露。
另外,相比Hive,Spark On Yarn有三個(gè)重要參數(shù)需要設(shè)置:Executor個(gè)數(shù)和內(nèi)存,以及Driver內(nèi)存。特別是Executor個(gè)數(shù)及內(nèi)存,設(shè)置是否合理將很大程度上決定任務(wù)是否可以正常執(zhí)行,以及資源是否合理利用。
上面兩張PPT可以看出:Driver和Executor預(yù)分配內(nèi)存以及實(shí)際占用內(nèi)存的使用情況,以及Executor預(yù)分配的CPU時(shí)間片利用率情況,通過它們可以快速定位業(yè)務(wù)的資源利用率。
后是站在Spark內(nèi)部,來看Cache以及Shuffle資源使用情況。Spark 1.5.2版本中的Cache和Shuffle內(nèi)存還是分段管理,對分段比例參數(shù)的調(diào)優(yōu)是一件非常頭疼事情。因此我們針對Cache和Shuffle內(nèi)存做了圖表可視化分析,可以快速指導(dǎo)業(yè)務(wù)進(jìn)行參數(shù)調(diào)優(yōu)。
另外對Cache機(jī)制,Spark開發(fā)新手可能會(huì)存在誤解,有時(shí)直接對所有的RDD進(jìn)行Cache。但實(shí)際上只有RDD/Dataset使用兩次以上,才有必要進(jìn)行Cache。因此我們對DAG圖進(jìn)行分析,針對是否需要Cache給出建議。
對于性能,主要從兩個(gè)角度進(jìn)行分析:1)站在Task角度,對Task耗時(shí)鏈和長尾Task進(jìn)行分析;2)站在Stage角度,對任務(wù)調(diào)度Overhead以及并行度進(jìn)行分析。
對于Task耗時(shí),目前Spark頁面已經(jīng)提供了一些統(tǒng)計(jì),比如調(diào)度延遲,GC耗時(shí),反序列化耗時(shí),Shuffle耗時(shí)等。但是業(yè)務(wù)還需要了解更多的耗時(shí)情況,比如每一步操作的耗時(shí)情況。假如業(yè)務(wù)邏輯其中一個(gè)map操作需要與外部數(shù)據(jù)源進(jìn)行IO操作,那么對它的耗時(shí)統(tǒng)計(jì)會(huì)非常重要,因此我們做了耗時(shí)鏈的統(tǒng)計(jì)。
目前Task耗時(shí)鏈?zhǔn)腔赗DD-Itertor來實(shí)現(xiàn)的,對于Spark 2.0+引入的Whole Stage Code Genaretion目前我們還未支持。
在RDD-Itertor模型中,RDD Transfer操作就是Itertor的連接操作,每一個(gè)Itertor的next和hasnext就是耗時(shí)源頭,我們通過對Spark中的Itertor進(jìn)行二次封裝來收集每步耗時(shí)。另外有些情況下Itertor的next和hasnext不存在耗時(shí),或者很小,主要耗時(shí)集中在Itertor對象的構(gòu)造上,比如flatmap操作或者mappartition操作,先構(gòu)造一個(gè)List,然后再做一個(gè)toItertor操作。這種情況下,需要統(tǒng)計(jì)Itertor的構(gòu)造耗時(shí),但是Itertor構(gòu)造耗時(shí)涵蓋了Parent耗時(shí),統(tǒng)計(jì)時(shí)需要剔除Parent耗時(shí)情況。
長尾Task是Spark中非常常見的性能問題。長尾原因可能是業(yè)務(wù)數(shù)據(jù)傾斜,也有可能機(jī)器丟包,網(wǎng)卡CPU等資源存在瓶頸。目前我們做了長尾Task的報(bào)警和實(shí)時(shí)監(jiān)控,并結(jié)合耗時(shí)鏈分析、進(jìn)程和宿主機(jī)狀態(tài)分析,以及后面談到的數(shù)據(jù)傾斜來對長尾Task進(jìn)行分析。
任務(wù)調(diào)度Overhead是平臺(tái)比較傷感的問題,看著那些細(xì)碎任務(wù),幾十M數(shù)據(jù)用幾百Task去跑,每個(gè)Task只執(zhí)行幾十毫秒。因此我們對任務(wù)Stage進(jìn)行分析,統(tǒng)計(jì)任務(wù)實(shí)際計(jì)算時(shí)間與等待調(diào)度時(shí)間,從而判斷是否存在調(diào)度Overhead。
造成任務(wù)調(diào)度Overhead的一個(gè)原因就是Reduce個(gè)數(shù)設(shè)置不合理,而且這是一個(gè)滾雪球效應(yīng),Reduce放大原始數(shù)據(jù)分區(qū)數(shù),計(jì)算后寫回HDFS,造成HDFS小文件,然后再反復(fù)的迭代,產(chǎn)生更多小文件,從而導(dǎo)致更加嚴(yán)重的Overhead。
在Spark 2.0+版本,新增Reduce個(gè)數(shù)自動(dòng)適應(yīng)是一個(gè)非常棒的功能,很大程度上解決了這個(gè)問題,但是對于1.5.2版本,這個(gè)問題還是依然存在。因此我們對Reduce操作進(jìn)行分析,如上圖,全局大的Reduce操作數(shù)據(jù)量只有13M,使用默認(rèn)40并發(fā)是不合理,強(qiáng)烈建議業(yè)務(wù)優(yōu)化。
性能這塊還做了一些其他的優(yōu)化分析,比如JDBC并發(fā)度分析以及Kafka并發(fā)度分析。JDBC默認(rèn)的API是可以不設(shè)置分區(qū)和并發(fā)度,這樣單線程讀JDBC會(huì)導(dǎo)致任務(wù)耗時(shí)較長;對于Kafka Direct API,默認(rèn)是一個(gè)Spark分區(qū)讀取Kafka一個(gè)分區(qū),但是在很多業(yè)務(wù)場景下可能會(huì)成為瓶頸。
后就是故障診斷,其實(shí)前面分析的結(jié)果可以直接用于故障診斷,但我們針對一些常見故障,單獨(dú)提煉出來,從而可以更加直接發(fā)現(xiàn)問題,比如:Shuffle數(shù)據(jù)傾斜、HDFS Commit阻塞、執(zhí)行器丟失、高維Parquet寫性能阻塞等。
我們在Shuffle Write任務(wù)結(jié)束以后,提前對后續(xù)Read的數(shù)據(jù)量進(jìn)行計(jì)算,判斷后續(xù)的Shuffle Read操作是否存在傾斜,從而可以直接給業(yè)務(wù)一個(gè)結(jié)論:是否需要優(yōu)化業(yè)務(wù)邏輯或參數(shù)。
HDFS Commit阻塞是出現(xiàn)頻率比較高的故障。目前CBT任務(wù)調(diào)度平臺(tái)有一個(gè)很密集的任務(wù)執(zhí)行時(shí)間,大概是0點(diǎn)-7點(diǎn)。在這個(gè)時(shí)間段,HDFS性能顯著下降,大rpc延遲可能達(dá)幾百ms。
其次如MAPREDUCE-4815描述:HDFS Commit操作是在Driver中串行執(zhí)行,如果計(jì)算生成幾千個(gè)小文件,那么整體Commit耗時(shí)就會(huì)增加幾百秒,這是一個(gè)很大的性能損耗。
后就是資源報(bào)表,通過它與業(yè)務(wù)之間構(gòu)成一個(gè)Feed-Back機(jī)制,推進(jìn)業(yè)務(wù)主動(dòng)對App的邏輯以及配置進(jìn)行優(yōu)化。
對于Spark及其他組件平臺(tái)化服務(wù)化,將是一個(gè)持續(xù)經(jīng)驗(yàn)積累和優(yōu)化的過程,大家有好的想法歡迎討論和交流。
本站文章版權(quán)歸原作者及原出處所有 。內(nèi)容為作者個(gè)人觀點(diǎn), 并不代表本站贊同其觀點(diǎn)和對其真實(shí)性負(fù)責(zé),本站只提供參考并不構(gòu)成任何投資及應(yīng)用建議。本站是一個(gè)個(gè)人學(xué)習(xí)交流的平臺(tái),網(wǎng)站上部分文章為轉(zhuǎn)載,并不用于任何商業(yè)目的,我們已經(jīng)盡可能的對作者和來源進(jìn)行了通告,但是能力有限或疏忽,造成漏登,請及時(shí)聯(lián)系我們,我們將根據(jù)著作權(quán)人的要求,立即更正或者刪除有關(guān)內(nèi)容。本站擁有對此聲明的最終解釋權(quán)。