Prometheus是什么?
Prometheus 是由 SoundCloud 開源監(jiān)控告警解決方案,從 2012 年開始編寫代碼,再到 2015 年 github 上開源以來,已經(jīng)吸引了 9k+ 關(guān)注,以及很多大公司的使用;2016 年 Prometheus 成為繼 k8s 后,第二名 CNCF(Cloud Native Computing Foundation) 成員。
主要功能
- 多維 數(shù)據(jù)模型(時序由 metric 名字和 k/v 的 labels 構(gòu)成)。
- 靈活的查詢語句(PromQL)。
- 無依賴存儲,支持 local 和 remote 不同模型。
- 采用 http 協(xié)議,使用 pull 模式,拉取數(shù)據(jù),簡單易懂。
- 監(jiān)控目標,可以采用服務(wù)發(fā)現(xiàn)或靜態(tài)配置的方式。
- 支持多種統(tǒng)計數(shù)據(jù)模型,圖形化友好。
核心組件
- Prometheus Server, 主要用于抓取數(shù)據(jù)和存儲時序數(shù)據(jù),另外還提供查詢和 Alert Rule 配置管理。
- client libraries,客戶端庫,為需要監(jiān)控的服務(wù)生成相應(yīng)的 metrics 并暴露給 Prometheus server。當 Prometheus server 來 pull 時,直接返回實時狀態(tài)的 metrics,比如Java Client。
- push gateway ,用于批量,短期的監(jiān)控數(shù)據(jù)的匯總節(jié)點,主要用于業(yè)務(wù)數(shù)據(jù)匯報等。
-
各種匯報數(shù)據(jù)的 exporters ,下面是兩種解釋:
- 用于暴露已有的第三方服務(wù)的 metrics 給 Prometheus,例如匯報機器數(shù)據(jù)的 node_exporter, 匯報 MongoDB 信息的 MongoDB exporter 等等。
- 在 Prometheus 中負責(zé)數(shù)據(jù)匯報的程序統(tǒng)一叫做 Exporter, 而不同的 Exporter 負責(zé)不同的業(yè)務(wù)。 它們具有統(tǒng)一命名格式,即 xx_exporter, 例如負責(zé)主機信息收集node_exporter。Prometheus 社區(qū)已經(jīng)提供了很多 exporter, 詳情請參考這里。
- Alertmanager:從 Prometheus server 端接收到 alerts 后,會進行去除重復(fù)數(shù)據(jù),分組,并路由到對收的接受方式,發(fā)出報警。常見的接收方式有:電子郵件 ,OpsGenie, webhook 等。
Prometheus基礎(chǔ)架構(gòu)
架構(gòu)邏輯
- Prometheus server 定期從靜態(tài)配置的 targets 或者服務(wù)發(fā)現(xiàn)的 targets 拉取數(shù)據(jù)。
- 當新拉取的數(shù)據(jù)大于配置內(nèi)存緩存區(qū)的時候,Prometheus 會將數(shù)據(jù)持久化到磁盤(如果使用 remote storage 將持久化到云端)。
- Prometheus 可以配置 rules,然后定時查詢數(shù)據(jù),當條件觸發(fā)的時候,會將 alert 推送到配置的 Alertmanager。
- Alertmanager 收到警告的時候,可以根據(jù)配置,聚合,去重,降噪,最后發(fā)送警告。
- 可以使用 API, Prometheus Console 或者 Grafana 查詢和聚合數(shù)據(jù)。
Metric類型
Prometheus定義了4中不同的指標類型(metric type):Counter(計數(shù)器)、Gauge(儀表盤)、Histogram(直方圖)、Summary(摘要)。
Counter:只增不減的計數(shù)器
Counter類型的指標其工作方式和計數(shù)器一樣,只增不減(除非系統(tǒng)發(fā)生重置)。常見的監(jiān)控指標,如http_requests_total。一般在定義Counter類型指標的名稱時推薦使用_total作為后綴。
Gauge:可增可減的儀表盤
auge類型的指標側(cè)重于反應(yīng)系統(tǒng)的當前狀態(tài)。因此這類指標的樣本數(shù)據(jù)可增可減。常見指標如:node_memory_MemFree(主機當前空閑的內(nèi)容大小)、node_memory_MemAvailable(可用內(nèi)存大小)都是Gauge類型的監(jiān)控指標。
Summary 摘要
- 類似于 Histogram, 典型的應(yīng)用如:請求持續(xù)時間,響應(yīng)大小;
- 提供觀測值的 count 和 sum 功能;
- 提供百分位的功能,即可以按百分比劃分跟蹤結(jié)果。
舉例:
# HELP prometheus_tsdb_wal_fsync_duration_seconds Duration of WAL fsync.
# TYPE prometheus_tsdb_wal_fsync_duration_seconds summary
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173
prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002
prometheus_tsdb_wal_fsync_duration_seconds_count 216
當前Prometheus Server進行wal_fsync操作的總次數(shù)為216次,耗時2.888716127000002s。其中中位數(shù)(quantile=0.5)的耗時為0.012352463,9分位數(shù)(quantile=0.9)的耗時為0.014458005s。
Histogram 直方圖
- 可以理解為柱狀圖,典型的應(yīng)用如:請求持續(xù)時間,響應(yīng)大小。
- 可以對觀察結(jié)果采樣,分組及統(tǒng)計。
舉例:
# HELP prometheus_tsdb_compaction_chunk_range Final time range of chunks on their first compaction
# TYPE prometheus_tsdb_compaction_chunk_range histogram
prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="6400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="25600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="102400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260
prometheus_tsdb_compaction_chunk_range_bucket{le="6.5536e+06"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780
prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09
prometheus_tsdb_compaction_chunk_range_count 780
Histogram指標直接反應(yīng)了在不同區(qū)間內(nèi)樣本的個數(shù),區(qū)間通過標簽len進行定義,sum是總數(shù),count是次數(shù)。
Summary與Histogram不同
不同在于Histogram通過histogram_quantile函數(shù)是在服務(wù)器端計算的分位數(shù)。 而Sumamry的分位數(shù)則是直接在客戶端計算完成。因此對于分位數(shù)的計算而言,Summary在通過PromQL進行查詢時有更好的性能表現(xiàn),而Histogram則會消耗更多的資源。反之對于客戶端而言Histogram消耗的資源更少。在選擇這兩種方式時用戶應(yīng)該按照自己的實際場景進行選擇。
PromQL
PromQL是Prometheus內(nèi)置的數(shù)據(jù)查詢語言,其提供對時間序列數(shù)據(jù)豐富的查詢,聚合以及邏輯運算能力的支持。并且被廣泛應(yīng)用在Prometheus的日常應(yīng)用當中,包括對數(shù)據(jù)查詢、可視化、告警處理當中。
有興趣可以PromQL學(xué)習(xí)。
Prometheus報警
Prometheus報警簡單可以分為:
- 定義AlertRule(告警規(guī)則),告警規(guī)則實際上主要由PromQL進行定義,其實際意義是當表達式(PromQL)查詢結(jié)果持續(xù)多長時間(During)后出發(fā)告警;
- AlertManager(報警管理),Alertmanager作為一個獨立的組件,負責(zé)接收并處理來自Prometheus Server(也可以是其它的客戶端程序)的告警信息。
自定義Prometheus告警規(guī)則
groups:
- name: example
rules:
- alert: HighErrorRate # 告警規(guī)則的名稱
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 # 基于PromQL表達式告警觸發(fā)條件,用于計算是否有時間序列滿足該條件。
for: 10m # 評估等待時間,可選參數(shù)。用于表示只有當觸發(fā)條件持續(xù)一段時間后才發(fā)送告警。在等待期間新產(chǎn)生告警的狀態(tài)為pending。
labels:
severity: page # 自定義標簽,允許用戶指定要附加到告警上的一組附加標簽
annotations:
summary: High request latency
description: description info
AlertManager
- 分組,分組機制可以將詳細的告警信息合并成一個通知。在某些情況下,比如由于系統(tǒng)宕機導(dǎo)致大量的告警被同時觸發(fā),在這種情況下分組機制可以將這些被觸發(fā)的告警合并為一個告警通知,避免一次性接受大量的告警通知,而無法對問題進行快速定位。
- 抑制,抑制是指當某一告警發(fā)出后,可以停止重復(fù)發(fā)送由此告警引發(fā)的其它告警的機制。
- 靜默,靜默提供了一個簡單的機制可以快速根據(jù)標簽對告警進行靜默處理。如果接收到的告警符合靜默的配置,Alertmanager則不會發(fā)送告警通知。
集群及高可用
功能分區(qū)(高并發(fā))
考慮另外一種極端情況(網(wǎng)上有人說一臺prometheus可以支持一千個左右節(jié)點),即單個采集任務(wù)的Target數(shù)也變得非常巨大。這時簡單通過聯(lián)邦集群進行功能分區(qū),Prometheus Server也無法有效處理時。這種情況只能考慮繼續(xù)在實例級別進行功能劃分。
global:
external_labels:
slave: 1 # This is the 2nd slave. This prevents clashes between slaves.
scrape_configs:
- job_name: some_job
relabel_configs:
- source_labels: [__address__] # IP
modulus: 4 # 取模
target_label: __tmp_hash # 臨時的label
action: hashmod # 使用取模方法
- source_labels: [__tmp_hash]
regex: ^1$ # 取哪個模
action: keep # 可用
在微服務(wù)平臺,我們使用IP取模奇偶數(shù)的形式來過去微服務(wù)系統(tǒng)的監(jiān)控數(shù)據(jù)。
聯(lián)邦集群
這種部署方式一般適用于兩種場景:
場景一:單數(shù)據(jù)中心 + 大量的采集任務(wù)
這種場景下Promthues的性能瓶頸主要在于大量的采集任務(wù),因此用戶需要利用Prometheus聯(lián)邦集群的特性,將不同類型的采集任務(wù)劃分到不同的Promthues子服務(wù)中,從而實現(xiàn)功能分區(qū)。例如一個Promthues Server負責(zé)采集基礎(chǔ)設(shè)施相關(guān)的監(jiān)控指標,另外一個Prometheus Server負責(zé)采集應(yīng)用監(jiān)控指標。再有上層Prometheus Server實現(xiàn)對數(shù)據(jù)的匯聚。
場景二:多數(shù)據(jù)中心(微服務(wù)使用)
這種模式也適合與多數(shù)據(jù)中心的情況,當Promthues Server無法直接與數(shù)據(jù)中心中的Exporter進行通訊時,在每一個數(shù)據(jù)中部署一個單獨的Promthues Server負責(zé)當前數(shù)據(jù)中心的采集任務(wù)是一個不錯的方式。這樣可以避免用戶進行大量的網(wǎng)絡(luò)配置,只需要確保主Promthues Server實例能夠與當前數(shù)據(jù)中心的Prometheus Server通訊即可。 中心Promthues Server負責(zé)實現(xiàn)對多數(shù)據(jù)中心數(shù)據(jù)的聚合。
Prometheus的服務(wù)發(fā)現(xiàn)
在 Prometheus 的配置中,一個最重要的概念就是數(shù)據(jù)源 target,而數(shù)據(jù)源的配置主要分為靜態(tài)配置和動態(tài)發(fā)現(xiàn), 大致為以下幾類:
- static_configs: 靜態(tài)服務(wù)發(fā)現(xiàn)
- dns_sd_configs: DNS 服務(wù)發(fā)現(xiàn)
- file_sd_configs: 文件服務(wù)發(fā)現(xiàn)
- consul_sd_configs: Consul 服務(wù)發(fā)現(xiàn)
- serverset_sd_configs: Serverset 服務(wù)發(fā)現(xiàn)
- nerve_sd_configs: Nerve 服務(wù)發(fā)現(xiàn)
- marathon_sd_configs: Marathon 服務(wù)發(fā)現(xiàn)
- kubernetes_sd_configs: Kubernetes 服務(wù)發(fā)現(xiàn)
- gce_sd_configs: GCE 服務(wù)發(fā)現(xiàn)
- ec2_sd_configs: EC2 服務(wù)發(fā)現(xiàn)
- openstack_sd_configs: OpenStack 服務(wù)發(fā)現(xiàn)
- azure_sd_configs: Azure 服務(wù)發(fā)現(xiàn)
- triton_sd_configs: Triton 服務(wù)發(fā)現(xiàn)
使用Consul進行服務(wù)發(fā)現(xiàn)
Prometheus exporter配置部分:
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'consul-prometheus'
consul_sd_configs:
#consul 地址
- server: '127.0.0.1:8500'
services: []
微服務(wù)平臺服務(wù)發(fā)現(xiàn)優(yōu)化
我們微服務(wù)使用Eureka并不在Prometheus的自動配置范圍內(nèi),于是我們對Eureka進行二次開發(fā),讓它偽裝成Consul的API接口提供給Prometheus接口。
參見源碼。
使用Java自定義Export
添加攔截器,為監(jiān)控埋點做準備
繼承WebMvcConfigurerAdapter類,復(fù)寫addInterceptors方法,對所有請求/**添加攔截器
@SpringBootApplication
@EnablePrometheusEndpoint
public class SpringApplication extends WebMvcConfigurerAdapter implements CommandLineRunner {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PrometheusMetricsInterceptor()).addPathPatterns("/**");
}
}
PrometheusMetricsInterceptor集成HandlerInterceptorAdapter,通過復(fù)寫父方法,實現(xiàn)對請求處理前/處理完成的處理。
public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}
以Gauge為例
對于Gauge指標的對象則包含兩個主要的方法inc()以及dec(),用戶添加或者減少計數(shù)。在這里我們使用Gauge記錄當前正在處理的Http請求數(shù)量。
public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter {
...省略的代碼
static final Gauge inprogressRequests = Gauge.build()
.name("io_namespace_http_inprogress_requests").labelNames("path", "method", "code")
.help("Inprogress requests.").register();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...省略的代碼
// 計數(shù)器+1
inprogressRequests.labels(requestURI, method, String.valueOf(status)).inc();
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
...省略的代碼
// 計數(shù)器-1
inprogressRequests.labels(requestURI, method, String.valueOf(status)).dec();
super.afterCompletion(request, response, handler, ex);
}
}
參考:
Prometheus 入門與實踐
Prometheus 實戰(zhàn)
Prometheus 官網(wǎng)文檔
Consul+Prometheus系統(tǒng)監(jiān)控之服務(wù)發(fā)現(xiàn)
自定義Metrics:讓Prometheus監(jiān)控你的應(yīng)用程序(Spring版)