打造云原生大型分布式監(jiān)控系統(tǒng)

笑談監(jiān)控系統(tǒng)
隨著時間的積累,出現(xiàn)故障的風(fēng)險越來越高,事故的發(fā)生總是出人預(yù)料,如果采用人力運(yùn)維的方式,對于故障定位、故障處理都是很大的挑戰(zhàn)。故障的時間越長,面臨的損失越大,所以在發(fā)展到一定程度的團(tuán)隊(duì)都需要一套完善的監(jiān)控系統(tǒng)

一套完善的監(jiān)控系統(tǒng)最重要的就是本身永遠(yuǎn)不可以故障,即使平臺故障也要確保監(jiān)控可能告警出來,所以監(jiān)控系統(tǒng)本身的高可用,是我們一直在追求的,先來看看一個完備的監(jiān)控系統(tǒng)應(yīng)該考慮哪些功能
監(jiān)控系統(tǒng)設(shè)計面臨什么問題
監(jiān)控系統(tǒng)會對很多角色進(jìn)行監(jiān)控,我把他分為這幾個大類:服務(wù)器、容器、服務(wù)或應(yīng)用、網(wǎng)絡(luò)、存儲、中間件,根據(jù)業(yè)界的方案,不同的分類使用不同的采集器進(jìn)行采集
在功能上要考慮哪些問題?
支持標(biāo)記不同監(jiān)控指標(biāo)來源,方便理清楚業(yè)務(wù)來源 支持聚合運(yùn)算,轉(zhuǎn)換指標(biāo)的含義、組合用來進(jìn)行計算、匯總、分析 告警、報表、圖形化大屏展示 保存歷史數(shù)據(jù)便于溯源
在易用性上應(yīng)該考慮
支持配置增減監(jiān)控項(xiàng),自定義監(jiān)控
支持配置表達(dá)式進(jìn)行計算
最好有自動發(fā)現(xiàn),在新增服務(wù)器或新增pod等資源時自動納入監(jiān)控
支持配置告警策略定義告警范圍與閾值,支持自定義告警
方案選型
從以上方面考慮,應(yīng)該選用哪些開源方案呢?業(yè)界常見的有Elasticsearch、Nagios、zabbix、prometheus,其他方案比較小眾不做討論

Elasticsearch是一個實(shí)時的分布式搜索和分析引擎,支持分片、搜索速度快,一般和Logstash、Kibana結(jié)合起來一起用,也就是ELK,更擅長文檔日志的搜索分析Nagios: 優(yōu)點(diǎn)是出錯的服務(wù)器、應(yīng)用和設(shè)備會自動重啟,自動日志滾動;配置靈活,可以自定義 shell 腳本,通過分布式監(jiān)控模式;并支持以冗余方式進(jìn)行主機(jī)監(jiān)控,報警設(shè)置多樣,以及命令重新加載配置文件無需打擾 Nagios 的運(yùn)行。缺點(diǎn)是事件控制臺功能很弱,插件易用性差;對性能、流量等指標(biāo)的處理不給力;看不到歷史數(shù)據(jù),只能看到報警事件,很難追查故障原因;配置復(fù)雜,初學(xué)者投入的時間、精力和成本比較大。zabbix入門容易、上手簡單、功能強(qiáng)大,容易配置和管理,但是深層次需求需要非常熟悉zabbix并進(jìn)行大量的二次定制開發(fā),二次開發(fā)太多是不可接受的prometheus幾乎支撐了上面所有的需求,可視化展示可以接入grafana,可以用promSQL語言來做聚合查詢,不需要定制;可以使用打tag的方式,對每個指標(biāo)分類;強(qiáng)大的社區(qū)針對各種應(yīng)用、網(wǎng)絡(luò)、服務(wù)器等設(shè)備、角色都提供了采集方案以及無侵入式的高可用方案,這個就是今天討論的重點(diǎn)
根據(jù)上面的種種原因,綜合來看prometheus比較合適
prometheus與他的缺陷

從上面的架構(gòu)圖可以看出,
prometheus是在客戶端部署采集器(exporter)的形式來采集數(shù)據(jù),服務(wù)端主動向prometheus通信來拉取數(shù)據(jù)客戶端也可以通過推送數(shù)據(jù)到
PushGateway再交給prometheus拉取prometheus有自動發(fā)現(xiàn)的能力,簡單配置以后就可以主動拉取平臺接口獲取監(jiān)控范圍:azure、consul、openstack等,并針對檢測角色配置tag,如果和業(yè)務(wù)強(qiáng)相關(guān),可以定制修改代碼,拉取自己平臺的接口來識別監(jiān)控角色和動態(tài)打tagprometheus也有告警的能力,接入官方提供的
AlertManager組件可以檢測產(chǎn)生告警,再使用webhook接入自己的告警郵件/短信通知平臺這里的問題在于無法通過頁面配置告警策略、也無法存儲告警記錄,可以在
AlertManager后面加一些組件來告警收斂、靜默、分組、存儲告警策略的動態(tài)配置,可以寫程序根據(jù)策略生成告警配置、放到
prometheus指定目錄下,并調(diào)用prometheus熱更新接口
唯一要解決的就是負(fù)載量大時出現(xiàn)的性能問題以及高可用問題
單機(jī)prometheus的部署存在的問題

prometheus的架構(gòu)決定是他更適合單機(jī)的部署方案,單機(jī)部署在壓力過大時可以通過服務(wù)器升配的方式緩解壓力,但是依然會存在共性的問題

采集速率會因?yàn)閏pu/網(wǎng)絡(luò)通信限制導(dǎo)致卡頓,采集速度變慢,指標(biāo)在周期內(nèi)未主動拉取的時候會丟失本次的指標(biāo),這里可以把采集周期拉長,后果是粒度變粗,不建議拉太長;另一種方式就是減少無用指標(biāo)的采集
查詢時也是因?yàn)橥瑯拥脑蛩俣葧艿较拗疲瑪?shù)據(jù)存儲時間范圍過多時,對磁盤會有很大的壓力
單點(diǎn)故障時就完全沒有辦法了,直接服務(wù)不可用
單點(diǎn)高負(fù)載考慮什么方案?
參考前一次的文章,高負(fù)載的時候自動水平擴(kuò)展,并做負(fù)載均衡,首先想到的水平擴(kuò)展方式就是Prometheus提供的分組能力

相應(yīng)于把prometheus分片,通過配置的方式各采集部分節(jié)點(diǎn),這種方式有三個問題
數(shù)據(jù)分散運(yùn)維困難
要來回切換數(shù)據(jù)源,看不到全局視圖
解決這個問題,考慮增加一個存儲位置匯總數(shù)據(jù)(remote write)

這里考慮使用TSDB匯總,需要支持?jǐn)U容的、支持集群保證高可用的TSDB
但是需要在TSDB上層再加一個查詢組件來做查詢,會喪失原生的查詢語句能力,可以考慮把TSDB替換成prometheus節(jié)點(diǎn),用聯(lián)邦的形式存儲

這種情況可以滿足基本的使用要求,通過prometheus自監(jiān)控來通知運(yùn)維人員手動擴(kuò)容修改分組,有沒有更自動一點(diǎn)的方式呢?
彈性伸縮(自動水平伸縮)
彈性伸縮的前提有三個
要能監(jiān)控當(dāng)前節(jié)點(diǎn)負(fù)載狀態(tài),預(yù)判擴(kuò)容時機(jī)
需要維護(hù)服務(wù)啟停方式、自動創(chuàng)建服務(wù)并放到相應(yīng)節(jié)點(diǎn)上
同時要能修改
prometheus各節(jié)點(diǎn)數(shù)據(jù)采集范圍
上k8s做容器編排是最直接的方案,可以解決創(chuàng)建和銷毀服務(wù)的問題,也是可以通過cpu使用率或自定義指標(biāo)完成橫向擴(kuò)容的,但解決不了的問題是修改prometheus節(jié)點(diǎn)配置,動態(tài)分配采集范圍,考慮使用以下方案

prometheus注意要配置節(jié)點(diǎn)反親和性(k8s配置podAntiAffinity)寫一個調(diào)度器通過
k8s api檢測prometheus節(jié)點(diǎn)狀態(tài)通過k8s檢測節(jié)點(diǎn)故障以及負(fù)載情況,使用
hash分?jǐn)倝毫Γ瑪U(kuò)展prometheus的sd自動發(fā)現(xiàn)功能,帶上自己的hostname來獲取調(diào)度器提供的數(shù)據(jù)范圍
用這種方式就不需要修改配置文件了,因?yàn)槭?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.047);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">prometheus接口端定時更新監(jiān)控范圍
根據(jù)具體運(yùn)行情況伸縮prometheus,不需要再配置configmap
到這里你可能有一個疑問,假如我監(jiān)控服務(wù)器用上面的方式,那么多接收端,再加一個redis集群的監(jiān)控,應(yīng)該放到哪個節(jié)點(diǎn)上呢?答案是可以專門創(chuàng)建獨(dú)立于此自動伸縮方案的prometheus來進(jìn)行少量數(shù)據(jù)監(jiān)控,或者直接放到所有節(jié)點(diǎn)上,在上層再考慮去重的問題,這個我們一會討論。
到目前為止分片以后分散了壓力,但還沒有解決的問題是數(shù)據(jù)分散無法匯總查詢、單點(diǎn)故障數(shù)據(jù)丟失的問題。
匯總查詢可能你會想到剛剛說的聯(lián)邦部署,但壓力又匯總到一點(diǎn)上了,不能根本的解決問題;解決單點(diǎn)故障應(yīng)該使用冗余的形式部署,給每個監(jiān)控范圍分配2個及以上監(jiān)控節(jié)點(diǎn),但會導(dǎo)致客戶端拉取次數(shù)翻倍,也不建議。
如何保證單點(diǎn)故障數(shù)據(jù)不丟失
為了避免無法匯總查詢、單點(diǎn)故障數(shù)據(jù)丟失的問題,這里打算接入一個高可用方案thanos,把prometheus設(shè)置為無狀態(tài)應(yīng)用,并開啟遠(yuǎn)程寫把數(shù)據(jù)推送到thanos

這樣的話prometheus本身不存儲數(shù)據(jù),即使掛掉部分節(jié)點(diǎn),只要保證node夠多也會再自動伸縮出新的節(jié)點(diǎn),期間讀取到的采集范圍會先負(fù)載變大,然后又得到緩解,整個過程在2個周期內(nèi)解決
PS: ,Prometheus在將采集到的指標(biāo)寫入遠(yuǎn)程存儲之前,會先緩存在內(nèi)存隊(duì)列中,然后打包發(fā)送給遠(yuǎn)端存儲,以減少連接數(shù)量,要提高寫入速率需要修改配置項(xiàng)queue_config
簡單介紹下thanos,thanos是無侵入式的高可用方案,負(fù)責(zé)對prometheus產(chǎn)生的數(shù)據(jù)進(jìn)行匯總、計算、去重、壓縮、存儲、查詢、告警,他實(shí)現(xiàn)了prometheus提供的查詢接口,對外部而言查詢prometheus還是查詢thanos的效果完全一樣,是無感知的
一起來實(shí)現(xiàn)分布式高可用監(jiān)控系統(tǒng)
如何讓我們來實(shí)現(xiàn)一個這樣的組件,你會怎么做呢?

把分片數(shù)據(jù)寫入到存儲,其他組件和存儲通信,thanos的主流方案也是這么做的

如上圖所示所有的組件都會與對象存儲通信,完成數(shù)據(jù)存儲或者讀取的功能
使用對象存儲做存儲引擎
和
prometheus節(jié)點(diǎn)一同部署sidecar,每個節(jié)點(diǎn)對應(yīng)一個,定期放數(shù)據(jù)推送到對象存儲Ruler負(fù)責(zé)判定告警以及根據(jù)規(guī)則做指標(biāo)聚合運(yùn)算Compact負(fù)責(zé)降準(zhǔn)壓縮,一份數(shù)據(jù)變?nèi)荩话闶欠譃?分鐘、5分鐘、1小時寫回存儲,查詢時間粒度越大呈現(xiàn)指標(biāo)粒度越粗,防止前端數(shù)據(jù)刷爆Query與其他組件通過grpc的方式進(jìn)行通信讀取數(shù)據(jù),它不和對象存儲直接通信,而是在中間加了一層gateway網(wǎng)關(guān)上圖的方案
sidecar不是我這次的架構(gòu),其他是一樣的,sidecar的原理是把采集到的數(shù)據(jù)使用緩存到本地(默認(rèn)2小時數(shù)據(jù)為熱數(shù)據(jù)),冷數(shù)據(jù)才推送,近期數(shù)據(jù)存儲本地,查詢時再做匯總會有一定的壓力,同時單點(diǎn)故障問題還是沒有解決
如果是小規(guī)模集群無網(wǎng)絡(luò)壓力可以使用sidercar
不要在接收端存儲
和prometheus部署在一起的sidercar違背了容器中的簡單性原則,也提高存儲壓力,把他們剝離開試試?

我的想法是收集數(shù)據(jù)推送,然后進(jìn)行存儲,由其他組件完成與存儲的通信

如上圖,Receive組件實(shí)現(xiàn)了remote write接口,Prometheus可以將數(shù)據(jù)實(shí)時推送到Receive上;Receive本身實(shí)際上相當(dāng)于一個沒有收集功能的Prometheus,那此時Prometheus就不再需要存儲數(shù)據(jù),之前的方案就可以實(shí)施了
對象存儲中的數(shù)據(jù)具有不可修改特性,也就是說一旦寫入就變成只讀了
Prometheus本地存儲的原理是接受到的數(shù)據(jù)寫到本地文件存儲里面組成WAL文件列表,Receive也是這么做的,然后超過一定時限后生成block,這些block會上傳到對象存儲Query組件來近期數(shù)據(jù)(默認(rèn)2小時內(nèi))查詢recevie,過期后使用對象存儲receive使用k8s的dnssrv功能做服務(wù)發(fā)現(xiàn),便于下游拉取數(shù)據(jù)而不要使用k8s的service:ip自帶的負(fù)載均衡receive自帶了hash算法,可以把上游遠(yuǎn)程寫過來的流量均勻分布在各個節(jié)點(diǎn)上,這里可以采用k8s的service自動輪訓(xùn),recevie會把請求route到相應(yīng)節(jié)點(diǎn)上
為防止prometheus掛掉一個導(dǎo)致的數(shù)據(jù)丟失問題,給prometheus加一個副本,然后在query時去重,主要由query的--query.replica-label 參數(shù)和Prometheus配置的 prometheus_replica參數(shù)來實(shí)現(xiàn),如下圖

同樣的其他組件,如ruler也可以配置冗余部署rule_replica就不展開講了
還好recevie自帶了分布式一致性算法,不然就要自己實(shí)現(xiàn)一個了,到此我們解決了
數(shù)據(jù)接收端能應(yīng)對海量數(shù)據(jù)的壓力均衡
解決了prometheus部署在不同集群上時查詢延遲高的問題
解決了跨節(jié)點(diǎn)數(shù)據(jù)復(fù)合運(yùn)算(
ruler)解決了數(shù)據(jù)壓縮降準(zhǔn)
hashring真的是分布式一致性算法嗎
我們知道分布式一致性算法可以解決下面的問題
在壓力增加時做到自動擴(kuò)容,壓力減小時自動縮容
擴(kuò)縮容時必須要保障數(shù)據(jù)不丟失,單點(diǎn)故障時數(shù)據(jù)也不可以丟失
擴(kuò)縮容時數(shù)據(jù)映射落點(diǎn)要一致,不然會出現(xiàn)數(shù)據(jù)斷連
但是實(shí)際使用過程中,不難發(fā)現(xiàn),還是會發(fā)生數(shù)據(jù)丟失,這引起了我的興趣
這一塊的官網(wǎng)介紹很少,hashring 的endpoints參考下面的代碼,你會發(fā)現(xiàn)0 1 2 的方式就是k8s的statefulset為pod 分配的name,所以recevie要以sts的方式部署,并提前把副本數(shù)與配置關(guān)系對應(yīng)起來,3節(jié)點(diǎn)已經(jīng)可以支撐很大數(shù)量的數(shù)據(jù)處理了
thanos-receive-hashrings.json: |
[
{
"hashring": "soft-tenants",
"endpoints":
[
"thanos-receive-0.thanos-receive.thanos.svc.cluster.local:10901",
"thanos-receive-1.thanos-receive.thanos.svc.cluster.local:10901",
"thanos-receive-2.thanos-receive.thanos.svc.cluster.local:10901"
]
}
]
在源碼里發(fā)現(xiàn),實(shí)際上這里并沒有使用分布式一致性算法!! 在hashring.go函數(shù)里可以看到,這是一個簡單的hash mod,所以hashring是有誤導(dǎo)性的
func (s simpleHashring) GetN(tenant string, ts *prompb.TimeSeries, n uint64) (string, error) {
if n >= uint64(len(s)) {
return "", &insufficientNodesError{have: uint64(len(s)), want: n + 1}
}
return s[(hash(tenant, ts)+n)%uint64(len(s))], nil
}
提煉出來是這樣的hash算法
hash(string(tenant_id) + sort(timeseries.labelset).join())
tenant_id是指數(shù)據(jù)源帶上租戶,可以給不同租戶分配自己的hash具體的
hash算法使用xxHash參考文末資料5
解決的辦法也有了,可以通過配置多副本冗余的方式,把receive的數(shù)據(jù)冗余到其他位置,設(shè)置receive.replication-factor配置,然后拉取數(shù)據(jù)的時候因?yàn)槭褂玫氖欠?wù)發(fā)現(xiàn),和所有服務(wù)通信的方式,可以在一定程序上保證數(shù)據(jù)不丟失
PS: 冗余也會有點(diǎn)問題,算法是先選hash mod后的節(jié)點(diǎn),比如是第n個,然后如果factor是2,就再選n+1和n+2,然后發(fā)請求給n,這個時候如果n掛了其實(shí)會失敗,相對而言n+1或者n+2節(jié)點(diǎn)掛了的話不會對這部分的數(shù)據(jù)有影響
當(dāng)receive出現(xiàn)故障是怎么處理的
當(dāng)發(fā)生擴(kuò)縮容的時候,由于hashring發(fā)生變化,所有的節(jié)點(diǎn)需要將write-ahead-log的數(shù)據(jù)flush到TSDB塊并上傳到OSS中(如果配置了的話),因?yàn)檫@些節(jié)點(diǎn)之后將有一個新的分配。之前已存在節(jié)點(diǎn)上的時間序列不需要作調(diào)整,只是后面過來的請求按新的分發(fā)來尋找該去的receiver節(jié)點(diǎn)。
這個過程不需要重啟receive,代碼里有watch,可以檢測hashring的變化
注意,這種情況發(fā)生的flush可能會產(chǎn)生較小的TSDB塊,但compactor模塊可以將它們優(yōu)化合并,因此不會有什么問題。
當(dāng)有receiver節(jié)點(diǎn)發(fā)生故障時,prometheus的遠(yuǎn)程寫會在后端目標(biāo)無響應(yīng)或503時進(jìn)行重試,因此,receiver一定時間的服務(wù)掛掉是可以容忍的。如果這種掛機(jī)時間是不可接受的話,可以將副本數(shù)配置為 3 或以上,這樣即使有一個receiver節(jié)點(diǎn)掛掉,還有其他receiver節(jié)點(diǎn)來接收寫請求
業(yè)務(wù)指標(biāo)計算問題
如果有非常復(fù)雜的業(yè)務(wù)指標(biāo),需要從其他地方采集推送,最好的方式是寫成采集器exporter,在ruler進(jìn)行復(fù)合運(yùn)算,當(dāng)然也有可能出現(xiàn)表達(dá)式寫不出來的尷尬問題
考慮寫成k8s的job定時任務(wù),把數(shù)據(jù)推送到PushGateway,再交給prometheus去拉取
PS1: 注意按exporter的開發(fā)標(biāo)準(zhǔn),不允許出現(xiàn)重復(fù)指標(biāo)哦
PS2:如果要刪除過期的垃圾數(shù)據(jù)可以調(diào)用PushGateway的http://%s/metrics/job/%s/instance/%s/host/接口進(jìn)行刪除
告警策略動態(tài)更新/告警記錄儲存的問題
要動態(tài)生成告警策略,可以寫一個服務(wù)接收請求,調(diào)用k8s生成configmap,并通知ruler進(jìn)行熱更新
更新策略配置文件configmap(同步更新到pod里會有一定的延遲,使用 subPath是無法熱更新的,注意configMapAndSecretChangeDetectionStrategy: Watch參數(shù)必須為默認(rèn)參數(shù)Watch)把configmap掛載相應(yīng)的ruler上面
全景視圖

最后
當(dāng)然對于一個成熟的監(jiān)控系統(tǒng)來說,除了發(fā)現(xiàn)故障及時告警以外,還應(yīng)該有更多的功能,這不是本次討論的范圍,如果有時間未來會寫寫
運(yùn)營故障報表和資源日報周報月報等用于趨勢分析 低負(fù)載報表用于分析服務(wù)器利用率,防止資源浪費(fèi) 有了故障趨勢和更多的重要指標(biāo)覆蓋,可以結(jié)合AI進(jìn)行故障預(yù)測,在故障發(fā)生前提前預(yù)測
最后的最后
針對全k8s的集群監(jiān)控來說,還有更簡單的方式來監(jiān)控,那就是Prometheus Operator,可以非常簡單的創(chuàng)建k8s的資源,比如收集器Prometheus、采集器的抽象ServiceMonitor、AlertManager等,要監(jiān)控什么數(shù)據(jù)就變成直接操作k8s集群的資源對象了
監(jiān)控可能為其他應(yīng)用的水平伸縮服務(wù)服務(wù),使用Prometheus Adpater來自定義監(jiān)控某些指標(biāo),來達(dá)到自動擴(kuò)縮容的目的
監(jiān)控還可以為運(yùn)維平臺服務(wù),提供故障自動修復(fù)
一句話,只要監(jiān)控運(yùn)維平臺做得足夠好,運(yùn)維人員都得失業(yè)
引用與拓展資料
1、7 款你不得不了解的開源云監(jiān)控工具
2、Thanos在TKEStack中的實(shí)踐 - Even - A super concise theme for Hugo
3、Prometheus Remote Write配置 - 時序數(shù)據(jù)庫 TSDB - 阿里云
4、Thanos - Highly available Prometheus setup with long term storage capabilities
5、xxHash - Extremely fast non-cryptographic hash algorithm
![]()
