一、基本概念
Prometheus 是由前 Google 工程師從 2012 年開始在 Soundcloud 以開源軟件的形式進行研發的系統監控和告警工具包,自此以后,許多公司和組織都采用了 Prometheus 作為監控告警工具。Prometheus 的開發者和用戶社區非常活躍,它現在是一個獨立的開源項目,可以獨立於任何公司進行維護。為了證明這一點,Prometheus 於 2016 年 5 月加入 CNCF 基金會,成為繼 Kubernetes 之后的第二個 CNCF 托管項目。
1.1 架構圖
Prometheus Server 直接從監控目標中或者間接通過推送網關來拉取監控指標,它在本地存儲所有抓取到的樣本數據,並對此數據執行一系列規則,以匯總和記錄現有數據的新時間序列或生成告警。可以通過 Grafana 或者其他工具來實現監控數據的可視化。
Prometheus的基本原理是通過HTTP協議周期性抓取被監控組件的狀態,任意組件只要提供對應的HTTP接口就可以接入監控。不需要任何SDK或者其他的集成過程。這樣做非常適合做虛擬化環境監控系統,比如VM、Docker、Kubernetes等。輸出被監控組件信息的HTTP接口被叫做exporter 。目前互聯網公司常用的組件大部分都有exporter可以直接使用,比如Varnish、Haproxy、Nginx、MySQL、Linux系統信息(包括磁盤、內存、CPU、網絡等等)。
在Prometheus中,每一個暴露監控樣本數據的HTTP服務稱為一個實例。例如在當前主機上運行的node exporter可以被稱為一個實例(Instance)。
1.2 組件
1.2.1 prometheus server
負責數據的收集和存儲,並且對外提供PromQL實現監控數據的查詢以及聚合分析。
1.2.2 Exporters
廣義上講所有可以向Prometheus提供監控樣本數據的程序都可以被稱為一個Exporter。而Exporter的一個實例稱為target。Exporter將監控數據采集的端點通過HTTP服務的形式暴露給Prometheus Server,Prometheus Server通過訪問該Exporter提供的Endpoint端點,即可獲取到需要采集的監控數據。
一般來說可以將Exporter分為2類:
直接采集:這一類Exporter直接內置了對Prometheus監控的支持,比如cAdvisor,Kubernetes,Etcd,Gokit等,都直接內置了用於向Prometheus暴露監控數據的端點。
間接采集:間接采集,原有監控目標並不直接支持Prometheus,因此我們需要通過Prometheus提供的Client Library編寫該監控目標的監控采集程序。例如: Mysql Exporter,JMX Exporter,Consul Exporter等。
從間接采集的Exporter的來源上來講,主要分為兩類:
- 社區提供
Prometheus社區提供了豐富的Exporter實現,涵蓋了從基礎設施,中間件以及網絡等各個方面的監控功能。這些Exporter可以實現大部分通用的監控需求。下表列舉一些社區中常用的Exporter:
范圍 |
常用Exporter |
數據庫 |
MySQL Exporter, Redis Exporter, MongoDB Exporter, MSSQL Exporter等 |
硬件 |
Apcupsd Exporter,IoT Edison Exporter, IPMI Exporter, Node Exporter等 |
消息隊列 |
Beanstalkd Exporter, Kafka Exporter, NSQ Exporter, RabbitMQ Exporter等 |
存儲 |
Ceph Exporter, Gluster Exporter, HDFS Exporter, ScaleIO Exporter等 |
HTTP服務 |
Apache Exporter, HAProxy Exporter, Nginx Exporter等 |
API服務 |
AWS ECS Exporter, Docker Cloud Exporter, Docker Hub Exporter, GitHub Exporter等 |
日志 |
Fluentd Exporter, Grok Exporter等 |
監控系統 |
Collectd Exporter, Graphite Exporter, InfluxDB Exporter, Nagios Exporter, SNMP Exporter等 |
其它 |
Blockbox Exporter, JIRA Exporter, Jenkins Exporter, Confluence Exporter等 |
- 用戶自定義
除了直接使用社區提供的Exporter程序以外,用戶還可以基於Prometheus提供的Client Library創建自己的Exporter程序,目前Promthues社區官方提供了對以下編程語言的支持:Go、Java/Scala、Python、Ruby。同時還有第三方實現的如:Bash、C++、Common Lisp、Erlang,、Haskeel、Lua、Node.js、PHP、Rust等。
1.2.3 client libraries
顧名思義,用來生成自定義的exporters的java庫。
1.2.3 push gateway
由於Prometheus數據采集基於Pull模型進行設計,因此在網絡環境的配置上必須要讓Prometheus Server能夠直接與Exporter進行通信。 當這種網絡需求無法直接滿足時(短周期或者臨時采集的樣本數據),就可以利用PushGateway來進行中轉。可以通過PushGateway將內部網絡的監控數據主動Push到Gateway當中。而Prometheus Server則可以采用同樣Pull的方式從PushGateway中獲取到監控數據。
推送數據的方式:
1、API 方式 Push 數據到 PushGateway
2、用 Client SDK Push 數據到 Pushgateway
1.2.4 Alertmanager
在Prometheus Server中支持基於PromQL創建告警規則,如果滿足PromQL定義的規則,則會產生一條告警,而告警的后續處理流程則由AlertManager進行管理。在AlertManager中我們可以與郵件,Slack等等內置的通知方式進行集成,也可以通過Webhook自定義告警處理方式。AlertManager即Prometheus體系中的告警處理中心。
1.2.5 various support tools
1.3 特性
1. 由指標名稱和和鍵/值對標簽標識的時間序列數據組成的多維數據模型。
2. 強大的查詢語言 PromQL。
3. 不依賴分布式存儲;單個服務節點具有自治能力。
4. 時間序列數據是服務端通過 HTTP 協議主動拉取獲得的。
5. 也可以通過中間網關來推送時間序列數據。
6. 可以通過靜態配置文件或服務發現來獲取監控目標。
7. 支持多種類型的圖表和儀表盤。
引自官網:
- a multi-dimensional data model with time series data identified by metric name and key/value pairs;
- PromQL, a flexible query language to leverage this dimensionality;
- no reliance on distributed storage; single server nodes are autonomous;
- time series collection happens via a pull model over HTTP;
- pushing time series is supported via an intermediary gateway;
- targets are discovered via service discovery or static configuration;
- multiple modes of graphing and dashboarding support.
1.4 數據類型
為了能夠幫助用戶理解和區分這些不同監控指標之間的差異,Prometheus定義了4中不同的指標類型(metric type):Counter(計數器)、Gauge(儀表盤)、Histogram(直方圖)、Summary(摘要)。Summary和Histogram的用法基本保持一致(主用用於統計和分析樣本的分布情況),區別在於Summary可以指定在客戶端統計的分位數。
1.5 適用范圍
- When does it fit?
Prometheus 適用於記錄文本格式的時間序列,它既適用於以機器為中心的監控,也適用於高度動態的面向服務架構的監控。在微服務的世界中,它對多維數據收集和查詢的支持有特殊優勢。
Prometheus 是專為提高系統可靠性而設計的,它可以在斷電期間快速診斷問題,每個 Prometheus Server 都是相互獨立的,不依賴於網絡存儲或其他遠程服務。當基礎架構出現故障時,你可以通過 Prometheus 快速定位故障點,而且不會消耗大量的基礎架構資源。
- When does it not fit?
Prometheus 非常重視可靠性,即使在出現故障的情況下,你也可以隨時查看有關系統的可用統計信息。如果你需要百分之百的准確度,例如按請求數量計費,那么 Prometheus 不太適合你,因為它收集的數據可能不夠詳細完整。這種情況下,你最好使用其他系統來收集和分析數據以進行計費,並使用 Prometheus 來監控系統的其余部分。
1.6 配置文件
1 # my global config 2 3 global: 4 5 scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 6 7 evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 8 9 # scrape_timeout is set to the global default (10s). 10 11 # Alertmanager configuration 12 13 alerting: 14 15 alertmanagers: 16 17 - static_configs: 18 19 - targets: 20 21 # - alertmanager:9093 22 23 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 24 25 rule_files: 26 27 # - "first_rules.yml" 28 29 # - "second_rules.yml" 30 31 # A scrape configuration containing exactly one endpoint to scrape: 32 33 # Here it's Prometheus itself. 34 35 scrape_configs: 36 37 # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config. 38 39 - job_name: 'prometheus' 40 41 # metrics_path defaults to '/metrics' 42 43 # scheme defaults to 'http' 44 45 static_configs: 46 47 - targets: ['localhost:9090']
其中scrape_config包含一個或多個job(即job_name),每一個Job可以對應多個Instance,即配置文件中的targets. 通過Prometheus UI可以更直觀的看到其中的關系。
二、存儲
2.1 本地存儲
Prometheus 2.x 采用自定義的存儲格式將樣本數據保存在本地磁盤當中。如下所示,按照兩個小時(最少時間)為一個時間窗口,將兩小時內產生的數據存儲在一個塊(Block)中,每一個塊中包含該時間窗口內的所有樣本數據(chunks),元數據文件(meta.json)以及索引文件(index)。
最新寫入的數據保存在內存block中,達到2小時后寫入磁盤。為了防止程序崩潰導致數據丟失,實現了WAL(write-ahead-log)機制,啟動時會以寫入日志(WAL)的方式來實現重播,從而恢復數據。
在文件系統中這些塊保存在單獨的目錄當中,Prometheus保存塊數據的目錄結構如下所示:如上所示,Prometheus 2.x采用自定義的存儲格式將樣本數據保存在本地磁盤當中。按照兩個小時為一個時間窗口,將兩小時內產生的數據存儲在一個塊(Block)中,每一個塊中包含該時間窗口內的所有樣本數據(chunks),元數據文件(meta.json)以及索引文件(index)。
當前時間窗口內正在收集的樣本數據,Prometheus則會直接將數據保存在內存當中。為了確保此期間如果Prometheus發生崩潰或者重啟時能夠恢復數據,Prometheus啟動時會從寫入日志(WAL)進行重播,從而恢復數據。此期間如果通過API刪除時間序列,刪除記錄也會保存在單獨的邏輯文件當中(tombstone)。
./data |- 01BKGV7JBM69T2G1BGBGM6KB12 # 塊 |- meta.json # 元數據 |- wal # 寫入日志 |- 000002 |- 000001 |- 01BKGTZQ1SYQJTR4PB43C8PD98 # 塊 |- meta.json #元數據 |- index # 索引文件 |- chunks # 樣本數據 |- 000001 |- tombstones # 邏輯數據 |- 01BKGTZQ1HHWHV8FBJXW1Y3W0K |- meta.json |- wal |-00000
通過時間窗口的形式保存所有的樣本數據,可以明顯提高Prometheus的查詢效率,當查詢一段時間范圍內的所有樣本數據時,只需要簡單的從落在該范圍內的塊中查詢數據即可。而對於歷史數據的刪除,也變得非常簡單,只要刪除相應塊所在的目錄即可。
對於單節點的Prometheus而言,這種基於本地文件系統的存儲方式能夠讓其支持數以百萬的監控指標,每秒處理數十萬的數據點。為了保持自身管理和部署的簡單性,Prometheus放棄了管理HA的復雜度。
2.2 遠程存儲
Prometheus的本地存儲設計可以減少其自身運維和管理的復雜度,同時能夠滿足大部分用戶監控規模的需求。但是本地存儲也意味着Prometheus無法持久化數據,無法存儲大量歷史數據,同時也無法靈活擴展和遷移。
為了保持Prometheus的簡單性,Prometheus並沒有嘗試在自身中解決以上問題,而是通過定義兩個標准接口(remote_write/remote_read),讓用戶可以基於這兩個接口對接將數據保存到任意第三方的存儲服務中,這種方式在Promthues中稱為Remote Storage。
2.2.1 Remote Write
用戶可以在Prometheus配置文件中指定Remote Write(遠程寫)的URL地址,一旦設置了該配置項,Prometheus將采集到的樣本數據通過HTTP的形式發送給適配器(Adaptor)。而用戶則可以在適配器中對接外部任意的服務。外部服務可以是真正的存儲系統,公有雲的存儲服務,也可以是消息隊列等任意形式。
2.2.2 Remote Read
如下圖所示,Promthues的Remote Read(遠程讀)也通過了一個適配器實現。在遠程讀的流程當中,當用戶發起查詢請求后,Promthues將向remote_read中配置的URL發起查詢請求(matchers,ranges),Adaptor根據請求條件從第三方存儲服務中獲取響應的數據。同時將數據轉換為Promthues的原始樣本數據返回給Prometheus Server。
當獲取到樣本數據后,Promthues在本地使用PromQL對樣本數據進行二次處理。
三、集群與高可用
3.1 聯邦集群
如下圖所示,在每個數據中心部署單獨的Prometheus Server,用於采集當前數據中心監控數據。並由一個中心的Prometheus Server負責聚合多個數據中心的監控數據。這一特性在Promthues中稱為聯邦集群。
3.2 Prometheus高可用方案
3.2.1基本HA:服務可用性
基本的HA模式只能確保Promthues服務的可用性問題,但是不解決Prometheus Server之間的數據一致性問題以及持久化問題(數據丟失后無法恢復),也無法進行動態的擴展。因此這種部署方式適合監控規模不大,Promthues Server也不會頻繁發生遷移的情況,並且只需要保存短周期監控數據的場景。
3.2.2 基本HA + 遠程存儲
在解決了Promthues服務可用性的基礎上,同時確保了數據的持久化,當Promthues Server發生宕機或者數據丟失的情況下,可以快速的恢復。 同時Promthues Server可能很好的進行遷移。因此,該方案適用於用戶監控規模不大,但是希望能夠將監控數據持久化,同時能夠確保Promthues Server的可遷移性的場景。
3.2.3 基本HA + 遠程存儲 + 聯邦集群
這種部署方式一般適用於兩種場景:
場景一:單數據中心 + 大量的采集任務
這種場景下Promthues的性能瓶頸主要在於大量的采集任務,因此用戶需要利用Prometheus聯邦集群的特性,將不同類型的采集任務划分到不同的Promthues子服務中,從而實現功能分區。例如一個Promthues Server負責采集基礎設施相關的監控指標,另外一個Prometheus Server負責采集應用監控指標。再有上層Prometheus Server實現對數據的匯聚。
場景二:多數據中心
這種模式也適合與多數據中心的情況,當Promthues Server無法直接與數據中心中的Exporter進行通訊時,在每一個數據中部署一個單獨的Promthues Server負責當前數據中心的采集任務是一個不錯的方式。這樣可以避免用戶進行大量的網絡配置,只需要確保主Promthues Server實例能夠與當前數據中心的Prometheus Server通訊即可。 中心Promthues Server負責實現對多數據中心數據的聚合。
3.2.4 按照實例進行功能分區
這時在考慮另外一種極端情況,即單個采集任務的Target數也變得非常巨大。這時簡單通過聯邦集群進行功能分區,Prometheus Server也無法有效處理時。這種情況只能考慮繼續在實例級別進行功能划分。
如上圖所示,將統一任務的不同實例的監控數據采集任務划分到不同的Prometheus實例。通過relabel設置,我們可以確保當前Prometheus Server只收集當前采集任務的一部分實例的監控指標。
3.3 高可用方案選擇
上面的部分,根據不同的場景演示了3種不同的高可用部署方案。當然對於Promthues部署方案需要用戶根據監控規模以及自身的需求進行動態調整,下表展示了Promthues和高可用有關3個選項各自解決的問題,用戶可以根據自己的需求靈活選擇。
選項\需求 |
服務可用性 |
數據持久化 |
水平擴展 |
主備HA |
v |
x |
x |
遠程存儲 |
x |
v |
x |
聯邦集群 |
x |
x |
v |
四、服務發現
4.1 Prometheus與服務發現
雲原生、容器場景下按需的資源使用方式對於監控系統而言就意味着沒有了一個固定的監控目標,所有的監控對象(基礎設施、應用、服務)都在動態的變化,這對基於Push模式傳統監控軟件帶來挑戰。
對於Prometheus這一類基於Pull模式的監控系統,顯然也無法繼續使用的static_configs的方式靜態的定義監控目標。而對於Prometheus而言其解決方案就是引入一個中間的代理人(服務注冊中心),這個代理人掌握着當前所有監控目標的訪問信息,Prometheus只需要向這個代理人詢問有哪些監控目標控即可, 這種模式被稱為服務發現。
4.2基於文件的服務發現通過服務發現的方式,管理員可以在不重啟Prometheus服務的情況下動態的發現需要監控的Target實例信息。
用戶可以通過JSON或者YAML格式的文件,定義所有的監控目標。例如,在下面的JSON文件中分別定義了3個采集任務,以及每個任務對應的Target列表:
[
{
"targets": [ "localhost:8080"],
"labels": {
"env": "localhost",
"job": "cadvisor"
}
},
{
"targets": [ "localhost:9104" ],
"labels": {
"env": "prod",
"job": "mysqld"
}
},
{
"targets": [ "localhost:9100"],
"labels": {
"env": "prod",
"job": "node"
}
}
]
創建Prometheus配置文件/etc/prometheus/prometheus-file-sd.yml,並添加以下內容:
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 15s
scrape_configs:
- job_name: 'file_ds'
file_sd_configs:
- refresh_interval: 1m
files:
- targets.json
通過這種方式,Prometheus會自動的周期性讀取文件中的內容。當文件中定義的內容發生變化時,不需要對Prometheus進行任何的重啟操作。
4.3基於Consul的服務發現
Consul是由HashiCorp開發的一個支持多數據中心的分布式服務發現和鍵值對存儲服務的開源軟件,被大量應用於基於微服務的軟件架構當中。
Consul作為一個通用的服務發現和注冊中心,記錄並且管理了環境中所有服務的信息。Prometheus通過與Consul的交互可以獲取到相應Exporter實例的訪問信息。在Prometheus的配置文件當可以通過以下方式與Consul進行集成:
- job_name: node_exporter
metrics_path: /metrics
scheme: http
consul_sd_configs:
- server: localhost:8500 #指定了consul的訪問地址
services: #為注冊到consul中的實例信息
- node_exporter
- cadvisor
在consul_sd_configs定義當中通過server定義了Consul服務的訪問地址,services則定義了當前需要發現哪些類型服務實例的信息,這里限定了只獲取node_exporter和cadvisor的服務實例信息。
4.4服務發現與Relabel
如何過濾選擇Target實例?relabel
目前為止,只要是注冊到Consul上的Node Exporter或者cAdvisor實例是可以自動添加到Prometheus的Target當中。現在請考慮下面的場景:
對於線上環境我們可能會划分為:dev, stage, prod不同的集群。每一個集群運行多個主機節點,每個服務器節點上運行一個Node Exporter實例。Node Exporter實例會自動測試到服務注冊中心Consul服務當中,Prometheus會根據Consul返回的Node Exporter實例信息產生Target列表,並且向這些Target輪訓數據。
然而,如果我們可能還需要:
1.需要按照不同的環境dev, stage, prod聚合監控數據?
2.對於研發團隊而言,我可能只關心dev環境的監控數據?
3.為每一個團隊單獨搭建一個Prometheus Server? 如何讓不同團隊的Prometheus Server采集不同的環境監控數據?
Relabel可以在Prometheus采集數據之前,通過Target實例的Metadata信息,動態重新寫入Label的值。除此之外,我們還能根據Target實例的Metadata信息選擇是否采集或者忽略該Target實例。
在默認情況下,我們從所有環境的Node Exporter中采集到的主機指標如下:
node_cpu{cpu="cpu0",instance="172.21.0.3:9100",job="consul_sd",mode="guest"}
基於Consul動態發現的Target實例,具有以下Metadata信息:
- __meta_consul_address: consul地址
- __meta_consul_dc: consul中服務所在的數據中心
- __meta_consul_metadata_: 服務的metadata
- __meta_consul_node: 服務所在consul節點的信息
- __meta_consul_service_address: 服務訪問地址
- __meta_consul_service_id: 服務ID
- __meta_consul_service_port: 服務端口
- __meta_consul_service: 服務名稱
- __meta_consul_tags: 服務包含的標簽信息
在Prometheus UI中,也可以直接查看target的metadata信息。
這里我們使用__meta_consul_dc信息來標記當前target所在的data center。並且通過regex來匹配source_label的值,使用replacement來選擇regex表達式匹配到的mach group。通過action來告訴prometheus在采集數據之前,需要將replacement的內容寫入到target_label dc當中
scrape_configs: - job_name: consul_sd relabel_configs: - source_labels: ["__meta_consul_dc"] regex: "(.*)" replacement: $1 action: replace target_label: "dc"
對於直接保留標簽的值時,也可以簡化為:
- source_labels: ["__meta_consul_dc"] target_label: "dc"
查詢Prometheus查詢監控數據,所有metrics都被寫入了所在的數據中心標簽dc
node_cpu{cpu="cpu0",dc="dc1",instance="172.21.0.6:9100",job="consul_sd",mode="guest"} 0
當需要過濾target目標時,我們則將action定義為keep或者drop。在Job的配置當中使用一下配置,當匹配到target的元數據標簽__meta_consul_tags中匹配到”.,development,.“,則keep當前實例。
relabel_configs: - source_labels: ["__meta_consul_tags"] regex: ".*,development,.*" action: keep
總結:
通過relabeling可以在寫入metrics數據之前,動態修改metrics的label;
通過relabeling可以對target實例進行過濾和選擇。