【系統設計】指標監控和告警系統

2022-07-05 12:08:51

在本文中,我們將探討如何設計一個可延伸的指標監控和告警系統。 一個好的監控和告警系統,對基礎設施的可觀察性,高可用性,可靠性方面發揮著關鍵作用。

下圖顯示了市面上一些流行的指標監控和告警服務。

接下來,我們會設計一個類似的服務,可以供大公司內部使用。

設計要求

從一個小明去面試的故事開始。

面試官:如果讓你設計一個指標監控和告警系統,你會怎麼做?

小明:好的,這個系統是為公司內部使用的,還是設計像 Datadog 這種 SaaS 服務?

面試官:很好的問題,目前這個系統只是公司內部使用。

小明:我們想收集哪些指標資訊?

面試官:包括作業系統的指標資訊,中介軟體的指標,以及執行的應用服務的 qps 這些指標。

小明:我們用這個系統監控的基礎設施的規模是多大的?

面試官:1億日活躍使用者,1000個伺服器池,每個池 100 臺機器。

小明:指標資料要儲存多長時間呢?

面試官:我們想保留一年。

小明:好吧,為了較長時間的儲存,可以降低指標資料的解析度嗎?

面試官:很好的問題,對於最新的資料,會儲存 7 天,7天之後可以降低到1分鐘的解析度,而到 30 天之後,可以按照 1 小時的解析度做進一步的彙總。

小明:支援的告警渠道有哪些?

面試官:郵件,電 釘釘,企業微信,Http Endpoint。

小明:我們需要收集紀錄檔嗎?還有是否需要支援分散式系統的鏈路追蹤?

面試官:目前專注於指標,其他的暫時不考慮。

小明:好的,大概都瞭解了。

總結一下,被監控的基礎設施是大規模的,以及需要支援各種維度的指標。另外,整體的系統也有較高的要求,要考慮到可延伸性,低延遲,可靠性和靈活性。

基礎知識

一個指標監控和告警系統通常包含五個元件,如下圖所示

  1. 資料收集:從不同的資料來源收集指標資料。
  2. 資料傳輸:把指標資料傳送到指標監控系統。
  3. 資料儲存:儲存指標資料。
  4. 告警:分析接收到的資料,檢測到異常時可以發出告警通知。
  5. 視覺化:視覺化頁面,以圖形,圖表的形式呈現資料。

資料模式

指標資料通常會儲存為一個時間序列,其中包含一組值及其相關的時間戳。

序列本身可以通過名稱進行唯一標識,也可以通過一組標籤進行標識。

讓我們看兩個例子。

範例1:生產伺服器 i631 在 20:00 的 CPU 負載是多少?

上圖示記的資料點可以用下面的格式表示

在上面的範例中,時間序列由指標名稱,標籤(host:i631,env:prod),時間戳以及對應的值構成。

範例2:過去 10 分鐘內上海地區所有 Web 伺服器的平均 CPU 負載是多少?

從概念上來講,我們會查詢出和下面類似的內容

CPU.load host=webserver01,region=shanghai 1613707265 50

CPU.load host=webserver01,region=shanghai 1613707270 62

CPU.load host=webserver02,region=shanghai 1613707275 43

我們可以通過上面每行末尾的值計算平均 CPU 負載,上面的資料格式也稱為行協定。是市面上很多監控軟體比較常用的輸入格式,Prometheus 和 OpenTSDB 就是兩個例子。

每個時間序列都包含以下內容:

  • 指標名稱,字串型別的 metric name 。
  • 一個鍵值對的陣列,表示指標的標籤,List<key,value>
  • 一個包含時間戳和對應值的的陣列,List <value, timestamp>

資料儲存

資料儲存是設計的核心部分,不建議構建自己的儲存系統,也不建議使用常規的儲存系統(比如 MySQL)來完成這項工作。

理論下,常規資料庫可以支援時間序列資料, 但是需要資料庫專家級別的調優後,才能滿足資料量比較大的場景需求。

具體點說,關係型資料庫沒有對時間序列資料進行優化,有以下幾點原因

  • 在捲動時間視窗中計算平均值,需要編寫複雜且難以閱讀的 SQL。
  • 為了支援標籤(tag/label)資料,我們需要給每個標籤加一個索引。
  • 相比之下,關係型資料庫在持續的高並行寫入操作時表現不佳。

那 NoSQL 怎麼樣呢?理論上,市面上的少數 NoSQL 資料庫可以有效地處理時間序列資料。比如 Cassandra 和 Bigtable 都可以。但是,想要滿足高效儲存和查詢資料的需求,以及構建可延伸的系統,需要深入瞭解每個 NoSQL 的內部工作原理。

相比之下,專門對時間序列資料優化的時序資料庫,更適合這種場景。

OpenTSDB 是一個分散式時序資料庫,但由於它基於 Hadoop 和 HBase,執行 Hadoop/HBase 叢集也會帶來複雜性。Twitter 使用了 MetricsDB 時序資料庫儲存指標資料,而亞馬遜提供了 Timestream 時序資料庫服務。

根據 DB-engines 的報告,兩個最流行的時序資料庫是 InfluxDB 和 Prometheus ,它們可以儲存大量時序資料,並支援快速地對這些資料進行實時分析。

如下圖所示,8 核 CPU 和 32 GB RAM 的 InfluxDB 每秒可以處理超過 250,000 次寫入。

高層次設計

  • Metrics Source 指標來源,應用服務,資料庫,訊息佇列等。
  • Metrics Collector 指標收集器。
  • Time series DB 時序資料庫,儲存指標資料。
  • Query Service 查詢服務,向外提供指標查詢介面。
  • Alerting System 告警系統,檢測到異常時,傳送告警通知。
  • Visualization System 視覺化,以圖表的形式展示指標。

深入設計

現在,讓我們聚焦於資料收集流程。主要有推和拉兩種方式。

拉模式

上圖顯示了使用了拉模式的資料收集,單獨設定了資料收集器,定期從執行的應用中拉取指標資料。

這裡有一個問題,資料收集器如何知道每個資料來源的地址? 一個比較好的方案是引入服務註冊發現元件,比如 etcd,ZooKeeper,如下

下圖展示了我們現在的資料拉取流程。

  1. 指標收集器從服務發現元件中獲取後設資料,包括拉取間隔,IP 地址,超時,重試引數等。
  2. 指標收集器通過設定的 HTTP 端點獲取指標資料。

在資料量比較大的場景下,單個指標收集器是獨木難支的,我們必須使用一組指標收集器。但是多個收集器和多個資料來源之間應該如何協調,才能正常工作不發生衝突呢?

一致性雜湊很適合這種場景,我們可以把資料來源對映到雜湊環上,如下

這樣可以保證每個指標收集器都有對應的資料來源,相互工作且不會發生衝突。

推模式

如下圖所示,在推模式中,各種指標資料來源(Web 應用,資料庫,訊息佇列)直接傳送到指標收集器。

在推模式中,需要在每個被監控的伺服器上安裝收集器代理,它可以收集伺服器的指標資料,然後定期的傳送給指標收集器。

推和拉兩種模式哪種更好?沒有固定的答案,這兩個方案都是可行的,甚至在一些複雜場景中,需要同時支援推和拉。

擴充套件資料傳輸

現在,讓我們主要關注指標收集器和時序資料庫。不管使用推還是拉模式,在需要接收大量資料的場景下,指標收集器通常是一個服務叢集。

但是,當時序資料庫不可用時,就會存在資料丟失的風險,所以,我們引入了 Kafka 訊息佇列元件, 如下圖

指標收集器把指標資料傳送到 Kafka 訊息佇列,然後消費者或者流處理服務進行資料處理,比如 Apache Storm、Flink 和 Spark, 最後再推播到時序資料庫。

指標計算

指標在多個地方都可以聚合計算,看看它們都有什麼不一樣。

  • 使用者端代理:使用者端安裝的收集代理只支援簡單的聚合邏輯。
  • 傳輸管道:在資料寫入時序資料庫之前,我們可以用 Flink 流處理服務進行聚合計算,然後只寫入彙總後的資料,這樣寫入量會大大減少。但是由於我們沒有儲存原始資料,所以丟失了資料精度。
  • 查詢端:我們可以在查詢端對原始資料進行實時聚合查詢,但是這樣方式查詢效率不太高。

時序資料庫查詢語言

大多數流行的指標監控系統,比如 Prometheus 和 InfluxDB 都不使用 SQL,而是有自己的查詢語言。一個主要原因是很難通過 SQL 來查詢時序資料, 並且難以閱讀,比如下面的SQL 你能看出來在查詢什麼資料嗎?

select id,
       temp,
       avg(temp) over (partition by group_nr order by time_read) as rolling_avg
from (
  select id,
         temp,
         time_read,
         interval_group,
         id - row_number() over (partition by interval_group order by time_read) as group_nr
  from (
    select id,
    time_read,
    "epoch"::timestamp + "900 seconds"::interval * (extract(epoch from time_read)::int4 / 900) as interval_group,
    temp
    from readings
  ) t1
) t2
order by time_read;

相比之下, InfluxDB 使用的針對於時序資料的 Flux 查詢語言會更簡單更好理解,如下

from(db:"telegraf")
  |> range(start:-1h)
  |> filter(fn: (r) => r._measurement == "foo")
  |> exponentialMovingAverage(size:-10s)

資料編碼和壓縮

資料編碼和壓縮可以很大程度上減小資料的大小,特別是在時序資料庫中,下面是一個簡單的例子。

因為一般資料收集的時間間隔是固定的,所以我們可以把一個基礎值和增量一起儲存,比如 1610087371, 10, 10, 9, 11 這樣,可以佔用更少的空間。

下取樣

下取樣是把高解析度的資料轉換為低解析度的過程,這樣可以減少磁碟使用。由於我們的資料保留期是1年,我們可以對舊資料進行下取樣,這是一個例子:

  • 7天資料,不進行取樣。
  • 30天資料,下取樣到1分鐘的解析度
  • 1年資料,下取樣到1小時的解析度。

我們看另外一個具體的例子,它把 10 秒解析度的資料聚合為 30 秒解析度。

原始資料

下取樣之後

告警服務

讓我們看看告警服務的設計圖,以及工作流程。

  1. 載入 YAML 格式的告警組態檔到快取。

    - name: instance_down
      rules:
      # 服務不可用時間超過 5 分鐘觸發告警.
      - alert: instance_down
        expr: up == 0
        for: 5m
        labels:
          severity: page
    
  2. 警報管理器從快取中讀取設定。

  3. 根據告警規則,按照設定的時間和條件查詢指標,如果超過閾值,則觸發告警。

  4. Alert Store 儲存著所有告警的狀態(掛起,觸發,已解決)。

  5. 符合條件的告警會新增到 Kafka 中。

  6. 消費佇列,根據告警規則,傳送警報資訊到不同的通知渠道。

視覺化

視覺化建立在資料層之上,指標資料可以在指標儀表板上顯示,告警資訊可以在告警儀表板上顯示。下圖顯示了一些指標,伺服器的請求數量、記憶體/CPU 利用率、頁面載入時間、流量和登入資訊。

Grafana 可以是一個非常好的視覺化系統,我們可以直接拿來使用。

總結

在本文中,我們介紹了指標監控和告警系統的設計。在高層次上,我們討論了資料收集、時序資料庫、告警和視覺化,下圖是我們最終的設計:

Reference

[0] System Design Interview Volume 2:
https://www.amazon.com/System-Design-Interview-Insiders-Guide/dp/1736049119

[1] Datadog: https://www.datadoghq.com/

[2] Splunk: https://www.splunk.com/

[3] Elastic stack: https://www.elastic.co/elastic-stack

[4] Dapper, a Large-Scale Distributed Systems Tracing Infrastructure:
https://research.google/pubs/pub36356/

[5] Distributed Systems Tracing with Zipkin:
https://blog.twitter.com/engineering/en_us/a/2012/distributed-systems-tracing-with-zipkin.html

[6] Prometheus: https://prometheus.io/docs/introduction/overview/

[7] OpenTSDB - A Distributed, Scalable Monitoring System: http://opentsdb.net/

[8] Data model: : https://prometheus.io/docs/concepts/data_model/

[9] Schema design for time-series data | Cloud Bigtable Documentation
https://cloud.google.com/bigtable/docs/schema-design-time-series

[10] MetricsDB: TimeSeries Database for storing metrics at Twitter:
https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/metricsdb.html

[11] Amazon Timestream: https://aws.amazon.com/timestream/

[12] DB-Engines Ranking of time-series DBMS: https://db-engines.com/en/ranking/time+series+dbms

[13] InfluxDB: https://www.influxdata.com/

[14] etcd: https://etcd.io

[15] Service Discovery with Zookeeper
https://cloud.spring.io/spring-cloud-zookeeper/1.2.x/multi/multi_spring-cloud-zookeeper-discovery.html

[16] Amazon CloudWatch: https://aws.amazon.com/cloudwatch/

[17] Graphite: https://graphiteapp.org/

[18] Push vs Pull: http://bit.ly/3aJEPxE

[19] Pull doesn’t scale - or does it?:
https://prometheus.io/blog/2016/07/23/pull-does-not-scale-or-does-it/

[20] Monitoring Architecture:
https://developer.lightbend.com/guides/monitoring-at-scale/monitoring-architecture/architecture.html

[21] Push vs Pull in Monitoring Systems:
https://giedrius.blog/2019/05/11/push-vs-pull-in-monitoring-systems/

[22] Pushgateway: https://github.com/prometheus/pushgateway

[23] Building Applications with Serverless Architectures
https://aws.amazon.com/lambda/serverless-architectures-learn-more/

[24] Gorilla: A Fast, Scalable, In-Memory Time Series Database:
http://www.vldb.org/pvldb/vol8/p1816-teller.pdf

[25] Why We’re Building Flux, a New Data Scripting and Query Language:
https://www.influxdata.com/blog/why-were-building-flux-a-new-data-scripting-and-query-language/

[26] InfluxDB storage engine: https://docs.influxdata.com/influxdb/v2.0/reference/internals/storage-engine/

[27] YAML: https://en.wikipedia.org/wiki/YAML

[28] Grafana Demo: https://play.grafana.org/

最後

做了一個 .NET 的學習網站,內容涵蓋了分散式系統,資料結構與演演算法,設計模式,作業系統,計算機網路等,以及工作推薦和麵試經驗分享,歡迎來撩。

關注公眾號

回覆 dotnet 獲取網站地址。

回覆 面試題 獲取 .NET 面試題。

回覆 程式設計師副業 獲取適合程式設計師的副業指南。