Uber:大規模系統下如何構建可伸縮的告警生態系統
Uber 的軟體架構包含上千個 ofollow,noindex">微服務 ,它們能夠讓團隊快速迭代並支撐公司的全球化增長。這些微服務支撐著各種解決方案,比如移動應用、內部與基礎設施服務、產品等,它們有著非常複雜的配置,這些配置會在城市和子城市級別對產品的行為產生影響。
為了維持我們的增長和架構,Uber 的可觀察性(Observability)團隊構建了一個健壯的、可伸縮的指標和告警管道,當服務出現問題時,它負責探測、緩解和通知相關的工程師。具體來講,我們構建了兩個資料中心內的告警系統,稱為 uMonitor 和 Neris,它們會流入相同的通知和告警管道。uMonitor 是我們基於度量指標的告警系統,它會基於度量資料庫 M3 執行檢查,而 Neris 主要查詢主機級別的告警。
Neris 和 uMonitor 利用一個公共管道傳送通知和去重。我們將深入研究這些系統,並討論採取更多的緩解行動、名為 Origami 的新告警去重平臺以及在建立高信噪比告警等方面所面臨的挑戰。
除此之外,我們還開發了一個黑盒告警系統,當我們的內部系統出現故障或資料中心完全不可用時,該系統可以從資料中心外部檢測出高級別的中斷。以後的部落格文章將討論這種設定。
Uber 的告警
在 Uber 這種規模的公司中,監控和告警不能按照傳統的現成解決方案進行思考。Uber 的告警系統是從 Nagios 開始的,使用源控制指令碼對 carbon 指標進行 Graphite 閾值檢查。由於 Carbon metrics 叢集的可伸縮性問題,我們決定構建自己的大規模度量平臺 M3。為了提高告警系統的可用性,我們開發了 uMonitor,這是我們自行開發的基於時間序列的度量指標告警系統,它所針對的是儲存在 M3 中的度量資料。對於沒有儲存在 M3 中的度量指標,我們構建了 Neris 來執行主機級別的告警檢查。
uMonitor 在構建時考慮到了靈活性和使用場景的多樣性。有些告警資訊是基於標準的度量指標自動生成的,比如端點錯誤和 CPU/ 記憶體消耗。其他的告警由度量指標相關的各個團隊來建立。我們將 uMonitor 構建成一個平臺,滿足各種不同的使用場景,具體來說:
- 簡單的告警管理:對於每個告警,迭代確定適合的函式和閾值;
- 靈活的操作:各種通知機制,比如網路簡訊(paging)、Email 和聊天工具。支援自動緩解,比如回滾到上次部署以及配置修改;
- 處理高基數:能夠針對最小範圍內的嚴重問題發出告警,但是不會在出現更大範圍故障時,對團隊成員形成通知風暴。
使用 uMonitor 實現基於度量指標的告警
uMonitor 有三個獨立的元件:一個儲存服務,它具有告警管理的 API,幷包裝了我們的 Cassandra 告警和狀態儲存;一個排程器,跟蹤所有的告警,針對每個告警要求,每隔一分鐘分發告警檢查任務給 worker;worker,針對每個告警所定義的底層度量指標,執行告警檢查。
worker 會在 Cassandra 儲存中維護告警檢查的狀態,並通過一個主動重試機制確保通知至少能夠成功傳送一次。worker 會間隔一定的時間(通常是一個小時)對持續出現的告警重新發出警報。目前,uMonitor 擁有 12.5 萬個告警配置,每秒檢查 7 億個資料點,超過 140 萬個時間序列。
告警的定義包含一個 M3 查詢( Graphite 或 M3QL )和閾值,用來判斷告警是否違反了閾值。查詢將會返回一個或多個時序,閾值將會應用到每個底層的時序上。如果查詢違反了閾值的話,就會發送警報。Cassandra 會儲存告警的狀態,在它的幫助下,worker 會維護一個狀態機,確保通知至少能夠成功傳送一次,如果告警持續觸發的話,它會定期重新發送通知,如果問題得到了緩解,告警將會變更為已解決的狀態。
閾值有兩種型別:靜態閾值和異常閾值。對於具有特定穩定狀態的指標,或者是可以通過構建查詢返回一致值(藉助一定的值計算,比如成功 / 失敗的百分比)的指標,我們通常會使用靜態閾值。對於週期性的指標,如每個城市的出行次數和其他業務指標,uMonitor 會利用我們的異常檢測平臺 Argos 基於歷史資料生成動態閾值,這個動態閾值代表了異常的度量指標值。
使用 Neris 進行主機告警
Neris 是我們內部基於主機的告警系統,它針對的是高密度、高基數的主機度量指標,在 M3 中,這是並未實現的。主機度量指標並未存放到 M3 中有兩個主要原因。首先,在每個資料中心的 40000 臺主機上檢查每分鐘生成的 150 萬個主機度量指標要比查詢中心化的度量指標儲存更高效。在前者的方案中,攝取和儲存度量指標的開銷就能避免了。其次,直到最近,M3 的資料保留策略會導致每 10 秒鐘的指標要儲存 48 個小時,每分鐘的指標會儲存 30 天,對於高密度的主機指標來說,這樣的保留策略是沒有必要的。鑑於 Nagios 需要為每項檢查編寫程式碼,並且需要單獨部署,這樣無法隨著我們的基礎設施增長進行擴充套件,所以我們決定自行構建一個系統。
Neris 會有一個代理(agent),執行在我們資料中心的每個主機上,它會定期(每分鐘)針對主機本身執行告警檢查。代理會將檢查結果傳送到一個聚合層,這個聚合層會將聚合後的結果傳送給 Origami。Origami 會基於規則確定要將哪些告警傳送出去,這個規則會檢視故障告警的數量以及底層告警的緊急程度來進行判斷。藉助 Origami,Neris 能夠在每個資料中心的主機群中每分鐘執行大約 150 萬次檢查。
當代理在主機上啟動時,Neris 會從一箇中央配置儲存中拉取關於該主機的告警定義資訊,這個中央配置名為 Object Config,它廣泛應用於 Uber 的低層級基礎設施服務中。給定主機要執行哪些告警取決於它的角色。例如,執行 Cassandra 的主機應該檢查 Cassandra 的狀態、磁碟使用以及其他度量指標。大多數這種主機級別的檢查是由基礎設施平臺團隊建立和維護的。
處理基數
對於我們的告警系統來說,高基數一直是最大的挑戰。在傳統做法中,可以執行一個告警查詢並返回多個序列,然後可以針對該告警執行簡單的規則,如果違反閾值的序列超出了特定的百分比,就會發送該告警資訊。uMonitor 還允許使用者基於其他告警的結果而傳送告警。假設一個告警所跟蹤的範圍依賴於一個範圍更大的告警,如果範圍更大的告警已經發出的話,依賴它的告警就會被抑制。
如果查詢只返回數量有限的序列,那麼上述技術能夠執行地非常好,依賴也能很容易地進行定義。但是,Uber 隨著增長,需要運維跨數百個城市的眾多產品線,基數方面所面臨的挑戰需要一個更加通用的解決方案。我們使用 Origami 來幫助我們處理高基數方面的問題。Neris 使用 Origami 作為其主要的資料去重和通知引擎,它為 uMonito 告警實現了統一的通知。
對於業務指標,當我們希望為每個城市、每個產品、每個版本的應用提供告警時,Origami 就非常有用了。Origami 允許使用者為城市、產品和應用程式版本組合建立底層的告警 / 檢查,並根據聚合策略發出告警,以便於接收基於每個城市 / 產品 / 應用程式版本的通知。在出現更大規模的停機情況時 (例如,當許多城市同時出現問題時),Origami 將傳送累積的通知,它代表了底層告警的觸發列表。
在主機告警的場景中,Origami 能夠根據聚合的不同狀態傳送不同嚴重程度的通知。以 Cassandra 叢集的磁碟使用為例,在這個場景下,Origami 的通知策略可能會像如下所示:
- 如果三個以下的主機磁碟使用率為 70%,使用 Email 進行通知;
- 如果三個以上的機磁碟使用率為 70%,使用簡訊(Send A Page,通過網路傳送文字通知 - 譯註)進行通知;
- 如果有一個或更多的主機磁碟使用率達到了 90%,使用簡訊進行通知。
告警通知
在擴充套件我們的告警系統時,有用的警報資訊是最大的挑戰。告警操作一般會從通知開始,比如針對高優先順序的問題,為值班工程師傳送簡訊,對於資訊級別的問題,給他們傳送郵件或進行線上聊天工具通知。我們現在的焦點工作已經轉移到為這些問題構建緩解操作。大多數故障和中斷都是由於配置更改或部署而引發的。在緩解操作方面,uMonitor 為回滾最近的配置和部署環境提供了良好的支援。對於具備更復雜緩解操作的團隊來說,我們會支援 webhook 的方式,它會針對某個端點發起一個 POST 呼叫,在呼叫中會包含告警的完整上下文資訊,在這個請求處理中可以執行緩解問題的操作。除此之外,通過使用 Origami 中的去重管道,我們可以在出現更大範圍的停機狀況時,抑制粒度更細的通知。
除了以上提到的功能,我們一直在努力使通知更加具有相關性,也就是讓它們針對最合適的個人。最近的一項工作涉及到識別配置 / 部署的更改者,並在他們所修改的服務出現告警時,直接聯絡到這些修改人。通過將 Jaeger 的跟蹤資訊與告警資訊相結合,我們能夠在為出現故障的服務傳送告警通知時獲取更多的上下文資訊。
告警管理
如前文所述,我們一直致力於將 uMonitor 打造成為一個平臺,讓其他的團隊都能根據特定的使用場景基於它來進行構建。主機告警的設定和管理通常是比較專業化的,由維護專屬硬體的團隊和為公司構建基礎設施平臺的團隊來進行管理,包括儲存、度量指標以及計算解決方案。告警是在團隊的 Git 倉庫中進行配置的,它們會同步到 Object Config 中。
如果從較高的級別來進行區分,uMonitor 有三種類型的告警:
- 針對所有的服務,根據 CPU、磁碟使用情況和 PRC 統計資料等標準度量指標自動生成的告警;
- 通過 UI 建立的一次性告警,主要用來探查特定的問題;
- 通過指令碼或基於 uMonitor 的外部配置系統建立和管理的告警。
我們看到,增長最快的是最後一種告警,因為團隊都致力於在最細的粒度探查可告警的問題。對這種粒度的需求來源於 Uber 的全球化增長。對於支撐 Uber 移動應用的服務來說,程式碼變更通常只涉及特定的一組城市,並且只會在幾個小時內有效。所以,非常重要的一點在於,我們需要在城市級別監控平臺的健康狀況,從而能夠在問題大範圍擴散之前將其定位出來。除此之外,每個城市的配置引數都是不同的,這些配置是由工程團隊和當地的運維團隊所控制的。例如,遊行或其他的事件會導致交通狀況的變化,騎行者在街道上可能被攔阻。
很多團隊都基於 uMonitor 構建了告警生成方案以解決此類問題。這些工具所解決的挑戰包括:
- 跨不同維度,迭代生成告警;
- 根據特定的業務資訊確定告警的時間安排,比如特定國家或城市的節假日,要將這些資訊配置在 uMonitor 中,以避免出現虛假告警;
- 在靜態或當前異常閾值都無法發揮作用的情況下,根據過去的資料或底層度量資料的複雜查詢來確定閾值,這些閾值會用到特定的業務線上(關於告警查詢,參見下文的介紹)。
除此之外,很多這樣的方案都會生成儀表盤,它們會與生成告警保持同步。
uMonitor 還提供了 UI,用於編輯告警和展現根本原因。UI 化的編輯和實驗性功能非常重要,因為指標具有變化性和峰值,所以大多數的指標不能原樣作為告警進行傳送。可觀察團隊提供瞭如何建立查詢更適合作為告警的指南。
告警查詢
Graphite 查詢語言和 M3QL 提供了大量的功能,以便於支援更加可定製化的方案。如下是一些樣例,列出瞭如何讓查詢返回更加一致的值,從而能夠讓度量指標更加具備告警的能力:
- 告警要反映幾分鐘的移動平均值(movingAverage),以消除指標峰值的影響;
- 將上述操作與一定的持續時間結合起來,只有當在一定的時間內持續違反閾值的時候,才會傳送通知;
- 對於具有上升和下降模式的指標,使用導數函式,確保兩個方向的峰值都不會變化太劇烈;
- 針對百分比或比例進行告警,這樣度量指標就不會受到量級變化影響。
未來計劃
在擴充套件系統時,我們剛剛使其具備檢測分鐘級問題的能力,並且能夠做到只為使用者顯示恰當的資訊,抑制掉不必要的告警。我們正在致力於將該功能應用於管道的各個組成部分中,包括更有效地收集度量指標、擴充套件性以及流程化告警執行的基礎設施,並構建跨各種資源協調資訊的 UI 和介面。