前言
軟件的開發(fā)不僅僅在于解決業(yè)務(wù),它還需要程序盡可能的運行下去,這就涉及到了服務(wù)的穩(wěn)定性。穩(wěn)定性涉及很多因素,硬件軟件都需要保證。為了能讓這些條件更加充足,我們需要不斷的收集數(shù)據(jù),分析數(shù)據(jù),監(jiān)控數(shù)據(jù),進而優(yōu)化能優(yōu)化的點。Prometheus 在這方面就為我們提供了很好的監(jiān)控方案。
什么是 Prometheus?
Prometheus 是一個開源的監(jiān)控和報警系統(tǒng),它將我們關(guān)心的指標值通過 PULL 的方式獲取并存儲為時間序列數(shù)據(jù)。如果單從它的收集功能來講,我們也可以通過 mysql、redis 等方式實現(xiàn)。然而,這些數(shù)據(jù)是在每時每刻產(chǎn)生的,其龐大的規(guī)模需要我們好好的考慮其存儲方式。另外,這些監(jiān)控數(shù)據(jù)大多數(shù)時候是跟統(tǒng)計相關(guān)的,比如數(shù)據(jù)與時間的分布情況等,這需要有專業(yè)的度量知識。而這些正是 Prometheus 的擅長所在。
由于 Prometheus 的關(guān)注重點在于指標值以及時間點這兩個因素,所以外部程序?qū)λ慕尤氤杀痉浅5牡?。這種易用性可以讓我們對數(shù)據(jù)進行多維度、多角度的觀察和分析,使得監(jiān)控的效果更加具體化,例如內(nèi)存消耗、網(wǎng)絡(luò)利用率、請求連接數(shù)等。
除此之外,Prometheus 還具備了操作簡單、可拓展的數(shù)據(jù)收集和強大的查詢語言等特性,這些特性能幫助我們在問題出現(xiàn)的時候,快速告警并定位錯誤。所以現(xiàn)在很多微服務(wù)基礎(chǔ)設(shè)施都會選擇接入 Prometheus,像 k8s、云原生等。
Prometheus 的整體架構(gòu)
Prometheus 為了保證它的拓展性、可靠性,在除了提供核心的 server 外還提供了很多生態(tài)組件,為了不增加理解的復(fù)雜度,我們先從上帝視角,看看它的核心 Prometheus server:
可以看到,Prometheus server 的處理鏈路很清晰,其實就是數(shù)據(jù)收集-數(shù)據(jù)存儲-數(shù)據(jù)查詢。當然,一個完善的系統(tǒng)肯定會衍生出許多組件來支撐它的特性。所以我們會看到,在 Prometheus 架構(gòu)里還存在著其他的組件,例如:
- Pushgateway:為監(jiān)控節(jié)點提供 Push 功能,再由 Prometheus server 到 Pushgateway 集中 Pull 數(shù)據(jù)。
- Targets Discover:根據(jù)服務(wù)發(fā)現(xiàn)獲取監(jiān)控節(jié)點的地址。
- PromQL:針對指標數(shù)據(jù)查詢的語言,類似 SQL。
- Alertmanager:根據(jù)配置規(guī)則以及指標分析,提供告警服務(wù)。
最后,Prometheus 的整體架構(gòu)如下:
指標(Metrics)
上面提到 Prometheus 的核心關(guān)注點在于指標(Metrics),指標我們可以簡單的理解為一個度量值,這個值可以是 CPU 負載、內(nèi)存使用、請求連接數(shù)等。它們來源于操作系統(tǒng)、應(yīng)用服務(wù)、設(shè)備數(shù)據(jù)等,會隨著時間的變化而有所不同。為了能讓這些指標更好的體現(xiàn)出度量內(nèi)涵,Prometheus 提供了四種指標類型:
- Counter(計數(shù)器):只增不減的計數(shù)器
- Gauge(儀表盤):可增可減,任意變化的儀表盤
- Histogram(直方圖):將指標進行量化平均,得到類似在 0~10ms 之間的請求數(shù)有多少,而 10~20ms 之間的請求數(shù)又有多少的直方圖
- Summary(摘要):histogram 在客戶端是簡單的分桶和分桶計數(shù),由于 prometheus 服務(wù)端基于這么有限的數(shù)據(jù)做百分位估算,不是很準確,summary 就是解決百分位準確的問題而來的。
實際上,在 Prometheus 里指標的組成有幾部分:指標名、labels、指標值。 (labels 就是我們經(jīng)常提到的維度)。例如,下面就是一個關(guān)于 http 的計數(shù)器類型指標數(shù)據(jù):
# HELP prometheus_http_requests_total Counter of HTTP requests.
# TYPE prometheus_http_requests_total counter
prometheus_http_requests_total{code="200",handler="/api/v1/label/:name/values"} 7
prometheus_http_requests_total{code="200",handler="/api/v1/query"} 19
prometheus_http_requests_total{code="200",handler="/api/v1/query_range"} 27
prometheus_http_requests_total{code="200",handler="/graph"} 11
prometheus_http_requests_total{code="200",handler="/metrics"} 8929
prometheus_http_requests_total{code="200",handler="/static/*filepath"} 52
prometheus_http_requests_total{code="302",handler="/"} 1
prometheus_http_requests_total{code="400",handler="/api/v1/query_range"} 6
需要注意的是,Prometheus 需要收集的數(shù)據(jù)是隨著時間的增長而增長的,所以它一般不建議保留長期的指標數(shù)據(jù),默認保留 15 天。如果監(jiān)控的數(shù)據(jù)發(fā)現(xiàn)問題,那么需要我們配置告警發(fā)現(xiàn),快速處理。
Prometheus 配置
關(guān)于 Prometheus 的使用相信網(wǎng)上有很多詳細教程,此處不再說明。我們來看下它的關(guān)鍵配置文件:prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
可以看到,主要分為了三部分:全局配置(例如數(shù)據(jù)收集時間間隔)、告警規(guī)則、監(jiān)控節(jié)點。告警規(guī)則是基于 PromQL 表達式觸發(fā)條件的,如:
groups:
- name: example
rules:
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: Instance has been down for more than 5 minutes
PromQL
PromQL 是 Prometheus 內(nèi)置的數(shù)據(jù)查詢語言,就像 Mysql 的 SQL 語句一樣,為我們提供了豐富的查詢功能,可應(yīng)用在面板上查詢過濾、告警規(guī)則里的表達式等。下面我們就來初步認識下 PromQL。
PromQL 是面向指標查詢的,前面我們說過,指標是由指標名、labels、指標值組成的,所以當我們想要查詢某個指標時,便可以在瀏覽器訪問 http://localhost:9090/graph 后輸入如下表達式:
prometheus_http_requests_total
然后就可以看到相關(guān)的指標值了:
像上面這種查詢表達式返回的結(jié)果被稱之為 瞬時向量,即返回結(jié)果里每個標簽的每個指標只會存在單個值。如果我們想要按時間范圍來查詢的話,那么就需要使用區(qū)間向量表達式了,通過 [] 來選擇我們的時間。例如:
prometheus_http_requests_total[1m]
表示查詢最近 1 分鐘的樣本數(shù)據(jù)
除了瞬時向量、區(qū)間向量,PromQL 的返回還有 標量(一個浮點型的數(shù)據(jù)值)、字符串類型,根據(jù)這些結(jié)果類型,我們就也做更多的操作了。
運算符
PromQL 支持我們對指標結(jié)果進行運算,比如:
- 算術(shù)運算符:+(加法)、–(減法)、*(乘法)、/(除)、%(模)、^(冪)。
- 比較運算符:>(大于)、<(小于)、==(等于)、!=(不等于)、>=(大于或等于)、<=(小于或等于)
- 邏輯運算符:and(與)、or(或)、unless(排除)
- 聚合運算:sum(和)、min(最?。vg(平均)、count(總數(shù))、stddev(計算維度上的總體標準偏差)、stdvar(計算維度上的總體標準方差)等
有了這些運算符,我們就可更靈活的處理指標值了。
數(shù)據(jù)過濾
當然,我們也可以對數(shù)據(jù)進行過濾,在 PromQL 里主要有以下兩種過濾表達式:
- 完全匹配:即 = 和 != 的用法
- 正則匹配:攜帶了正則表達式,可以用 =~ 和!~ 分表表示正向和反向匹配
例如:process_cpu_seconds_total{job="Node Exporter"} 將篩選標簽 job = Node Exporter 的指標數(shù)據(jù)。
數(shù)據(jù)存儲
Prometheus 2.x 默認將時間序列數(shù)據(jù)庫保存在本地磁盤中,當然,我們也可以將數(shù)據(jù)保存到第三方的存儲服務(wù)中。
本地存儲
Prometheus 按照兩個小時為一個時間窗口,將兩小時內(nèi)產(chǎn)生的數(shù)據(jù)存儲在一個塊(Block)中。每個塊都是一個單獨的目錄,里面包含了對應(yīng)時間窗口內(nèi)的所有樣本數(shù)據(jù)(chunks),元數(shù)據(jù)文件(meta.json)以及索引文件(index)。
其中索引文件會將指標名稱和標簽索引到樣板數(shù)據(jù)的時間序列中。此期間如果通過 API 刪除時間序列,刪除記錄會保存在單獨的邏輯文件 tombstone 當中。
而樣本數(shù)據(jù)所在的塊則會被直接保存在內(nèi)存中,不會持久化到磁盤中。為了確保 Prometheus 發(fā)生崩潰或重啟時能夠恢復(fù)數(shù)據(jù),Prometheus 啟動時會通過預(yù)寫日志(write-ahead-log(WAL))重新記錄,從而恢復(fù)數(shù)據(jù)。
預(yù)寫日志文件保存在 wal 目錄中,每個文件大小為 128MB。wal 文件包括還沒有被壓縮的原始數(shù)據(jù),所以比常規(guī)的塊文件大得多。一般情況下,Prometheus 會保留三個 wal 文件,但如果有些高負載服務(wù)器需要保存兩個小時以上的原始數(shù)據(jù),wal 文件的數(shù)量就會大于 3 個。
遠程存儲
受限于可拓展性和持久性,Prometheus 的本地存儲僅限于單個節(jié)點,所以 Prometheus 并沒有提供集群的存儲解決方案,而是提供了一系列的接口,以便和遠程存儲系統(tǒng)相結(jié)合,比如當我們在 prometheus.yml 配置文件里配置了 Remote Write(遠程寫) 的 URL 地址后,Prometheus 就會將采集到的樣本數(shù)據(jù)通過 HTTP 的形式發(fā)送給適配器,后續(xù)用戶就可以在適配器里對接外部服務(wù)了。外部服務(wù)可以是真正的存儲系統(tǒng),也可以是云存儲、消息隊列等。

和Remote Write(遠程寫) 一樣,當我們在配置文件里配置了 Remote Read(遠程讀) 的 URL 地址后,就會通過 HTTP 的形式到 Adaptor 里查詢數(shù)據(jù),然后 Adaptor 再去第三方存儲服務(wù)里獲取數(shù)據(jù)轉(zhuǎn)發(fā)回來。
Prometheus 缺點
由于 Prometheus 是以指標為關(guān)鍵數(shù)據(jù),所以當我們想要對數(shù)據(jù)進行一條鏈路的走向時,是達不到的。而且它的數(shù)據(jù)是面向時間順序的,如果我們想要提供一些報表處理,那是挺難的。
另外,由于 Prometheus 是奔著簡單易拓展目的設(shè)計的,所以在分布式存儲、集群、多租戶等方面基本沒有涉及,它更專注于實時監(jiān)控。
總結(jié)
系統(tǒng)監(jiān)控其實是每一個成熟架構(gòu)都需要考慮的重點,它是基礎(chǔ)設(shè)施里的重要組成部分,能讓我們提前發(fā)現(xiàn)問題,解決問題。而 Prometheus 作為流行的開源監(jiān)控系統(tǒng),現(xiàn)在逐漸成為了標準,所以提前熟悉它,使用它,還是大有收益的,畢竟保證業(yè)務(wù)的穩(wěn)定性,也是我們開發(fā)工作的一部分呢。
參考
感興趣的朋友可以搜一搜公眾號「 閱新技術(shù) 」,關(guān)注更多的推送文章。
可以的話,就順便點個贊、留個言、分享下,感謝各位支持!
閱新技術(shù),閱讀更多的新知識。