監控告警平臺Hickwall架構
編輯推薦: |
本文來源網路,本文將從儲存、聚合、告警三個方面介紹 Hickwall 在核心架構方面的演進,希望對您的學習有所幫助。 |
架構演進概述
為了更好地瞭解 Hickwall 在核心架構方面的設計,我們首先將 Hickwall 第一代的架構和現有架構進行比較。
Hickwall 最初的研發是在 2015-2016 年,當時我們調研了業界知名的開源監控系統。
比如 Graphite,擁有非常好的生態,但是叢集配置複雜,每個指標都採用一個檔案儲存,導致小檔案多,iowait 高,並且使用 python 實現,效能方面不太令人滿意。
再比如 OpenTSDB,基於 HBase 天然就支援分散式,但是也受限於 HBase,多維查詢的時候效能比較差。而其他的監控系統也並未非常成熟,最後我們決定使用 ElasticSearch 作為儲存引擎。下圖是第一代的核心架構圖。
在這個架構中監控資料從 Proxy 進來,經過格式整理、資料補全、限流後傳送到 Kafka。Donwsample 消費 Kafka 中的原始資料進行時間維度上的聚合,聚合成 5m、15m 等時間維度的資料點之後寫入到 Kafka。Consumer 消費 Kafka 中的原始資料和聚合資料寫入到 ES,通過 API-Server 提供統一的介面給看圖和告警。
因為 ES 的查詢效能無法滿足 Trigger 高頻率的拉取需求,我們另外增加了 Redis 用來快取最近一段時間的資料用於告警。這套架構初步實現了監控系統的功能,但是在使用過程中我們也發現以下幾個問題:
1.元件過多。運維架構追求的是至簡至穩,過多的元件會增加部署和維護的難度。另外在團隊人員變動的情況下,新成員進來無法快速上手。
2.資料堆積。Consumer 消費 Kafka 出現問題,容易導致 Kafka 中資料堆積,使用者將無法看到線上系統的當前實時狀態,直到將堆積的資料消費完。按照我們的實踐經驗,資料堆積的時間往往會有幾十分鐘,這對於網際網路企業來講是個非常大的問題。
3.資料鏈條過長。監控資料從 Proxy 進來到 Trigger 告警需要依次經過 6 個元件,任何一個元件出現問題,都可能導致告警漏告或誤告。
為了解決這些問題,我們研發了 Hickwall 的第二代架構,使用自研的 Influxdb 叢集取代了 ES 作為儲存引擎,如下圖。
在這個架構中監控資料從 Proxy 進來分三路轉發,第一路傳送給 Influxdb 叢集,確保無論發生任何故障,只要 Hickwall 恢復正常,使用者就能立即看到線上系統的當前狀態。
第二路傳送給 Kafka,由 Downsample 完成資料聚合後將聚合資料直接寫入到 Influxdb 叢集。第三路傳送給流式告警,這三路資料互不影響,即使儲存和聚合都出現問題,告警依然可以正常工作,確保了告警的可靠穩定。
Influxdb 叢集設計
ES 用於時間序列儲存存在不少問題,例如磁碟使用空間大,磁碟 IO 使用多,索引維護複雜,寫入和查詢速度慢等。
而 Influxdb 是排名第一的時間序列資料庫,能針對時間範圍進行高效的查詢,支援自動刪除過時資料,較低的使用和維護成本。只是早期的 Influxdb 不夠穩定,bug 比較多,直到 2017 年底。我們經過測試確認 Influxdb 已經足夠穩定可以交付生產,就萌生了用 Influxdb 替換 ES 的想法。當然 Influxdb 存在單點問題,在 0.12 版本以後,官方的叢集方案還閉源了。
為了解決 Influxdb 的單點問題,我們研發了 Influxdb 的叢集方案 Incluster,如下圖。
Incluster 並沒有對 Influxdb 進行程式碼侵入式的修改,而是在上層維護關於資料分佈和查詢的元資料,因此當 Influxdb 有重大發布的時候 Incluster 能夠及時更新資料節點。
客戶端通過 Incluster 節點寫入資料,Incluster 按照資料分佈策略將寫入請求轉發到相關的 Influxdb 節點上,查詢的時候按照資料分佈策略從各個節點上讀取資料併合並查詢結果。在元資料這一層 Incluster 採用 raft 保證元資料的一致性和分割槽容錯性,在具體資料節點上使用一致性 hash 保證資料的可用性和分割槽容錯性。
Incluster 提供了三種資料分佈策略 Series、Measurement 和 Measurement+Tag。通過調整資料分佈策略,Incluster 能夠儘量做到減少資料熱點並在查詢時減少查詢節點。在實踐過程中,我們使用 Measurement 策略來儲存系統指標,如 CPU;使用 Measurement+Appid 策略來儲存請求量。
作為一個分散式儲存,磁碟損壞不可避免,災備是必須考慮的問題。我們按照資料分佈策略通過讀取 Influxdb 底層的 TSM 資料檔案,來恢復損壞的節點上面的資料。實踐經驗表明 Incluster 能夠做到半個小時恢復一個損壞的節點。
在使用者使用方面,Incluster 提供了對 InfluxQL 的透明支援,也提供了類 Graphite 語法用於配圖。類 Graphite 語法可以簡化配圖語法,提供 InfluxQL 無法實現的功能,例如查詢最近一段時間變化最劇烈的指標,除此之外還可以遮蔽底層儲存細節,以後如果想使用比 Influxdb 更優秀的時間序列儲存引擎,可以減少使用者遷移成本。
資料聚合的探索
Influxdb 在資料儲存和簡單查詢方面表現出色,但是在資料聚合上就存在一些問題。
Influxdb 提供了 Continuous Query Language(CQL) 用於資料聚合,但是經過測試發現 CQL 記憶體佔用較大。Influxdb 原本需要的記憶體就不小,在我們使用過程中 128G 記憶體已經使用了一半,如果再加上 CQL 的記憶體,容易造成節點不穩定。
另外 CQL 無法從不同的節點獲取資料進行聚合,在 Incluster 叢集方案中存在資源浪費維護複雜的問題。因此我們將資料聚合功能獨立出來,在外部進行資料聚合後再將聚合資料寫入到 Incluster。
時間維度的聚合是有狀態的計算,我們面臨兩個問題。一個是中間狀態如何減少記憶體的使用,另外一個是節點重啟的時候中間狀態如何恢復。
我們通過指定每個節點需要消費的 Kafka Partition,使得每個節點需要處理的資料可控,避免 KafkaPartition Rebalance 導致記憶體不必要的使用,另外通過對 Measurement 和 Tag 這些字串的去重可以減少記憶體使用。中間狀態恢復方面我們並沒有使用儲存 CheckPoint 的方法,而是通過提前一段時間消費來恢復中間狀態。這種方式避免了儲存 CheckPoint 帶來的資源損耗。
業務場景聚合主要的挑戰在於一次聚合涉及到的指標數太多,聚合邏輯複雜。例如某個應用的某個介面的請求成功率,涉及到的指標數目上千,這種聚合查詢 Influxdb 無法支援的。
我們的解決方案是使用 ClickHouse 進行預聚合。ClickHouse 是俄羅斯開源的面向 OLAP 的分散式列式資料庫,擁有極高的讀寫效能,並提供了強大的 SQL 語言和豐富的資料處理函式,可以完成很多指標的處理,例如 P95。
流式告警的實現
告警最簡單的實現就是定時從資料庫中拉取資料,然後檢查一下資料是否有異常。但是這種 Pull 的方式對儲存存在一定的壓力,尤其是告警規則告警物件眾多的時候,對儲存的可靠性和響應時間有極高的要求。
我們經過研究發現告警資料在所有監控資料中佔比其實不大,以攜程為例只佔了 8%,而且需要的絕大部分都是最近幾分鐘的資料,如果我們能從資料流中直接獲取所需要的資料,就能過濾掉大部分不必要的資料,避免對後臺儲存的依賴,讓告警變得更加可靠實時。
實現流式告警最大的挑戰是資料訂閱。我們不可能讓每一個告警規則都去消費一遍資料流,最好的方式是消費一遍資料流然後將告警資料準確的分發到告警上下文中。
在這裡如何降低資料分發的時間複雜度和空間複雜度是最大的難點。
Hickwall 的實現思路是減治法,通過 Measurement 精確匹配減少下一步需要匹配的規則數量,通過 tagValue 的布隆過濾器判斷是哪個 Trigger 節點需要的資料。Trigger 節點收到資料以後對資料點進行精確的匹配過濾,轉發到具體的告警上下文中。
這個方案的優勢在於時間複雜度不隨規則數量告警物件而線性增長,空間複雜度不隨 tagValue 的長度而增長。
Hickwall 使用 Akka 框架進行告警邏輯和告警資料的處理。Akka 是非同步高併發的框架,提供了 Actor 程式設計模型,能夠輕鬆實現併發地處理資料和執行告警邏輯。
生產系統是個時刻變化的系統,每時每刻都可能有機器上下線,每時每刻都可能有應用釋出變更,隨著這些變動告警系統需要隨之增刪告警物件和修改告警閾值。而 Actor 的建立刪除是非常輕量的,為生產系統提供了非常友好的抽象,降低了開發成本。
Hickwall 使用了 RocksDB 來快取告警資料,通過 JNI 直接嵌入到 Trigger 例項中。RocksDB 是 Facebook 開源的 KV 資料庫,基於 Google 的 LevelDB 進行了二次開發,底層儲存使用 LSM Tree,擁有極高的寫入速率。
無停滯的處理資料在流式告警中是非常重要的,使用 RocksDB 能夠減少 JVM 中的物件,減少記憶體的使用,進而減少了 JVM GC 的壓力。
在使用者使用方面,Hickwall 提供了基於 JS 語法的 DSL 語言,Init DSL 負責資料的訂閱和接收到資料後的處理工作,提供了 groupBy、filter、exclude、summarize 等流式計算中常見的資料處理函式,Run DSL 負責具體的告警邏輯,判斷是否有異常。
考慮到 DSL 書寫有一定的難度,Hickwall 提供了語法檢查、歷史資料回測等功能,幫助使用者書寫出符合需求的告警邏輯。