Go語言開發Prometheus Exporter示例


一、Prometheus中的基本概念

Prometheus將所有數據存儲為時間序列,這里先來了解一下prometheus中的一些基本概念

指標名和標簽
每個時間序列都由指標名和一組鍵值對(也稱為標簽)唯一標識。

metric的格式如下:

<metric name>{<label name>=<label value>, ...}


例如:

http_requests_total{host="192.10.0.1", method="POST", handler="/messages"}


http_requests_total是指標名;
host、method、handler是三個標簽(label),也就是三個維度;
查詢語句可以基於這些標簽or維度進行過濾和聚合;
指標類型
Prometheus client庫提供四種核心度量標准類型。注意是客戶端。Prometheus服務端沒有區分類型,將所有數據展平為無類型時間序列。

1、 Counter:只增不減的累加指標

Counter就是一個計數器,表示一種累積型指標,該指標只能單調遞增或在重新啟動時重置為零,例如,您可以使用計數器來表示所服務的請求數,已完成的任務或錯誤。

2、 Gauge:可增可減的測量指標

Gauge是最簡單的度量類型,只有一個簡單的返回值,可增可減,也可以set為指定的值。所以Gauge通常用於反映當前狀態,比如當前溫度或當前內存使用情況;當然也可以用於“可增加可減少”的計數指標。

3、Histogram:自帶buckets區間用於統計分布的直方圖

Histogram主要用於在設定的分布范圍內(Buckets)記錄大小或者次數。

例如http請求響應時間:0-100ms、100-200ms、200-300ms、>300ms 的分布情況,Histogram會自動創建3個指標,分別為:

事件發送的總次數<basename>_count:比如當前一共發生了2次http請求
所有事件產生值的大小的總和<basename>_sum:比如發生的2次http請求總的響應時間為150ms
事件產生的值分布在bucket中的次數<basename>_bucket{le="上限"}:比如響應時間0-100ms的請求1次,100-200ms的請求1次,其他的0次

4、Summary:數據分布統計圖

Summary和Histogram類似,都可以統計事件發生的次數或者大小,以及其分布情況。

Summary和Histogram都提供了對於事件的計數_count以及值的匯總_sum,因此使用_count,和_sum時間序列可以計算出相同的內容。

同時Summary和Histogram都可以計算和統計樣本的分布情況,比如中位數,n分位數等等。不同在於Histogram可以通過histogram_quantile函數在服務器端計算分位數。 而Sumamry的分位數則是直接在客戶端進行定義。因此對於分位數的計算。 Summary在通過PromQL進行查詢時有更好的性能表現,而Histogram則會消耗更多的資源。相對的對於客戶端而言Histogram消耗的資源更少。

作業和實例
在Prometheus中,一個可以拉取數據的端點IP:Port叫做一個實例(instance),而具有多個相同類型實例的集合稱作一個作業(job)

- job: api-server
- instance 1: 1.2.3.4:5670
- instance 2: 1.2.3.4:5671
- instance 3: 5.6.7.8:5670
- instance 4: 5.6.7.8:5671

當Prometheus拉取指標數據時,會自動生成一些標簽(label)用於區別抓取的來源:

job:配置的作業名;
instance:配置的實例名,若沒有實例名,則是抓取的IP:Port。
對於每一個實例(instance)的抓取,Prometheus會默認保存以下數據:

up{job="<job>", instance="<instance>"}:如果實例是健康的,即可達,值為1,否則為0;
scrape_duration_seconds{job="<job>", instance="<instance>"}:抓取耗時;
scrape_samples_post_metric_relabeling{job="<job>", instance="<instance>"}:指標重新標記后剩余的樣本數。
scrape_samples_scraped{job="<job>", instance="<instance>"}:實例暴露的樣本數
該up指標對於監控實例健康狀態很有用。

二、最簡單的Exporter
當你安裝好go的開發環境,並下載好Prometheus依賴包到vendor以后,就可以編譯個最簡單的Exporter,代碼如下:

 1 package main
 2 
 3 import (
 4 "log"
 5 "net/http"
 6 "github.com/prometheus/client_golang/prometheus/promhttp"
 7 )
 8 
 9 func main() {
10 http.Handle("/metrics", promhttp.Handler())
11 log.Fatal(http.ListenAndServe(":8080", nil))
12 }

執行go build編譯運行,然后訪問http://127.0.0.1:8080/metrics就可以看到采集到的指標數據。

這段代碼僅僅通過http模塊指定了一個路徑/metrics,並將client_golang庫中的promhttp.Handler()作為處理函數傳遞進去后,就可以獲取指標數據了。這個最簡單的 Exporter 內部其實是使用了一個默認的收集器NewGoCollector采集當前Go運行時的相關信息,比如go堆棧使用、goroutine數據等等。

三、Demo Exporter的目錄結構
項目的目錄結構如下:

1 prometheus-exporter/
2 |-- collector
3 `-- vendor
4   `-- github.com
5     |-- beorn7
6     |-- golang
7     |-- matttproud
8     `-- prometheus

vendor是項目依賴的外部包

collector實現一個采集器,用於采集指標數據
四、代碼實現
包括以下幾個主要的步驟。

1、定義指標
定義指標就是創建指標的描述符,通常把要采集的指標描述符放在一個結構體里:

// 指標結構體
type Metrics struct {
metrics map[string]*prometheus.Desc
mutex sync.Mutex
}

/**
* 函數:newGlobalMetric
* 功能:創建指標描述符
*/
func newGlobalMetric(namespace string, metricName string, docString string, labels []string) *prometheus.Desc {
return prometheus.NewDesc(namespace+"_"+metricName, docString, labels, nil)
}


/**
* 工廠方法:NewMetrics
* 功能:初始化指標信息,即Metrics結構體
*/
func NewMetrics(namespace string) *Metrics {
return &Metrics{
metrics: map[string]*prometheus.Desc{
"my_counter_metric": newGlobalMetric(namespace, "my_counter_metric", "The description of my_counter_metric", []string{"host"}),
"my_gauge_metric": newGlobalMetric(namespace, "my_gauge_metric","The description of my_gauge_metric", []string{"host"}),
},
}
}

調用工廠方法即可創建一個結構體的實例

2、注冊指標

metrics := collector.NewMetrics(*metricsNamespace) // 創建指標結構體實例
registry := prometheus.NewRegistry()
registry.MustRegister(metrics) // 注冊指標

3、數據采集
數據采集需要實現collector的兩個接口:

 1 /**
 2 * 接口:Describe
 3 * 功能:傳遞結構體中的指標描述符到channel
 4 */
 5 func (c *Metrics) Describe(ch chan<- *prometheus.Desc) {
 6 for _, m := range c.metrics {
 7 ch <- m
 8 }
 9 }
10 
11 /**
12 * 接口:Collect
13 * 功能:抓取最新的數據,傳遞給channel
14 */
15 func (c *Metrics) Collect(ch chan<- prometheus.Metric) {
16 c.mutex.Lock() // 加鎖
17 defer c.mutex.Unlock()
18 
19 mockCounterMetricData, mockGaugeMetricData := c.GenerateMockData()
20 for host, currentValue := range mockCounterMetricData {
21 ch <-prometheus.MustNewConstMetric(c.metrics["my_counter_metric"], prometheus.CounterValue, float64(currentValue), host)
22 }
23 for host, currentValue := range mockGaugeMetricData {
24 ch <-prometheus.MustNewConstMetric(c.metrics["my_gauge_metric"], prometheus.GaugeValue, float64(currentValue), host)
25 }
26 }

4、啟動HTTP服務

http.Handle(*metricsPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
http.ListenAndServe(":"+*listenAddr, nil)

這只是一個Demo,當實際需要開發一個exporter時,你需要重新定義要抓取的指標,並添加采集數據的具體邏輯。


免責聲明!

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



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