第04期:Prometheus 數據采集(三)


Prometheus 的監控對象各式各樣,沒有統一標准。為了解決這個問題,Prometheus 制定了一套監控規范,符合這個規范的樣本數據可以被 Prometheus 采集並解析樣本數據。Exporter 在 Prometheus 監控系統中是一個采集監控數據並通過 Prometheus 監控規范對外提供數據的組件,針對不同的監控對象可以實現不同的 Exporter,這樣就解決了監控對象標准不一的問題。從廣義上說,所有可以向 Prometheus 提供監控樣本數據的程序都可以稱為 Exporter,Exporter 的實例也就是我們上期所說的"target"。

Exporter 的運行方式

Exporter 有兩種運行方式

  • 集成到應用中

    使用 Prometheus 提供的 Client Library,可以很方便地在應用程序中實現監控代碼,這種方式可以將程序內部的運行狀態暴露給 Prometheus,適用於需要較多自定義監控指標的項目。目前一些開源項目就增加了對 Prometheus 監控的原生支持,如 Kubernetes,ETCD 等。

  • 獨立運行

    在很多情況下,對象沒法直接提供監控接口,可能原因有:

    1. 項目發布時間較早,並不支持 Prometheus 監控接口,如 MySQL、Redis;

    2. 監控對象不能直接提供 HTTP 接口,如監控 Linux 系統狀態指標。

    對於上述情況,用戶可以選擇使用獨立運行的 Exporter。除了用戶自行實現外,Prometheus 社區也提供了許多獨立運行的 Exporter,常見的有 Node Exporter、MySQL Server Exporter。更多詳情可以到官網了解:https://prometheus.io/docs/instrumenting/exporters/

Exporter 接口數據規范

Exporter 通過 HTTP 接口以文本形式向 Prometheus 暴露樣本數據,格式簡單,沒有嵌套,可讀性強。每個監控指標對應的數據文本格式如下:

# HELP <監控指標名稱> <監控指標描述> # TYPE <監控指標名稱> <監控指標類型> <監控指標名稱>{ <標簽名稱>=<標簽值>,<標簽名稱>=<標簽值>...} <樣本值1> <時間戳> <監控指標名稱>{ <標簽名稱>=<標簽值>,<標簽名稱>=<標簽值>...} <樣本值2> <時間戳> ... 
  • 以 # 開頭的行,如果后面跟着"HELP",Prometheus 將這行解析為監控指標的描述,通常用於描述監控數據的來源;
  • 以 # 開頭的行,如果后面跟着"TYPE",Prometheus 將這行解析為監控指標的類型,支持的類型有:Counter、Gauge、Histogram、Summary、Untyped。在往期文章中介紹過 Prometheus 在存儲數據時是不區分數據類型的,所以當你在猶豫一個數據類型應該用 Counter 或 Gauge 時,可以試試 Untype;
  • 以 # 開頭的行,如果后面沒有跟着"HELP"或"TYPE",則 Prometheus 將這行視為注釋,解析時忽略;
  • 如果一個監控指標有多條樣本數據,那么每條樣本數據的標簽值組合應該是唯一的;
  • 每行數據對應一條樣本數據;
  • 時間戳應為采集數據的時間,是可選項,如果 Exporter 沒有提供時間戳的話,Prometheus Server 會在拉取到樣本數據時將時間戳設置為當前時間;
  • Summary 和 Histogram 類型的監控指標要求提供兩行數據分別表示該監控指標所有樣本的和、樣本數量,命名格式為:<監控指標名稱>_sum、<監控指標名稱>_count;
  • Summary 類型的樣本數據格式:1. 根據 Exporter 提供的分位點,樣本會被計算后拆分成多行數據,每行使用標簽"quantile"區分,"quantile"的值包括 Exporter 提供的所有分位點。2. 數據的排列順序必須是按照標簽"quantile"值遞增;3. 舉個栗子:一個名為x的監控指標,提供的分位點為:0.5, 0.9, 0.99,那么它暴露給 Prometheus 的接口數據格式如下:
# HELP x balabala
# TYPE x summary
x{quantile="0.5"} value1
x{quantile="0.9"} value2
x{quantile="0.99"} value3
x_sum sum(values)
x_count count(values)
  • Histogram 類型的樣本數據格式:
    1. 根據 Exporter 提供的 Bucket 值,樣本會被計算后拆分成多行數據,每行使用標簽"le"區分,"le"為 Exporter 提供的 Buckets;
    2. 數據的排列順序必須是按照標簽"le"值遞增;
    3. 必須要有一行數據的標簽 le="+Inf",值為該監控指標的樣本總數;
    4. 舉個栗子:一個名為 x 的監控指標,提供的 Buckets 為:20, 50, 70,那么它暴露給 Prometheus 的接口數據格式如下:
# HELP x The temperature of cpu
# TYPE x histogram
x_bucket{le="20"} value1
x_bucket{le="50"} value2
x_bucket{le="70"} value3
x_bucket{le="+Inf"} count(values)
x_sum sum(values)
x_count count(values)

這樣的文本格式也有不足之處:
1. 文本內容可能過於冗長;
2. Prometheus 在解析時不能校驗 HELP 和 TYPE 字段是否缺失,如果缺失 HELP 字段,這條樣本數據的來源可能就難以判斷;如果缺失 TYPE 字段,Prometheus 對這條樣本數據的類型就無從得知;
3. 相比於 protobuf,Prometheus 使用的文本格式沒有做任何壓縮處理,解析成本較高。

MySQL Server Exporter

針對被廣泛使用的關系型數據庫 MySQL,Prometheus 官方提供了 MySQL Server Exporter,支持 MySQL 5.6 及以上版本,對於 5.6 以下的版本,部分監控指標可能不支持。
MySQL Server Exporter 監控的信息包括了常用的 global status/variables 信息、schema/table 的統計信息、user 統計信息、innodb 的信息以及主從復制、組復制的信息,監控指標比較全面。但是由於它提供的監控指標中缺少對 MySQL 實例的標識,所以當一台主機上存在多個 MySQL 實例,需要運行多個 MySQL Server Exporter 進行監控時,就會難以區分實例信息。具體使用方式可參考:https://github.com/prometheus/mysqld_exporter

Node Exporter

Prometheus 官方的 Node Exporter 提供對 *NIX 系統、硬件信息的監控,監控指標包括 CPU 使用率/配置、系統平均負載、內存信息、網絡狀況、文件系統信息統計、磁盤使用情況統計等。對於不同的系統,監控指標會有所差異,如 diskstats 支持 Darwin, Linux, OpenBSD 系統;loadavg 支持 Darwin, Dragonfly, FreeBSD, Linux, NetBSD, OpenBSD, Solaris 系統。Node Exporter 的監控指標沒有對主機身份的標識,可以通過 relabel 功能在 Prometheus Server 端增加一些標識標簽。具體使用方式可參考:https://github.com/prometheus/node_exporter

如何實現一個 Exporter

編寫一個簡單的 Exporter

使用 prometheus/client_golang 包,我們來編寫一個簡單的 Exporter,包括 Prometheus 支持的四種監控指標類型

package main import ( "log" "net/http" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( //使用GaugeVec類型可以為監控指標設置標簽,這里為監控指標增加一個標簽"device" speed = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "disk_available_bytes", Help: "Disk space available in bytes", }, []string{"device"}) tasksTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "test_tasks_total", Help: "Total number of test tasks", }) taskDuration = prometheus.NewSummary(prometheus.SummaryOpts{ Name: "task_duration_seconds", Help: "Duration of task in seconds", //Summary類型的監控指標需要提供分位點 Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) cpuTemperature = prometheus.NewHistogram(prometheus.HistogramOpts{ Name: "cpu_temperature", Help: "The temperature of cpu", //Histogram類型的監控指標需要提供Bucket Buckets: []float64{20, 50, 70, 80}, }) ) func init() { //注冊監控指標 prometheus.MustRegister(speed) prometheus.MustRegister(tasksTotal) prometheus.MustRegister(taskDuration) prometheus.MustRegister(cpuTemperature) } func main() { //模擬采集監控數據 fakeData() //使用prometheus提供的promhttp.Handler()暴露監控樣本數據 //prometheus默認從"/metrics"接口拉取監控樣本數據 http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":10000", nil)) } func fakeData() { tasksTotal.Inc() //設置該條樣本數據的"device"標簽值為"/dev/sda" speed.With(prometheus.Labels{"device": "/dev/sda"}).Set(82115880) taskDuration.Observe(10) taskDuration.Observe(20) taskDuration.Observe(30) taskDuration.Observe(45) taskDuration.Observe(56) taskDuration.Observe(80) cpuTemperature.Observe(30) cpuTemperature.Observe(43) cpuTemperature.Observe(56) cpuTemperature.Observe(58) cpuTemperature.Observe(65) cpuTemperature.Observe(70) } 

接下來編譯、運行我們的 Exporter

GOOS=linux GOARCH=amd64 go build -o my_exporter main.go ./my_exporter & 

Exporter 運行起來之后,還要在 Prometheus 的配置文件中加入 Exporter 信息,Prometheus 才能從 Exporter 拉取數據。

static_configs:
- targets: ['localhost:9090','172.17.0.3:10000']

在 Prometheus 的 targets 頁面可以看到剛才新增的 Exporter 了

 
untitled.png

訪問"/metrics"接口可以找到如下數據:

Gauge
因為我們使用了 GaugeVec,所以產生了帶標簽的樣本數據

# HELP disk_available_bytes disk space available in bytes
# TYPE disk_available_bytes gauge
disk_available_bytes{device="/dev/sda"} 8.211588e+07

Counter

# HELP test_tasks_total total number of test tasks
# TYPE test_tasks_total counter
test_tasks_total 1

Summary

# HELP task_duration_seconds Duration of task in seconds
# TYPE task_duration_seconds summary
task_duration_seconds{quantile="0.5"} 30
task_duration_seconds{quantile="0.9"} 80
task_duration_seconds{quantile="0.99"} 80
task_duration_seconds_sum 241
task_duration_seconds_count 6

Histogram

# HELP cpu_temperature The temperature of cpu
# TYPE cpu_temperature histogram
cpu_temperature_bucket{le="20"} 0
cpu_temperature_bucket{le="50"} 2
cpu_temperature_bucket{le="70"} 6
cpu_temperature_bucket{le="80"} 6
cpu_temperature_bucket{le="+Inf"} 6
cpu_temperature_sum 322
cpu_temperature_count 6

Exporter實現方式的考量

上面的栗子中,我們在程序一開始就初始化所有的監控指標,這種方案通常接下來會開啟一個采樣協程去定期采集、更新監控指標的樣本數據,最新的樣本數據將一直保留在內存中,在接到 Prometheus Server 的請求時,返回內存里的樣本數據。這個方案的優點在於,易於控制采樣頻率;不用擔心並發采樣可能帶來的資源搶占問題。不足之處有:

1. 由於樣本數據不會被自動清理,當某個已被采樣的采集對象失效了,Prometheus Server 依然能拉取到它的樣本數據,只是這個數據從監控對象失效時就已經不會再被更新。這就需要 Exporter 自己提供一個對無效監控對象的數據清理機制;
2. 由於響應 Prometheus Server 的請求是從內存里取數據,如果 Exporter 的采樣協程異常卡住,Prometheus Server 也無法感知,拉取到的數據可能是過期數據;
3. Prometheus Server 拉取的數據不是即時采樣的,對於某時間點的數據一致性不能保證。

另一種方案是 MySQL Server Exporter 和 Node Exporter 采用的,也是 Prometheus 官方推薦的方案。該方案是在每次接到 Prometheus Server 的請求時,初始化新的監控指標,開啟一個采樣協程。和方案一不同的是,這些監控指標只在請求期間存活。然后采樣協程會去采集所有樣本數據並返回給 Prometheus Server。相比於方案一,方案二的數據是即時拉取的,可以保證時間點的數據一致性;因為監控指標會在每次請求時重新初始化,所以也不會存在失效的樣本數據。不過方案二同樣有不足之處:

1. 當多個拉取請求同時發生時,需要控制並發采集樣本的資源消耗;

2. 當多個拉取請求同時發生時,在短時間內需要對同一個監控指標讀取多次,對於一個變化頻率較低的監控指標來說,多次讀取意義不大,卻增加了對資源的占用。



作者:愛可生開源社區
鏈接:https://www.jianshu.com/p/2cedcd1bf810
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM