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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最后
當然對于一個成熟的監(jiān)控系統(tǒng)來說,除了發(fā)現(xiàn)故障及時告警以外,還應該有更多的功能,這不是本次討論的范圍,如果有時間未來會寫寫
運營故障報表和資源日報周報月報等用于趨勢分析 低負載報表用于分析服務器利用率,防止資源浪費 有了故障趨勢和更多的重要指標覆蓋,可以結(jié)合AI進行故障預測,在故障發(fā)生前提前預測
最后的最后
針對全k8s的集群監(jiān)控來說,還有更簡單的方式來監(jiān)控,那就是Prometheus Operator,可以非常簡單的創(chuàng)建k8s的資源,比如收集器Prometheus、采集器的抽象ServiceMonitor、AlertManager等,要監(jiān)控什么數(shù)據(jù)就變成直接操作k8s集群的資源對象了
監(jiān)控可能為其他應用的水平伸縮服務服務,使用Prometheus Adpater來自定義監(jiān)控某些指標,來達到自動擴縮容的目的
監(jiān)控還可以為運維平臺服務,提供故障自動修復
一句話,只要監(jiān)控運維平臺做得足夠好,運維人員都得失業(yè)
引用與拓展資料
1、7 款你不得不了解的開源云監(jiān)控工具
2、Thanos在TKEStack中的實踐 - 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
—————END————— 推薦閱讀:
SpringBoot+vue.js搭建圖書管理系統(tǒng) i++ 是線程安全的嗎? IDEA 卡成球了 !咋優(yōu)化 ? 最美的Vue+Element開源后臺管理系統(tǒng) 基于SpringBoot的迷你商城系統(tǒng),附源碼! IntelliJ IDEA 2020.2.4款 神級超級牛逼插件推薦
最近面試BAT,整理一份面試資料《Java面試BAT通關手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。 獲取方式:關注公眾號并回復 java 領取,更多內(nèi)容陸續(xù)奉上。 明天見(??ω??)??
