打造雲原生大型分佈式監控系統

打造雲原生大型分佈式監控系統

笑談監控系統

隨着時間的積累,出現故障的風險越來越高,事故的發生總是出人預料,如果採用人力運維的方式,對於故障定位、故障處理都是很大的挑戰。故障的時間越長,面臨的損失越大,所以在發展到一定程度的團隊都需要一套完善的監控系統

監控大屏

一套完善的監控系統最重要的就是本身永遠不可以故障,即使平臺故障也要確保監控可能告警出來,所以監控系統本身的高可用,是我們一直在追求的,先來看看一個完備的監控系統應該考慮哪些功能

監控系統設計面臨什麼問題

監控系統會對很多角色進行監控,我把他分爲這幾個大類:服務器、容器、服務或應用、網絡、存儲、中間件,根據業界的方案,不同的分類使用不同的採集器進行採集

在功能上要考慮哪些問題?

  • 支持標記不同監控指標來源,方便理清楚業務來源
  • 支持聚合運算,轉換指標的含義、組合用來進行計算、彙總、分析
  • 告警、報表、圖形化大屏展示
  • 保存歷史數據便於溯源

在易用性上應該考慮

  • 支持配置增減監控項,自定義監控

  • 支持配置表達式進行計算

  • 最好有自動發現,在新增服務器或新增pod等資源時自動納入監控

  • 支持配置告警策略定義告警範圍與閾值,支持自定義告警

方案選型

從以上方面考慮,應該選用哪些開源方案呢?業界常見的有ElasticsearchNagioszabbixprometheus,其他方案比較小衆不做討論

方案選型
  • Elasticsearch 是一個實時的分佈式搜索和分析引擎,支持分片、搜索速度快,一般和LogstashKibana結合起來一起用,也就是ELK,更擅長文檔日誌的搜索分析

  • Nagios: 優點是出錯的服務器、應用和設備會自動重啓,自動日誌滾動;配置靈活,可以自定義 shell 腳本,通過分佈式監控模式;並支持以冗餘方式進行主機監控,報警設置多樣,以及命令重新加載配置文件無需打擾 Nagios 的運行。缺點是事件控制檯功能很弱,插件易用性差;對性能、流量等指標的處理不給力;看不到歷史數據,只能看到報警事件,很難追查故障原因;配置複雜,初學者投入的時間、精力和成本比較大。

  • zabbix入門容易、上手簡單、功能強大,容易配置和管理,但是深層次需求需要非常熟悉 zabbix 並進行大量的二次定製開發,二次開發太多是不可接受的

  • prometheus幾乎支撐了上面所有的需求,可視化展示可以接入grafana,可以用promSQL語言來做聚合查詢,不需要定製;可以使用打tag的方式,對每個指標分類;強大的社區針對各種應用、網絡、服務器等設備、角色都提供了採集方案以及無侵入式的高可用方案,這個就是今天討論的重點

根據上面的種種原因,綜合來看prometheus比較合適

prometheus與他的缺陷

prometheus架構圖
  • 從上面的架構圖可以看出,prometheus是在客戶端部署採集器(exporter)的形式來採集數據,服務端主動向prometheus通信來拉取數據

  • 客戶端也可以通過推送數據到PushGateway再交給prometheus拉取

  • prometheus有自動發現的能力,簡單配置以後就可以主動拉取平臺接口獲取監控範圍:azure、consul、openstack等,並針對檢測角色配置tag,如果和業務強相關,可以定製修改代碼,拉取自己平臺的接口來識別監控角色和動態打tag

  • prometheus也有告警的能力,接入官方提供的AlertManager組件可以檢測產生告警,再使用webhook接入自己的告警郵件/短信通知平臺

    • 這裏的問題在於無法通過頁面配置告警策略、也無法存儲告警記錄,可以在AlertManager後面加一些組件來告警收斂、靜默、分組、存儲

    • 告警策略的動態配置,可以寫程序根據策略生成告警配置、放到prometheus指定目錄下,並調用prometheus熱更新接口

唯一要解決的就是負載量大時出現的性能問題以及高可用問題

單機prometheus的部署存在的問題

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

單點prometheus的問題
  • 採集速率會因爲cpu/網絡通信限制導致卡頓,採集速度變慢,指標在週期內未主動拉取的時候會丟失本次的指標,這裏可以把採集週期拉長,後果是粒度變粗,不建議拉太長;另一種方式就是減少無用指標的採集

  • 查詢時也是因爲同樣的原因速度會受到限制,數據存儲時間範圍過多時,對磁盤會有很大的壓力

  • 單點故障時就完全沒有辦法了,直接服務不可用

單點高負載考慮什麼方案?

參考前一次的文章,高負載的時候自動水平擴展,並做負載均衡,首先想到的水平擴展方式就是Prometheus提供的分組能力

分片採集

相應於把prometheus分片,通過配置的方式各採集部分節點,這種方式有三個問題

  • 數據分散運維困難

  • 要來回切換數據源,看不到全局視圖

解決這個問題,考慮增加一個存儲位置彙總數據(remote write)

分片後彙總

這裏考慮使用TSDB彙總,需要支持擴容的、支持集羣保證高可用的TSDB

但是需要在TSDB上層再加一個查詢組件來做查詢,會喪失原生的查詢語句能力,可以考慮把TSDB替換成prometheus節點,用聯邦的形式存儲

聯邦

這種情況可以滿足基本的使用要求,通過prometheus自監控來通知運維人員手動擴容修改分組,有沒有更自動一點的方式呢?

彈性伸縮(自動水平伸縮)

彈性伸縮的前提有三個

  • 要能監控當前節點負載狀態,預判擴容時機

  • 需要維護服務啓停方式、自動創建服務並放到相應節點上

  • 同時要能修改prometheus各節點數據採集範圍

k8s做容器編排是最直接的方案,可以解決創建和銷燬服務的問題,也是可以通過cpu使用率或自定義指標完成橫向擴容的,但解決不了的問題是修改prometheus節點配置,動態分配採集範圍,考慮使用以下方案

調度器
  • prometheus注意要配置節點反親和性(k8s配置podAntiAffinity

  • 寫一個調度器通過k8s api檢測prometheus節點狀態

  • 通過k8s檢測節點故障以及負載情況,使用hash分攤壓力,擴展prometheussd自動發現功能,帶上自己的hostname來獲取調度器提供的數據範圍

用這種方式就不需要修改配置文件了,因爲是prometheus接口端定時更新監控範圍

  • 根據具體運行情況伸縮prometheus,不需要再配置configmap

到這裏你可能有一個疑問,假如我監控服務器用上面的方式,那麼多接收端,再加一個redis集羣的監控,應該放到哪個節點上呢?答案是可以專門創建獨立於此自動伸縮方案的prometheus來進行少量數據監控,或者直接放到所有節點上,在上層再考慮去重的問題,這個我們一會討論。

到目前爲止分片以後分散了壓力,但還沒有解決的問題是數據分散無法彙總查詢、單點故障數據丟失的問題。

彙總查詢可能你會想到剛剛說的聯邦部署,但壓力又彙總到一點上了,不能根本的解決問題;解決單點故障應該使用冗餘的形式部署,給每個監控範圍分配2個及以上監控節點,但會導致客戶端拉取次數翻倍,也不建議。

如何保證單點故障數據不丟失

爲了避免無法彙總查詢、單點故障數據丟失的問題,這裏打算接入一個高可用方案thanos,把prometheus設置爲無狀態應用,並開啓遠程寫把數據推送到thanos

推送到thanos

這樣的話prometheus本身不存儲數據,即使掛掉部分節點,只要保證node夠多也會再自動伸縮出新的節點,期間讀取到的採集範圍會先負載變大,然後又得到緩解,整個過程在2個週期內解決

PS: ,Prometheus在將採集到的指標寫入遠程存儲之前,會先緩存在內存隊列中,然後打包發送給遠端存儲,以減少連接數量,要提高寫入速率需要修改配置項queue_config

簡單介紹下thanosthanos是無侵入式的高可用方案,負責對prometheus產生的數據進行彙總、計算、去重、壓縮、存儲、查詢、告警,他實現了prometheus提供的查詢接口,對外部而言查詢prometheus還是查詢thanos的效果完全一樣,是無感知的

一起來實現分佈式高可用監控系統

如何讓我們來實現一個這樣的組件,你會怎麼做呢?

彙總存儲,上層完成其他功能

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

thanos架構圖

如上圖所示所有的組件都會與對象存儲通信,完成數據存儲或者讀取的功能

  • 使用對象存儲做存儲引擎

  • prometheus節點一同部署sidecar,每個節點對應一個,定期放數據推送到對象存儲

  • Ruler負責判定告警以及根據規則做指標聚合運算

  • Compact負責降準壓縮,一份數據變三份,一般是分爲1分鐘、5分鐘、1小時寫回存儲,查詢時間粒度越大呈現指標粒度越粗,防止前端數據刷爆

  • Query與其他組件通過grpc的方式進行通信讀取數據,它不和對象存儲直接通信,而是在中間加了一層gateway網關

  • 上圖的方案sidecar不是我這次的架構,其他是一樣的,sidecar的原理是把採集到的數據使用緩存到本地(默認2小時數據爲熱數據),冷數據才推送,近期數據存儲本地,查詢時再做彙總會有一定的壓力,同時單點故障問題還是沒有解決

如果是小規模集羣無網絡壓力可以使用sidercar

不要在接收端存儲

prometheus部署在一起的sidercar違背了容器中的簡單性原則,也提高存儲壓力,把他們剝離開試試?

彙總再轉存

我的想法是收集數據推送,然後進行存儲,由其他組件完成與存儲的通信

receive方案

如上圖,Receive組件實現了remote write接口,Prometheus可以將數據實時推送到Receive上;Receive本身實際上相當於一個沒有收集功能的Prometheus,那此時Prometheus就不再需要存儲數據,之前的方案就可以實施了

  • 對象存儲中的數據具有不可修改特性,也就是說一旦寫入就變成只讀了

  • Prometheus本地存儲的原理是接受到的數據寫到本地文件存儲裏面組成WAL文件列表,Receive也是這麼做的,然後超過一定時限後生成block,這些block會上傳到對象存儲

  • Query組件來近期數據(默認2小時內)查詢recevie,過期後使用對象存儲

  • receive使用k8s的dnssrv功能做服務發現,便於下游拉取數據而不要使用k8s的service:ip自帶的負載均衡

  • receive自帶了hash算法,可以把上游遠程寫過來的流量均勻分佈在各個節點上,這裏可以採用k8s的service自動輪訓,recevie會把請求route到相應節點上

爲防止prometheus掛掉一個導致的數據丟失問題,給prometheus加一個副本,然後在query時去重,主要由query--query.replica-label 參數和Prometheus 配置的 prometheus_replica參數來實現,如下圖

概覽

同樣的其他組件,如ruler也可以配置冗餘部署rule_replica就不展開講了

還好recevie自帶了分佈式一致性算法,不然就要自己實現一個了,到此我們解決了

  • 數據接收端能應對海量數據的壓力均衡

  • 解決了prometheus部署在不同集羣上時查詢延遲高的問題

  • 解決了跨節點數據複合運算(ruler

  • 解決了數據壓縮降準

hashring真的是分佈式一致性算法嗎

我們知道分佈式一致性算法可以解決下面的問題

  • 在壓力增加時做到自動擴容,壓力減小時自動縮容

  • 擴縮容時必須要保障數據不丟失,單點故障時數據也不可以丟失

  • 擴縮容時數據映射落點要一致,不然會出現數據斷連

但是實際使用過程中,不難發現,還是會發生數據丟失,這引起了我的興趣

這一塊的官網介紹很少,hashringendpoints參考下面的代碼,你會發現0 1 2 的方式就是k8sstatefulsetpod 分配的name,所以recevie要以sts的方式部署,並提前把副本數與配置關係對應起來,3節點已經可以支撐很大數量的數據處理了

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"
        ]
      }
    ]

在源碼裏發現,實際上這裏並沒有使用分佈式一致性算法!!hashring.go函數裏可以看到,這是一個簡單的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是指數據源帶上租戶,可以給不同租戶分配自己的hash

  • 具體的hash算法使用xxHash 參考文末資料5

解決的辦法也有了,可以通過配置多副本冗餘的方式,把receive的數據冗餘到其他位置,設置receive.replication-factor配置,然後拉取數據的時候因爲使用的是服務發現,和所有服務通信的方式,可以在一定程序上保證數據不丟失

PS: 冗餘也會有點問題,算法是先選hash mod後的節點,比如是第n個,然後如果factor是2,就再選n+1n+2,然後發請求給n,這個時候如果n掛了其實會失敗,相對而言n+1或者n+2節點掛了的話不會對這部分的數據有影響

當receive出現故障是怎麼處理的

當發生擴縮容的時候,由於hashring發生變化,所有的節點需要將write-ahead-log的數據flushTSDB塊並上傳到OSS中(如果配置了的話),因爲這些節點之後將有一個新的分配。之前已存在節點上的時間序列不需要作調整,只是後面過來的請求按新的分發來尋找該去的receiver節點。

這個過程不需要重啓receive,代碼裏有watch,可以檢測hashring的變化

注意,這種情況發生的flush可能會產生較小的TSDB塊,但compactor模塊可以將它們優化合並,因此不會有什麼問題。

當有receiver節點發生故障時,prometheus的遠程寫會在後端目標無響應或503時進行重試,因此,receiver一定時間的服務掛掉是可以容忍的。如果這種掛機時間是不可接受的話,可以將副本數配置爲 3 或以上,這樣即使有一個receiver節點掛掉,還有其他receiver節點來接收寫請求

業務指標計算問題

如果有非常複雜的業務指標,需要從其他地方採集推送,最好的方式是寫成採集器exporter,在ruler進行復合運算,當然也有可能出現表達式寫不出來的尷尬問題

考慮寫成k8sjob定時任務,把數據推送到PushGateway,再交給prometheus去拉取

PS1: 注意按exporter的開發標準,不允許出現重複指標哦

PS2:如果要刪除過期的垃圾數據可以調用PushGatewayhttp://%s/metrics/job/%s/instance/%s/host/接口進行刪除

告警策略動態更新/告警記錄儲存的問題

要動態生成告警策略,可以寫一個服務接收請求,調用k8s生成configmap,並通知ruler進行熱更新

  • 更新策略配置文件configmap(同步更新到pod裏會有一定的延遲,使用 subPath是無法熱更新的,注意 configMapAndSecretChangeDetectionStrategy: Watch參數必須爲默認參數 Watch
  • 把configmap掛載相應的ruler上面

全景視圖

全景視圖

最後

當然對於一個成熟的監控系統來說,除了發現故障及時告警以外,還應該有更多的功能,這不是本次討論的範圍,如果有時間未來會寫寫

  • 運營故障報表和資源日報週報月報等用於趨勢分析
  • 低負載報表用於分析服務器利用率,防止資源浪費
  • 有了故障趨勢和更多的重要指標覆蓋,可以結合AI進行故障預測,在故障發生前提前預測

最後的最後

針對全k8s的集羣監控來說,還有更簡單的方式來監控,那就是Prometheus Operator,可以非常簡單的創建k8s的資源,比如收集器Prometheus、採集器的抽象ServiceMonitorAlertManager等,要監控什麼數據就變成直接操作k8s集羣的資源對象了

監控可能爲其他應用的水平伸縮服務服務,使用Prometheus Adpater來自定義監控某些指標,來達到自動擴縮容的目的

監控還可以爲運維平臺服務,提供故障自動修復

一句話,只要監控運維平臺做得足夠好,運維人員都得失業

引用與拓展資料

  • 1、7 款你不得不瞭解的開源雲監控工具

  • 2、Thanos在TKEStack中的實踐 - Even - A super concise theme for Hugo

  • 3、Prometheus Remote Write配置 - 時序數據庫 TSDB - 阿里雲

  • 4、Thanos - Highly available Prometheus setup with long term storage capabilities

  • 5、xxHash - Extremely fast non-cryptographic hash algorithm

本文分享自微信公衆號 - 編程三分鐘(coding3min)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章