軟件測(cè)試 | 普羅米修斯-初識(shí)PromQL
理解監(jiān)控?cái)?shù)據(jù)
之前講過(guò)普羅米修斯自己就是一個(gè)時(shí)序數(shù)據(jù)庫(kù), 它從 exporter 拉取的數(shù)據(jù)都會(huì)按時(shí)間戳保存到對(duì)應(yīng)的文件里,這個(gè)時(shí)序數(shù)據(jù)庫(kù)默認(rèn)會(huì)保存 14 天的數(shù)據(jù)。 而它自己也開發(fā)了一套名為 PromQL 的類 SQL 的查詢語(yǔ)言用來(lái)從各種維度讓用戶來(lái)查詢并計(jì)算監(jiān)控的數(shù)據(jù)。 我們先來(lái)看一下我自己編寫的 exporter 的接口, 看看它向普羅米修斯的主服務(wù)返回的監(jiān)控?cái)?shù)據(jù)是什么樣的。

其中 # 開頭的是某個(gè)或者某些指標(biāo)的幫助文檔, 而非 # 開頭的每一行表示當(dāng)前 Exporter 采集到的一個(gè)監(jiān)控樣本。由于我是使用普羅米修斯的 python client 編寫的 exporter, 所以它自帶了 python 的多個(gè)監(jiān)控指標(biāo)。 而下面的MemoryUsage
?和HttpRequests
?就是我自己定義的監(jiān)控指標(biāo)了。 這其中MemoryUsage
?和HttpRequests
?是指標(biāo)名稱, 花括號(hào)內(nèi)的是這個(gè)指標(biāo)的 label, label 是個(gè)非常重要的機(jī)制, 它把相同的監(jiān)控指標(biāo)按自定義的 label 類型進(jìn)行分類,比如這個(gè)監(jiān)控?cái)?shù)據(jù)是哪個(gè)機(jī)器的, 哪個(gè)機(jī)房的等等。 是編寫 exporter 的人定義上去的, 目的是在后續(xù)使用 PromQL 的時(shí)候用戶可以用 label 把監(jiān)控?cái)?shù)據(jù)進(jìn)行分類查詢 。 比如用戶想要抓取每臺(tái)機(jī)器上的 CPU Load 數(shù)據(jù),分別顯示出來(lái) 。 那它就要在查詢 cpu_load15 的時(shí)候用 label 進(jìn)行分組了。 最后面的浮點(diǎn)數(shù)值就是監(jiān)控?cái)?shù)據(jù)的值了。
理解時(shí)間序列
^
│ ? . . . . . . . . . . . . . . . . . ? . . ? node_cpu{cpu="cpu1",mode="idle"}
│ ? ? . . . . . . . . . . . . . . . . . . . ? node_cpu{cpu="cpu1",mode="system"}
│ ? ? . . . . . . . . . . . . . . . . ? . . ?
v
? <------------------ 時(shí)間 ---------------->
在時(shí)間序列中的每一個(gè)點(diǎn)稱為一個(gè)樣本(sample),樣本由以下三部分組成:
指標(biāo) (metric):指標(biāo)名稱和描述當(dāng)前樣本的 label 集合(上面介紹過(guò)一個(gè)指標(biāo)都有哪些東西);
時(shí)間戳 (timestamp):一個(gè)精確到毫秒的時(shí)間戳;
樣本值 (value): 一個(gè)浮點(diǎn)型數(shù)據(jù)表示當(dāng)前樣本的值。
如下圖:
<--------------- metric ---------------------><-timestamp -><-value->
http_request_total{status="200", method="GET"}@1434417560938 => 94355
http_request_total{status="200", method="GET"}@1434417561287 => 94334
http_request_total{status="404", method="GET"}@1434417560938 => 38473
http_request_total{status="404", method="GET"}@1434417561287 => 38544
http_request_total{status="200", method="POST"}@1434417560938 => 4748
http_request_total{status="200", method="POST"}@1434417561287 => 4785
指標(biāo)類型
在普羅米修斯中,有 4 種類型的指標(biāo):Counter, Gauge, Histogram 和 Summary
Counter
counter 類型的指標(biāo)是一個(gè)只增不減的計(jì)數(shù)器, 我們上面的 http_request_total 還有 cpu 相關(guān)的信息都是屬于 counter 類型的。 一般在定義 Counter 類型指標(biāo)的名稱時(shí)推薦使用_total 作為后綴。 一般 counter 類型的指標(biāo)都會(huì)配合內(nèi)置函數(shù) rate 或者 irate 來(lái)完成指標(biāo)的計(jì)算。比如我想統(tǒng)計(jì)某臺(tái)機(jī)器上監(jiān)控的進(jìn)程 CPU 的使用率,那么可以使用這條語(yǔ)句查詢:
rate(process_cpu_seconds_total{kubernetes_io_hostname="xxxxxxxx"}[5m])
這里解釋一下這個(gè)查詢的含義。這里主要解釋 3 個(gè)地方
一定有人覺(jué)得 cpu 使用率的指標(biāo)定義成一個(gè)只增不減的 counter 類型很奇怪。 之前講 cgroups 的時(shí)候曾經(jīng)說(shuō)過(guò)我們限制 CPU 的手段是限制程序能夠使用的 cpu 的時(shí)間。 CPU 是按時(shí)間片輪轉(zhuǎn)規(guī)則去執(zhí)行任務(wù)的。 CPU 會(huì)把自己的一個(gè) CPU 時(shí)間劃分成很多個(gè)小片, 每個(gè)小片只運(yùn)行一個(gè)進(jìn)程任務(wù), 時(shí)間到了就會(huì)觸發(fā) CPU 上下文切換, cpu 就會(huì)去執(zhí)行下一個(gè)任務(wù)。 所以限制和計(jì)算一個(gè)進(jìn)程的 CPU 使用, 就看這個(gè)進(jìn)程能使用多少 CPU 時(shí)間 。 所以在普羅米修斯里針對(duì) CPU 的使用也就定義成了 counter 類型了, 通過(guò)計(jì)算出使用 CPU 的時(shí)間數(shù)字間接的就可以計(jì)算出 CPU 的使用率來(lái)。
在這個(gè)語(yǔ)句的后面有一個(gè) [5m] 意思是查詢最近 5 分鐘的數(shù)據(jù), 這時(shí)候會(huì)返回最近 5m 內(nèi)采集到的所有指標(biāo)。方面后續(xù)我們統(tǒng)計(jì)最近 5m 內(nèi)的 CPU 使用率指標(biāo)
rate 是 PromQL 里內(nèi)置的函數(shù), 用來(lái)統(tǒng)計(jì)數(shù)據(jù)的增長(zhǎng)率。 所以通過(guò) rate 函數(shù)就可以計(jì)算出 CPU 的使用率了。
Gauge
與 Counter 不同,Gauge 類型的指標(biāo)側(cè)重于反應(yīng)系統(tǒng)的當(dāng)前狀態(tài)。這類指標(biāo)的樣本數(shù)據(jù)可增可減。常見(jiàn)指標(biāo)如:node_memory_MemFree(主機(jī)當(dāng)前空閑的內(nèi)存大?。?、node_memory_MemAvailable(可用內(nèi)存大?。┒际?Gauge 類型的監(jiān)控指標(biāo)。通過(guò) Gauge 指標(biāo),用戶可以直接查看系統(tǒng)的當(dāng)前狀態(tài)。
使用 Histogram 和 Summary 分析數(shù)據(jù)分布情況
Histogram 和 Summary 我用的不多,所以還是用網(wǎng)絡(luò)上一個(gè)例子來(lái)說(shuō)吧, 他們主要用于統(tǒng)計(jì)和分析樣本的分布情況。 他們類似在性能測(cè)試中我們喜歡查看 TP 99, TP 95, TP 90 這樣的指標(biāo)。Histogram 和 Summary 都是為了能夠解決這樣問(wèn)題的存在,通過(guò) Histogram 和 Summary 類型的監(jiān)控指標(biāo),我們可以快速了解監(jiān)控樣本的分布情況。
例如,指標(biāo) prometheus_tsdb_wal_fsync_duration_seconds 的指標(biāo)類型為 Summary。 它記錄了 Prometheus Server 中 wal_fsync 處理的處理時(shí)間,通過(guò)訪問(wèn) Prometheus Server 的/metrics 地址,可以獲取到以下監(jiān)控樣本數(shù)據(jù):
# 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
從上面的樣本中可以得知當(dāng)前 Prometheus Server 進(jìn)行 wal_fsync 操作的總次數(shù)為 216 次,耗時(shí) 2.888716127000002s。其中中位數(shù)(quantile=0.5)的耗時(shí)為 0.012352463,9 分位數(shù)(quantile=0.9)的耗時(shí)為 0.014458005s。
在 Prometheus Server 自身返回的樣本數(shù)據(jù)中,我們還能找到類型為 Histogram 的監(jiān)控指標(biāo) prometheus_tsdb_compaction_chunk_range_bucket。
# 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
與 Summary 類型的指標(biāo)相似之處在于 Histogram 類型的樣本同樣會(huì)反應(yīng)當(dāng)前指標(biāo)的記錄的總數(shù) (以_count 作為后綴) 以及其值的總量(以_sum 作為后綴)。不同在于 Histogram 指標(biāo)直接反應(yīng)了在不同區(qū)間內(nèi)樣本的個(gè)數(shù),區(qū)間通過(guò)標(biāo)簽 len 進(jìn)行定義。
同時(shí)對(duì)于 Histogram 的指標(biāo),我們還可以通過(guò) histogram_quantile() 函數(shù)計(jì)算出其值的分位數(shù)。不同在于 Histogram 通過(guò) histogram_quantile 函數(shù)是在服務(wù)器端計(jì)算的分位數(shù)。 而 Sumamry 的分位數(shù)則是直接在客戶端計(jì)算完成。因此對(duì)于分位數(shù)的計(jì)算而言,Summary 在通過(guò) PromQL 進(jìn)行查詢時(shí)有更好的性能表現(xiàn),而 Histogram 則會(huì)消耗更多的資源。反之對(duì)于客戶端而言 Histogram 消耗的資源更少。在選擇這兩種方式時(shí)用戶應(yīng)該按照自己的實(shí)際場(chǎng)景進(jìn)行選擇。
PromQL 的基本語(yǔ)法
我們直接用指標(biāo)的名稱進(jìn)行查詢的話,就可以返回該指標(biāo)的所有數(shù)據(jù)了。 比如直接查詢:process_cpu_seconds_total
?查詢結(jié)果:
process_cpu_seconds_total{GPUName="T4", beta_kubernetes_io_arch="amd64"} ?457320.31
而如果我們想查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù)則可以用 [5m] 這樣的語(yǔ)法進(jìn)行查詢。比如查詢process_cpu_seconds_total[5m]
?結(jié)果如下:
process_cpu_seconds_total{GPUName="T4", beta_kubernetes_io_arch="amd64"}
457290.56 @1636449224.643
457292.18 @1636449239.643
457294.08 @1636449254.643
457295.48 @1636449269.643
457297.15 @1636449284.643
457298.74 @1636449299.643
457300 @1636449314.643
457301.55 @1636449329.643
457302.9 @1636449344.643
457304.77 @1636449359.643
457306.26 @1636449374.643
457307.93 @1636449389.643
457309.26 @1636449404.643
除了制定查詢最近 5m 數(shù)據(jù)這樣的語(yǔ)法外, 還可以使用時(shí)間位移查詢。 比如:process_cpu_seconds_total{}[1d] offset 1d
?offset 是說(shuō)查詢的數(shù)據(jù)偏移一天。 在這個(gè)例子里就是在查詢 2 天前到 1 天前的數(shù)據(jù)。
上面 的例子本來(lái)都會(huì)返回很多數(shù)據(jù)的,因?yàn)槲耶?dāng)前監(jiān)控了許多臺(tái)機(jī)器。 為了不占用太多的篇幅,所以我刪除了很多內(nèi)容。我們知道每個(gè)指標(biāo)都會(huì)定義很多個(gè) label。 而如果我們?cè)诓樵兊臅r(shí)候就可以根據(jù) label 進(jìn)行過(guò)濾, 比如我們可以查詢process_cpu_seconds_total{kubernetes_io_hostname="xxxxxxx"}
?這樣就只會(huì)查詢出某臺(tái)機(jī)器的指標(biāo)了
還可以通過(guò)更多 PromQL 的內(nèi)置函數(shù)來(lái)聚合查詢結(jié)果, 比如我查詢avg(process_cpu_seconds_total{}) by (kubernetes_io_hostname)
?結(jié)果如下:
| |
|:--|
|kubernetes_io_hostname="xxxxxx"} |457320.31 |
|{kubernetes_io_hostname="xxxxxxxxx"} |463029.44 |
|{} |17048.66272727273 |
|{kubernetes_io_hostname="xxxxxxxx"} |951251.4 |
|{kubernetes_io_hostname="xxxxxxxx"} |1021410.33 |
|{kubernetes_io_hostname="xxxxxxx"} |1617941.71 |
上面 我們利用了 avg 這個(gè)內(nèi)置函數(shù)來(lái)計(jì)算 指標(biāo)的平均值, 并且我們利用了 by 關(guān)鍵字制定了指標(biāo)的某一個(gè) label。 意思是我們先把所有的數(shù)據(jù)按 kubernetes_io_hostname 進(jìn)行分類。 然后根據(jù)每個(gè)分類使用 avg 這個(gè)方法統(tǒng)計(jì)平均值。