從大公司投身到創(chuàng)業(yè)型的小公司,我最深的感受就是“由奢入儉難”這五個字。以前公司里有完善的框架體系,涵蓋了分布式log、監(jiān)控、實時報警、大數(shù)據(jù)存儲等等方面,并且有成熟的團(tuán)隊來運營,使用者大部分時間只要做好集成就行;換到了小公司,初始的一長段時間內(nèi),技術(shù)團(tuán)隊只有3人,起步階段一窮二白,而且要做兩個體系的產(chǎn)品,每天業(yè)務(wù)的壓力就很大,做起事來只能用些比較粗糙的手段。業(yè)務(wù)的壓力和質(zhì)量的追求始終是個矛盾。然而,該有的絕不能少,所以我們還是盡量抽出一些時間做好部分必須的框架工作。在我們看來,監(jiān)控和報警框架是優(yōu)先級***的:
1.創(chuàng)業(yè)型公司在測試方面,無法做到非常充分,出現(xiàn)問題的概率比較大,需要做好監(jiān)控。
2.對一個復(fù)雜系統(tǒng)的把握,必然是大量的自動化的監(jiān)控、度量,時刻要知道系統(tǒng)里每個組件的各種運行指標(biāo)。實際上,有經(jīng)驗的工程師會體會到,做好監(jiān)控和運營,在難度和重要性上要遠(yuǎn)高于你寫的功能代碼。
3.人少,就要自動化程度高。只有做好監(jiān)控和自動化報警,才能抽出更多的精力忙業(yè)務(wù),晚上才能放心睡覺。
因此,想要做出可靠穩(wěn)定的產(chǎn)品,首先要有靠譜的監(jiān)控報警框架去做支撐。而對于像我們這樣的創(chuàng)業(yè)公司來說,還需要關(guān)心以下幾點:
1.有沒有成熟的開源產(chǎn)品。大公司可以花費一個團(tuán)隊專心做一件事情;而小公司每個人都是非常珍貴的資源,半個人的開銷都嫌大,所以會更多的借力于開源產(chǎn)品。
2.坑多不多。開源產(chǎn)品的質(zhì)量和支持沒有辦法和商業(yè)產(chǎn)品相比,所以我們需要選用可以hold住的,坑少且穩(wěn)定的產(chǎn)品使用。
3.能否支持跨語言。我們的產(chǎn)品基本上是C、Java、Python、JSON的混合產(chǎn)品,尤其是后端主要由Java和python組成。
4.可伸縮性是否足夠好。我們的業(yè)務(wù)和數(shù)據(jù)在快速發(fā)展,所以使用的產(chǎn)品必須能支持后期海量數(shù)據(jù)的涌入。
5.是否有一定的擴展性。使用過程中必然會有一些特殊的需求,如何快速的做些定制化也是需要考量的點。
6.能否同時支持單機和分布式的部署。我們情況比較特殊,既有傳統(tǒng)的私有化部署的軟件解決方案,又有公有的SaaS以及配套的大規(guī)模計算集群。因此,我們很多產(chǎn)品都要有高低配兩種實現(xiàn),同時通過配置來實現(xiàn)無縫切換。監(jiān)控系統(tǒng)也不例外。
極度重要,要求又多,資源還少,所以我們在監(jiān)控和報警方面還是花了一些心思。下面,我會詳細(xì)分享下我們所做的實踐探索。
先看監(jiān)控
首先要談監(jiān)控。監(jiān)控的要點就是通過定義多種metrics來輔助我們?nèi)チ私猱a(chǎn)品。從硬件到軟件,從LB到后端數(shù)據(jù)庫的實時運行狀況,幫助我們發(fā)現(xiàn)問題、故障甄別和確認(rèn)恢復(fù)。這是最重要的事情。
舉個例子
廢話少敘,先來張以前的圖看個大概:
此圖是我們業(yè)務(wù)系統(tǒng)metrics的一個例子,顯示了我們前置nginx的部分metrics,通過實時的分析nginx log,我們可以得到所有機房nginx在吞吐量、延時、負(fù)載分配、流量等等多方面的實時信息,一目了然;還可以根據(jù)不同維度進(jìn)行分析比較,幫我們有效的找到各種異常情況(圖里就有一個小缺口)。類似的metrics,我們目前已經(jīng)有幾百個,通過不同的面板組織起來,并且還在不斷的增加。目前,公司的原則是每個項目在開發(fā)之前,就需要盡可能多的定義出相應(yīng)的metrics,做好詳盡的監(jiān)控。
技術(shù)選型
眼尖的同學(xué)會發(fā)現(xiàn)我們用了開源組件grafana。事實上,我們在metrics存儲上采用的就是influxdb/redis+grafana的組合:
1.在我們的SaaS后臺,采用influxdb+grafana 2.0(2.0有單獨的后臺服務(wù))的組合,存儲了海量的metrics,同時滿足大量數(shù)據(jù)的寫入,以及監(jiān)控報警系統(tǒng)的頻繁讀取,同時保留橫向擴展的可能性。
2.在我們的測試環(huán)境/私有化部署環(huán)境,采用redis+grafana 1.9的組合,這個組合部署簡單,開銷相對較小,可以滿足少量的metrics使用。實現(xiàn)上,我們根據(jù)influxdb的存儲結(jié)構(gòu)在redis上復(fù)刻了一份,并且通過proxy來模擬influxdb的接口。
3.實現(xiàn)方式上,我們提供了Python/Java兩個庫,并通過配置文件來作redis/influxdb的無縫切換。每個應(yīng)用根據(jù)自己的需求來決定配置,并調(diào)用api將metrics信息記錄到合適的地方;同時框架自身也做了一些組件專門用來收集系統(tǒng)層面的metrics(比如上面的例子就是通過syslog服務(wù)來接受nginx日志,并做實時的metrics統(tǒng)計)。
得出這樣的架構(gòu)選型,我們當(dāng)初也是傷透了腦筋:
1.前公司用的是類opentsdb的系統(tǒng),在使用便捷性和性能上沒的說,但后端強依賴于hbase,對于我們并不合適。
2.當(dāng)時也看了其他針對這種Time-series data的開源方案,目前其實沒有什么特別好的方案。
3.最終我們還是選了influxdb做為主力,這是一個相對輕量的開源時間序列數(shù)據(jù)庫,很適合于做為metrics使用:它有類似SQL的查詢語句比較容易上手;自帶簡易管理界面;可以用grafana作為前端看板;還有各個語言的客戶端支持;***,它最近還是比較火。
4.選redis的原因在于:私有環(huán)境下需要一個簡單的方案;比較熟悉,當(dāng)influxdb碰到問題時,redis版可以作為備胎頂上。
5.最初我們也考慮過用elasticsearch這個大殺器來做metrics使用,然而:
?。?)es 是重讀輕寫。由于是搜索引擎的出身,它強調(diào)索引。你寫一條記錄,還伴隨著大量的索引工作,有人做過實驗,es和influxdb之間在存儲上是10x的關(guān)系。所以es注定寫性能不是強項(就單機而言),而且索引的建立必然帶來延時和復(fù)雜性。當(dāng)然有了索引,在做一些過濾和聚合的時候,搜索引擎的優(yōu)勢就發(fā)揮出來了,能出更多的報表,也能支持長時間的查詢。
?。?)influxdb是面向時間序列的數(shù)據(jù)庫,這一類數(shù)據(jù)的特征是數(shù)據(jù)量大,寫入壓力高,所以influxdb在索引上沒有側(cè)重,保證了大量數(shù)據(jù)的快速存儲;缺陷在于,沒有索引,每次查詢需要過濾全量數(shù)據(jù),但是基本上能保證讀到***數(shù)據(jù)(沒有延遲索引的影響)。所以,influxdb是輕讀重寫。
?。?)我們的metrics主要是監(jiān)控當(dāng)前狀況,偶爾會回溯一下歷史,同時這些數(shù)據(jù)會被實時報警系統(tǒng)使用,要求響應(yīng)比較快。從使用場景和成本的角度,我們最終選擇了influxdb做為metrics的存儲,elasticsearch單做BI工具使用。
metrics監(jiān)控架構(gòu)
此圖概括描述了我們的監(jiān)控結(jié)構(gòu)。
1.Python和Java程序通過metrics庫將相應(yīng)的數(shù)據(jù)打到指定的地方。
?。?)程序里用到的框架組件(如rpc,分布式log等)會由組件自身進(jìn)行打點,方便框架層面的統(tǒng)一監(jiān)控排錯。
(2)程序里的業(yè)務(wù)metrics需要由工程師手動打點,來記錄每個業(yè)務(wù)和程序模塊的特殊運行狀況。
?。?)為了保證后端metrics數(shù)據(jù)寫入的穩(wěn)定性,我們在client段做了部分聚合操作,減少打點數(shù)據(jù)。
‘ * redis和influxdb做成驅(qū)動形式,通過配置來指定,開發(fā)人員不需要關(guān)心具體的實現(xiàn)。
2.通過jmx,我們來獲得系統(tǒng)數(shù)據(jù),并打入到metrics系統(tǒng),來查看各個機器的物理狀況(感謝前同事wxc的jmx庫)。
3.建立syslog服務(wù),對nginx日志進(jìn)行統(tǒng)計分析,可以得到網(wǎng)站訪問的各種統(tǒng)計信息。
4.對于外網(wǎng)延遲等其他數(shù)據(jù),也可以用相應(yīng)的agent來打入到metrics系統(tǒng)。
5.由于我們的架構(gòu)是跨數(shù)據(jù)中心的統(tǒng)一架構(gòu),還需要接收各個分機房的數(shù)據(jù),我們通過在每個機房建立proxy來接收數(shù)據(jù),并由自研的跨數(shù)據(jù)中心的rpc服務(wù)來進(jìn)行數(shù)據(jù)傳遞。這樣,在主機房的報表中能看到全國的系統(tǒng)運行狀況。
6.對于線上的大型系統(tǒng),我們采用grafana 2.0直連來進(jìn)行數(shù)據(jù)展示,歷史數(shù)據(jù)通過proxy來完成。
7.對于私有部署環(huán)境和測試環(huán)境,我們將數(shù)據(jù)記入redis版的tsdb,通過proxy來提供influxdb接口,來無縫的接入到grafana 1.9(比較輕量,可以嵌入web應(yīng)用)之中。
其他監(jiān)控工具
上文描述的metrics系統(tǒng)解決了我們大部分的問題,是我們監(jiān)控系統(tǒng)的主要成分。同時,我們還使用了一些其他零散的手段:
1.uptime。Uptime是一個開源項目,通過獲取網(wǎng)頁的心跳數(shù)據(jù)來檢測網(wǎng)頁的可用性。如圖:
2.系統(tǒng)資源(CPU、內(nèi)存、硬盤)監(jiān)控。系統(tǒng)監(jiān)控工具很多,一開始我們使用的是collectd這個傳統(tǒng)的工具;后來出于定制化、統(tǒng)一化、練兵的需要,我們改成自己寫Java程序,通過jmx來獲取相關(guān)數(shù)據(jù),并打入到metrics系。collectd就停止使用了。
3.腳本和外部工具。在遇到特殊需求,通用的系統(tǒng)無法滿足的時候,我們也會通過寫shell腳本來做一些工作,這種方式在開發(fā)效率和功能上都比較棒,只是不能很好的和其他數(shù)據(jù)集成;同時,目前互聯(lián)網(wǎng)上也有不少監(jiān)控服務(wù),我們也用了一些,來作為自身監(jiān)控系統(tǒng)的補足和備胎。
二次開發(fā)
因為主要借助于開源系統(tǒng),所以有時候需要進(jìn)行一些二次開發(fā)來滿足公司的定制化需求。這里舉一些比較有用的例子:
1.grafana默認(rèn)的分組顯示(group by)只支持一個tag,這種使用場景比較有限。為了讓其能支持多個版本,我們在兩個版本上都修改了它的前端JS代碼,如下圖所示,修改后的版本可以顯示多個tag組合的數(shù)據(jù)情況(這里是我們的rpc統(tǒng)計中,所有服務(wù)的延時范圍統(tǒng)計)。
2.grafana不支持聚合嵌套,所以像distinct count這樣的功能無法實現(xiàn),這個也通過修改前端代碼解決。
3.grafana可以建多個metrics進(jìn)行比較查看,但永遠(yuǎn)顯示的都是***的數(shù)據(jù),不方便做同環(huán)比比較。我們通過proxy來返回一段時間前的數(shù)據(jù),來達(dá)到這個目的。
4.Uptime檢測https的網(wǎng)頁會有證書錯誤的問題,需要手動在代碼里禁用相應(yīng)的環(huán)境變量。
接著,談報警
光有監(jiān)控是不夠的,因為這么多的數(shù)據(jù)和報表,無法通過人肉的方式跟蹤,所以在收集到這么多數(shù)據(jù)之后,需要有自動化的報警系統(tǒng)來進(jìn)行進(jìn)一步的分析和處理。為此,我們基于收集到的海量數(shù)據(jù),開發(fā)了一個輕量級的報警系統(tǒng),包括報警系統(tǒng)的完整架構(gòu)如下圖所示:
這套系統(tǒng)主要由DataSource,Drivers,Rules,Actions等幾部分組成:
1.DataSource和相應(yīng)的Driver對應(yīng)了不同的監(jiān)控數(shù)據(jù)來源。
2.rules表示我們的一些報警規(guī)則。
3.actions是規(guī)則***后的觸發(fā)動作。
DataSource和Driver
data source表示不同的數(shù)據(jù)來源,每種數(shù)據(jù)來源都由相應(yīng)的driver來獲取,并抽象成統(tǒng)一的數(shù)據(jù)格式(我們采用了類時間序列的格式),這樣可以把數(shù)據(jù)抽取系統(tǒng)和規(guī)則引擎完全解耦,減少開發(fā)復(fù)雜度。目前,我們的datasource,包括:
1.tsdb中的metrics數(shù)據(jù)。
2.這是最主要的數(shù)據(jù)來源,通過獲取存儲在redis/influxdb中的metrics數(shù)據(jù),我們可以對海量的監(jiān)控指標(biāo)進(jìn)行詳盡的分析。
3.grafana面板可以生成influxdb dsl,我們的報警系統(tǒng)直接支持利用此DSL進(jìn)行報警,這樣使用者在grafana面板上配置好監(jiān)控項后,可以很方便的進(jìn)行相應(yīng)的報警。
4.通過上文描述的metrics proxy可以獲取metrics的歷史數(shù)據(jù),方便做同環(huán)比檢測。
5.uptime的數(shù)據(jù)。uptime可以對各個url進(jìn)行監(jiān)控,通過獲取其數(shù)據(jù)可以進(jìn)行網(wǎng)站存活性報警。
6.其他數(shù)據(jù)。還有其他類型的數(shù)據(jù),比如collectd等,也可以方便的集成到報警系統(tǒng)中來。
Rules
從各種data source定期的獲得統(tǒng)一格式的監(jiān)控數(shù)據(jù)后,下一步就是通過報警規(guī)則進(jìn)行數(shù)據(jù)檢查了,來驗證數(shù)據(jù)是否超出了預(yù)設(shè)的閥值。報警規(guī)則向來是個復(fù)雜的問題,需要滿足各種各樣的需求。為此,我們在開發(fā)規(guī)則引擎時,比較重視減少開發(fā)的復(fù)雜程度。目前我們的規(guī)則,有以下兩類:
1.單數(shù)據(jù)源簡單規(guī)則。簡單規(guī)則通過對每次***的監(jiān)控數(shù)據(jù)進(jìn)行閾值比較,來獲得報警。比如:
?。?)上下限閾值比較。這種是最簡單的,定義好上限和下限,就可以發(fā)現(xiàn)異常值。
?。?)數(shù)據(jù)存活性比較。當(dāng)發(fā)現(xiàn)某一監(jiān)控項的數(shù)據(jù)存在(或消失)時,即報警,用來檢查錯誤指標(biāo)(或存活指標(biāo))。
2.單數(shù)據(jù)源組合規(guī)則。簡單規(guī)則產(chǎn)生的報警有可能非常多,我們可以通過對簡單規(guī)則產(chǎn)生的結(jié)果進(jìn)行進(jìn)一步的處理,來減少報警量。比如:
?。?)多次報警。當(dāng)簡單規(guī)則觸發(fā)的內(nèi)部報警在一段時間內(nèi)超過一定的次數(shù)時,才進(jìn)行真正的報警。
?。?)報警cooldown。當(dāng)同一報警不停出現(xiàn)時,此規(guī)則會進(jìn)行相應(yīng)的抑制。
?。?)斷崖式報警。當(dāng)監(jiān)控數(shù)據(jù)出現(xiàn)斷崖式特征時,才進(jìn)行報警。
3.多數(shù)據(jù)源組合規(guī)則。有時候,單一的數(shù)據(jù)源還不夠,需要對多個數(shù)據(jù)源進(jìn)行計算后獲得。比如:
?。?)同環(huán)比報警。對同一監(jiān)控項可以拉取不同時間段的兩條數(shù)據(jù),就可以進(jìn)行相應(yīng)的報警。
?。?)組合運算報警。比如說nginx 2xx狀態(tài)比例的監(jiān)控,可以通過對2xx次數(shù)和總訪問次數(shù)的計算來獲取。
這里只是舉例描述了一些規(guī)則類型,實際系統(tǒng)中會有更多的類型。
Actions
在獲得報警數(shù)據(jù)后,需要促發(fā)一些行為,來完成整個自動化。
1.最常用的報警動作就是發(fā)郵件了,通過對每一類報警制定不同的監(jiān)控人,可以使相關(guān)人員***時間獲悉系統(tǒng)異常。
2.微信報警,郵件的補充。
3.規(guī)則引擎產(chǎn)生的數(shù)據(jù)可以進(jìn)一步寫回metrics系統(tǒng),作第二輪的監(jiān)控報警。比如前文描述的2xx比例(類似的還有各種比例等)。在這種情況下,報警系統(tǒng)相當(dāng)于一個定時的自動化引擎,來做一些定期的數(shù)據(jù)處理,方便我們做更好的監(jiān)控和報表。實際上,這個規(guī)則引擎會成為我們后期自動化任務(wù)引擎的基礎(chǔ)。
有了這套系統(tǒng),目前我們的運營監(jiān)控基本實現(xiàn)了自動化。系統(tǒng)故障時會有相應(yīng)的報警郵件來通知,這樣開發(fā)人員可以集中精力在新功能的研發(fā)上。
數(shù)字化運營
實際上,整套報警監(jiān)控系統(tǒng)不但幫助我們?nèi)ゾS護(hù)網(wǎng)站/系統(tǒng)的穩(wěn)定性,提高自動化程度,還能提升我們的數(shù)字化運營能力,***限度的提升整個公司的效率。
1.簡單報表。grafana這種可視化工具可以解決大部分初期的報表需求,免掉了初期BI人員的投入。
2.定期報表。我們利用報警系統(tǒng),做了簡單的修改,可以對一些監(jiān)控項,在每天凌晨進(jìn)行強制報警(數(shù)據(jù)采集選取1天,報警顯示詳細(xì)數(shù)據(jù)),這樣每天早晨都可以收到過去一天的統(tǒng)計報表。由于復(fù)用了現(xiàn)有的系統(tǒng),省掉了相關(guān)報表功能的開發(fā)。
小結(jié)
本文是我們在過去的大半年中,在監(jiān)控報警上做的一些實踐探索。事實上,在后面的日子里,還需要進(jìn)行更多、更復(fù)雜的工作:
1.接收其他來源的數(shù)據(jù),同時大力完善公司內(nèi)部的監(jiān)控體系。
2.完善分布式log機制,方便排障和更細(xì)粒度的監(jiān)控。
3.將報警監(jiān)控系統(tǒng)和生產(chǎn)的業(yè)務(wù)發(fā)布系統(tǒng)打通,來實現(xiàn)彈性擴容和自動容災(zāi)的可能性。
關(guān)于作者
呂夢琪,上海豈安信息科技公司bigsec框架研發(fā)負(fù)責(zé)人,主導(dǎo)底層框架系統(tǒng)和Java服務(wù)端的研發(fā)工作。她擅長Java研發(fā)、分布式系統(tǒng)、監(jiān)控系統(tǒng)以及各類開源項目的引入和改造。