Prometheus, Alertmanager & Grafana 架構介詔¶
原文: Kubernetes Observability – Part II: architecture introduction to Prometheus, Alertmanager & Grafana
本文詳細介紹了 Prometheus 堆棧的架構,由各個應用程序 Prometheus、Alertmanager、Grafana、Pushgateway 和各種 Exporter 組成。本文會討論了每個組件是如何配置的,它存儲了哪種數據,以及如何擴展 Prometheus。
Prometheus 簡介¶
Prometheus 堆棧是一組流行的工具,用於實現系統的可觀察性。正如本可觀察性文章系列第一部分概述的學習指南中所述,第一步(在技術軌道上)是了解此堆棧的架構,以及不同工具如何組合在一起。本文詳細介紹了架構,解釋了其背後的基本原理,如何配置各個組件,還解釋了 Prometheus 如何擴展。
架構 high-level 概述¶
Prometheus 堆棧由許多獨立的程序/應用程序組成,它們都有專門的職責。它們都通過 HTTP 相互通信以實現它們的總體目標:(可能是大規模的)分佈式系統的可觀察性。 Prometheus 背後的團隊選擇了這種關注點分離的方法,以便每個程序都可以單獨配置和部署(如果需要,可以在不同的機器上)。它還降低了每個單獨程序的複雜性。先看一下官方文檔的 Prometheus 架構圖:
我們的核心是 Prometheus 服務器,它有幾個子系統,最值得注意的是 TSDB、檢索組件和 HTTP 服務器。 TSDB 是一個持久的、基於磁盤的時序數據庫。檢索組件定期從公開這些指標(通過 HTTP)的其他應用程序中提取“指標數據”,例如每 15 秒,並將它們存儲在 TSDB 中。這種有規律的拉動也稱為“刮取(scrape)”。 Prometheus HTTP 服務器提供了一個 REST 端點,允許其他系統(例如 Grafana)查詢存儲的指標數據。
什麼是指標 (Metrics)
一般而言,指標是提供有關特定服務(例如進程)或系統(例如服務器節點)的信息的數字數據。一個具體的例子可以是節點的磁盤使用量,或者對 HTTP 服務器(或特定 HTTP 路徑)的請求總數。指標標準存儲在 TSDB 中,可以有效地壓縮、處理和檢索它們。
在 Prometheus 中,指標的格式很簡單:<name>{label1=value1,label2=value2,...} <float64 value>
一個具體的例子:prometheus_target_metadata_cache_bytes{scrape_job="alertmanager"} 5672
在 Prometheus 中,指標是由 Exporter 等應用程序創建的(有關更多詳細信息,請參見下文)。指標通過 HTTP 以純文本形式公開,通常在 /metric
端點上,每行一個指標。如您所見,輸出中沒有時間戳,因為 Prometheus 期望返回的指標反映當前狀態,而 Prometheus 在將抓取的數據存儲到其 TSDB 時會添加時間戳。
指標從何而來?公開暴露指標的應用程序有兩種基本類型:
- Your own application,您為此實施了一個提供這些指標的
/metrics
HTTP 端點。 Prometheus 社區為所有常見的編程語言構建了簡化此任務的 SDK。 - A third-party exporter。導出器(exporter)是一個應用程序,它使用該系統的專有 API 從第三方系統(例如數據庫服務器或主機的操作系統內核)中抓取指標,並將獲得的指標轉換為 Prometheus 指標格式,並將它們暴露在
/metrics
端點。第三方系統本身並不知道它正在被觀察。有數百個即用型 exporter,請參見此處和此處!最著名的是節點導出器(node exporter),它從計算節點中提取指標,例如內存、存儲、網絡和 CPU 的使用情況。另一個重要的是 cAdvisor,它導出了運行 Docker 容器的資源使用情況和性能特徵。
回到“刮取(scrape)”的設計。在每個抓取循環結束時,Prometheus 會根據目前存儲的指標評估警報規則。您使用 Prometheus 的查詢語言 PromQL 編寫這些警報規則。警報規則的示例可能是“任何節點上的任何磁盤的磁盤使用率已超過 90%”。如果觸發了任何配置的警報規則,Prometheus 會生成警報並將其推送到 Alertmanager 應用程序。
請注意,警報只是一個 (JSON) 對象,尚未發送任何電子郵件、尋呼機消息等!當 Alertmanager 收到警報時,它會將其存儲在其內部數據庫中。 Alertmanager 使用您提供的配置對警報進行重複數據刪除和分組(以減少垃圾郵件)。然後它會生成警報通知,即將警報路由到正確的接收者,例如電子郵件或 PagerDuty。
架構圖中還有兩個方框需要特別說明:
-
Pushgateway 和 short-lived jobs:如果您希望 Prometheus 收集生命週期非常短(比 Prometheus 的抓取間隔短)的應用程序或作業(例如 cron 作業)的指標,那麼您會遇到問題:當 Prometheus 嘗試抓取應用程序(及其 /metrics HTTP 端點)時,它可能不存在。為了規避這個問題,您可以運行 Pushgateway 之類的工具,這是一個永久運行的指標緩存,短期作業可以將指標推送到該緩存。然後,Prometheus 從 Pushgateway 的
/metrics
端點抓取這些緩存的指標。 -
Service discovery:Prometheus 是為監控大型水平擴展分佈式系統而構建的。它們由許多(計算)節點和服務實例組成,它們一直來來去去,例如由於彈性縮放。對於這樣的系統,手動配置 Prometheus 的檢索組件應該抓取的主機/目標的靜態列表將是一個難以管理的負擔。 Prometheus 的服務發現(SD)機制實現了一種動態發現機制,已經有很多具體的實現,可以動態發現抓取目標。例如,SD 可以檢測 Kubernetes 或 Consul 中的服務和節點(有關實現列表,請參閱官方文檔中的 ..._sd_config)。
它應該可以幫助您了解不同應用程序的子組件。讓我們再看一下數據流:
- Prometheus 的檢索組件定期從應用程序、導出器甚至 Prometheus 自己導出的指標提供的 /metric 端點輪詢指標。這些指標存儲在 Prometheus 的(嵌入式)TSDB 中,當前的抓取時間戳被添加到數據中。
- 每當 Prometheus 的 Alert 規則評估器組件檢測到其配置的某些警報規則被觸發(它通過查詢 TSDB 來確定)時,它就會生成一個警報對象並將其發送給 Alertmanager。
- Alertmanager 將收到的警報對象存儲在它自己的(嵌入式)數據庫中,同時進行重複數據刪除。
- Alertmanager 的 Notification 管道定期評估存儲在數據庫中的警報(根據配置的警報路由樹,下面有更多詳細信息),並在適用時向配置的接收器發送警報通知。 “接收者”不是指“人”,而是實際通知人們的系統(電子郵件、Slack 等)
配置¶
每個應用程序(Prometheus、Alertmanager、Grafana、Exporters)都有不同的配置方法,如下表所示:
Component | Configuration approach | Persisted stateful data |
---|---|---|
Prometheus | Stateless (files) | Metrics |
Alertmanager | Stateless (files) | Alerts, silences |
Grafana | Stateful + Stateless (provisioning mechanism) | Grafana configuration |
Exporter | Usually stateless (files), depends on exporter | — |
每一個欄列的含義:
- Configuration approach:無狀態意味著應用程序不會(有狀態地)在內部存儲任何配置值,例如在數據庫中。相反,配置通常通過文件提供(例如格式化為 YAML 或 JSON)。這些文件應受版本控制,例如在 Git 中。
- 無狀態方法的優點是:
- 您始終在這些文件中明確更改配置(單一事實來源)
- 將應用程序的實例從一個節點移動到另一個節點更容易,因為只需要掛載幾個文件(而不是必須提取和傳輸狀態)
- 對配置所做的更改是可追溯的(通過 Git)
- 配置文件是人和機器可讀的
- 無狀態方法的缺點是:
- 您不能動態調整配置,例如通過 API 調用
- 與 GUI 相比,通過文本更改配置不太方便且容易出錯
- 無狀態方法的優點是:
- Persisted stateful data:指示應用程序是否有內部(有狀態)數據存儲,如果有,其中存儲了哪種數據。
讓我們更仔細地來了解每一個元件。
Prometheus¶
Prometheus 對於收集的指標(存儲在 TSDB 中)是有狀態的,但對於配置是無狀態的。因此,您無法通過 Prometheus Web 界面或 API 更改配置。但是,有第三方項目,例如 prometheus-configmanager,通過提供配置 API 使這成為可能。
配置是通過多個文件完成的,最值得注意的是:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert-rules.yml"
scrape_configs:
- job_name: 'nodeexporter'
scrape_interval: 5s
static_configs:
- targets: [ 'nodeexporter:9100' ]
- job_name: 'cadvisor'
scrape_interval: 5s
static_configs:
- targets: [ 'cadvisor:8080' ]
- job_name: 'prometheus'
scrape_interval: 10s
static_configs:
- targets: [ 'localhost:9090' ]
alerting:
alertmanagers:
- scheme: http
static_configs:
- targets:
- 'alertmanager:9093'
-
global
section: 指定全局配置參數,例如抓取間隔。 -
rule_files
section: 指定要考慮的警報規則 yaml 文件名。 -
scrape_configs
section: 指定指標抓取作業的列表。每個作業都有一個名稱(這樣 prometheus.yml 中的 job_name: 'nodeexporter' 之類的東西會導致 Prometheus 在將抓取的指標存儲在其 TSDB 中時添加一個標籤,例如 job: 'nodeexporter')和一個或多個目標IP:端口+路徑的形式)。scrape_configs
數組中的典型條目是 Prometheus 服務器本身、Alertmanager、cAdvisor、nodeexporter、Pushgateway 和其他自定義指標 exporter。對於每個目標,您可以使用static_config
(您只需手動提供要查詢的主機、端口和指標 URL),或使用動態服務發現 (service discovery) 選項之一,例如 kubernetes_sd_config 或 openstack_sd_config。 -
alerting
seciont: 指定向哪個 Alertmanager 實例發送警報。
groups:
- name: group1
rules:
- alert: alert-name-1
expr: up == 0
for: 30s
labels: # labels to attach to alerts
severity: critical
annotations:
summary: "Some monitoring-related service is non-operational"
description: "Service {{ $labels.instance }} ({{ $labels.job }}) is down."
- name: group2
rules:
- alert: SomeHttpServicesAreDown
expr: probe_success == 0
for: 10m
labels:
severity: critical
annotations:
summary: "Some HTTP service is non-operational"
description: "Service {{ $labels.instance }} ({{ $labels.job }}) is down."
-
您可以在此處定義記錄規則或警報規則。
- 記錄規則允許您預先計算您經常需要的計算量大的指標數據(來自傳入的原始指標),並將計算結果保存為一組新的指標。這對於每次刷新時重複查詢相同(計算成本高)指標數據的儀表板軟件(例如 Grafana)很有用。
- 警報規則是告訴 Prometheus 何時應該向 Alertmanager 發送警報的規則。
-
alert-rules.yml
文件以groups:
部分開頭,您可以在其中指定一個或多個組。對於每個組,您定義一個或多個規則。除了這些規則之外,您還可以給每個組一個名稱和一個可選的自定義間隔(評估發生的時間)。您可以認為每個組都由一個單獨的線程並行執行。組內的規則都按順序進行評估。 - 對於每個警報規則,您定義一個名稱(警報:foo)、一個 PromQL 表達式、持續時間(以過濾嘈雜的警報,例如:30 秒),並可選地添加 Prometheus 將在警報觸發時附加到警報的標籤或註釋:
- 標籤可以例如包含嚴重性級別
- 註釋通常包括摘要和描述。
- 您可以在標籤和註釋中使用變量(模板)
Alertmanager¶
與 Prometheus 一樣,Alertmanager 是部分有狀態的(存儲收到的警報和靜默),但也部分是無狀態的,因為它也是使用文件配置的。
global:
smtp_smarthost: smtp.example.org:587
route:
receiver: 'default-receiver'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
group_by: [ cluster, alertname ]
# Note: this root "route" node may not have any "matchers"! All incoming alerts must match the root
routes:
- receiver: 'database-pager'
group_wait: 10s
matchers:
- service=~"mysql|cassandra"
- receiver: 'frontend-pager'
group_by: [ product, environment ]
matchers:
- team="frontend"
receivers:
- name: 'default-receiver'
email_configs:
- send_resolved: true
to: some.receiver@mail.com
from: no-reply@host.com
- name: 'database-pager'
...
- name: 'frontend-pager'
...
inhibit_rules:
[ - <inhibit_rule> ... ]
global
設置,例如特定於接收器的全局值(用於消除配置值的冗餘,否則必須將其複製到 config.yml 文件中的不同位置)receivers
配置接收者(例如 SMTP 服務或網絡掛鉤)及其憑據route
:路由是遞歸的數據結構,從而形成路由樹。基本思想是警報沿著警報路由樹遍歷,基於匹配收到警報的標籤的匹配器。如果警報不匹配路由節點的任何子節點(因為沒有子節點的匹配器匹配,或者因為沒有子節點),則警報由當前節點處理。通常,路由節點從其父節點繼承值。每個路由節點都有以下配置參數:- 匹配器:只有具有指定標籤/值(或值的正則表達式)的警報才會繼續沿著樹的這個分支
- group_by:用於對警報進行分組的標籤鍵列表。 Alertmanager 然後為每個標籤值創建一個組。這樣可以避免您的開發人員/操作員收到警報通知的垃圾郵件。
- 各種等待期或間隔,以減少發送警報通知的數量
- receiver:應向其發送警報通知的接收者的名稱
- routes:路線子對象的數組
- inhibit_rules:禁止規則對像數組。允許靜音一組警報,因為另一個警報已經觸發。這減少了警報(通知)垃圾郵件。例如,如果您檢測到整個節點已關閉,則不需要為該節點上運行的 10 個服務(因此也已關閉)再發送 10 個警報通知。
Grafana¶
Grafana 不存儲任何指標數據,但總是在運行時從 Prometheus 檢索它。然而,Grafana 的配置(例如數據源、用戶和身份驗證或儀表板)是有狀態地存儲在 Grafana 的內部數據庫中。它們可以通過 Grafana 的 REST API 或其 Web 界面進行操作。但是,Grafana 也有一個配置機制,允許您在 Grafana 啟動時以及定期(每當 Grafana 檢測到文件內容髮生更改時)通過文件覆蓋內部存儲的配置。
Exporters¶
因為 Prometheus 期望 Exporter 僅在抓取時公開應用程序的指標數據(換句話說,Prometheus 不期望歷史指標數據),所以 Exporter 通常是完全無狀態的。
配置方法高度依賴於應用程序和 Exporter。一些 Exporter 不需要任何配置,但最典型的是,無狀態 CLI 參數和/或環境變量用於配置 Exporter。
拉式監控架構的注意事項¶
Prometheus 使用基於拉取的方法,其中 Prometheus 服務器從端點收集/拉取指標,Prometheus 必須通過靜態和動態服務發現來確定哪些列表。其他系統,例如 Graphite,則使用基於推送的方法,其中代理需要了解收集器,並將度量數據推送給它。
如果您搜索“推送與拉取監控”,您會發現關於哪個“更好”的看法截然不同。這些文章中的大多數都是由工具製造商撰寫的,然後他們證明了為什麼他們的方法是“略勝一籌”的方法,因為他們應用了一些優化,或者因為他們能夠揭穿一些關於他們的方法不好的神話,等等。最後,基於推和拉的系統都有缺點,但(缺點)的嚴重程度取決於您的具體用例。
對於任何監控系統,您都需要處理以下詳細信息:
- 延遲:“已知度量數據點”和“監控系統已處理它”之間的延遲。一些作者認為基於推送的系統提供更低的延遲,因此優於基於拉取的系統。他們很容易忘記提到節省這幾秒鐘在實踐中是無關緊要的,因為:
- 您的團隊將花費幾分鐘甚至幾小時來實際診斷和解決問題,
- 您通常在服務器/收集器上應用積極的滑動窗口平均(以消除異常值和誤報),窗口大小遠大於使用基於推送的系統減少的延遲。
- 發現:讓代理實例了解收集器/服務器實例(反之亦然)總是涉及復雜性。天下沒有免費的午餐…
- 安全性:易於實施機制(及其安全程度),確保沒有未經授權的一方可以訪問(代理或收集器的)指標數據
- 可擴展性:一旦你有許多代理產生大量數據,這兩種方法都不能很好地工作,只使用一個收集器服務器。大多數實現都有用於擴展的解決方案。有關 Prometheus 的擴展方法,請參見下文。
- 維運複雜度:涉及防火牆配置,以及更改整個監控系統配置的難易程度
在實踐中,您可能會使用任何一種方法都做得很好。從架構上講,您可以混合和匹配拉式與基於推式的方法。例如,Pushgateway 正在將推送轉換為拉取,而 Telegraf 等應用程序可以將拉取轉換為推送。
Prometheus 的基於拉的方法確實有兩個缺點(維運複雜度),我想解決這些缺點:
- 監控多進程應用程序:如果您使用多個進程,則從應用程序的
/metrics
端點提取指標具有挑戰性。原因是指標數據通常只保存在內存中,分別針對每個進程,並且不清楚您的哪些進程實際上為 /metrics 端點提供服務,以及指標數據是如何累積的。假設您正在構建一個 Python Web 應用程序(例如,使用 Django 或 Flask)並使用 WSGI 應用程序服務器來實現可伸縮性,例如 Gunicorn 或 uWSGI,它們運行多個工作進程:- 解決方案#1:讓每個進程收集自己的指標,每當調用 /metrics 時,Prometheus 客戶端庫使用一些進程間通信來收集來自所有進程的聚合指標(例如,使用引擎蓋下的文件)。有關實現示例,請參閱 Prometheus 的 Python 庫的多進程模式。
- 解決方案#2:將每個工作進程視為一個單獨的應用程序,即每個工作進程都被稱為 Prometheus 的單獨目標,每個工作進程都有自己的 /metrics 端點,例如在不同的端口下。指標的聚合在查詢時在服務器上的 PromQL 中延遲和執行。有關示例,請參見此處。
- 解決方案#3:讓工作人員將指標推送到聚合器(例如 Pushgateway 或 statsd),Prometheus 從中提取聚合的指標。參見例如這裡。
- 監控短暫的進程:進程(例如備份腳本)的存活時間可能不夠長,無法為指標抓取提供服務。在這裡,只有基於推送的方法才有意義。解決方案是混合基於推和拉的方法,並使用類似推送網關的東西(參見上面的解決方案方法#3)。
結論¶
Prometheus 堆棧是一個複雜的系統。在我看來,將堆棧分成多個應用程序(每個應用程序都有一個單獨的關注點)是一種很好的方法。您將很快習慣這影響組件配置的方式,了解配置的哪個部分屬於哪個文件。您現在擁有許多(較小的)文件(存儲在版本控制中)這一事實有助於更大的團隊根據誰配置什麼來劃分職責。