Prometheus 編寫(xiě)自己的 exporter

  1. 保證Go語(yǔ)言安裝環(huán)境(1.7以上)
    1) 下載對(duì)應(yīng)的prometheus包
go get github.com/prometheus/client_golang/prometheus/promhttp

2)程序主函數(shù):

package main

import (
    "log"
    "net/http"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(":8080", nil))
}
  1. 指標(biāo)類(lèi)別
    Prometheus中主要使用的四類(lèi)指標(biāo)類(lèi)型,如下所示
  • Counter (累加指標(biāo))
  • Gauge (測(cè)量指標(biāo))
  • Summary (概略圖)
  • Histogram (直方圖)

Counter 一個(gè)累加指標(biāo)數(shù)據(jù),這個(gè)值隨著時(shí)間只會(huì)逐漸的增加航瞭,比如程序完成的總?cè)蝿?wù)數(shù)量瞒瘸,運(yùn)行錯(cuò)誤發(fā)生的總次數(shù)双戳。常見(jiàn)的還有交換機(jī)中snmp采集的數(shù)據(jù)流量也屬于該類(lèi)型承粤,代表了持續(xù)增加的數(shù)據(jù)包或者傳輸字節(jié)累加值虑乖。

Gauge代表了采集的一個(gè)單一數(shù)據(jù)懦趋,這個(gè)數(shù)據(jù)可以增加也可以減少,比如CPU使用情況疹味,內(nèi)存使用量仅叫,硬盤(pán)當(dāng)前的空間容量等等

Histogram和Summary使用的頻率較少,兩種都是基于采樣的方式糙捺。另外有一些庫(kù)對(duì)于這兩個(gè)指標(biāo)的使用和支持程度不同诫咱,有些僅僅實(shí)現(xiàn)了部分功能。這兩個(gè)類(lèi)型對(duì)于某一些業(yè)務(wù)需求可能比較常見(jiàn)洪灯,比如查詢(xún)單位時(shí)間內(nèi):總的響應(yīng)時(shí)間低于300ms的占比坎缭,或者查詢(xún)95%用戶(hù)查詢(xún)的門(mén)限值對(duì)應(yīng)的響應(yīng)時(shí)間是多少。 使用Histogram和Summary指標(biāo)的時(shí)候同時(shí)會(huì)產(chǎn)生多組數(shù)據(jù)签钩,_count代表了采樣的總數(shù)掏呼,_sum則代表采樣值的和。 _bucket則代表了落入此范圍的數(shù)據(jù)铅檩。

下面是使用historam來(lái)定義的一組指標(biāo)憎夷,計(jì)算出了平均五分鐘內(nèi)的查詢(xún)請(qǐng)求小于0.3s的請(qǐng)求占比總量的比例值。

sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
/
  sum(rate(http_request_duration_seconds_count[5m])) by (job)

如果需要聚合數(shù)據(jù)昧旨,可以使用histogram. 并且如果對(duì)于分布范圍有明確的值的情況下(比如300ms)拾给,也可以使用histogram。但是如果僅僅是一個(gè)百分比的值(比如上面的95%),則使用Summary

  1. 定義指標(biāo)
    這里我們需要引入另一個(gè)依賴(lài)庫(kù)
go get github.com/prometheus/client_golang/prometheus

下面先來(lái)定義了兩個(gè)指標(biāo)數(shù)據(jù),一個(gè)是Guage類(lèi)型宁舰, 一個(gè)是Counter類(lèi)型。分別代表了CPU溫度和磁盤(pán)失敗次數(shù)統(tǒng)計(jì)额衙,使用上面的定義進(jìn)行分類(lèi)。

    cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
        Name: "cpu_temperature_celsius",
        Help: "Current temperature of the CPU.",
    })
    hdFailures = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "hd_errors_total",
            Help: "Number of hard-disk errors.",
        },
        []string{"device"},
    )

這里還可以注冊(cè)其他的參數(shù),比如上面的磁盤(pán)失敗次數(shù)統(tǒng)計(jì)上入偷,我們可以同時(shí)傳遞一個(gè)device設(shè)備名稱(chēng)進(jìn)去追驴,這樣我們采集的時(shí)候就可以獲得多個(gè)不同的指標(biāo)。每個(gè)指標(biāo)對(duì)應(yīng)了一個(gè)設(shè)備的磁盤(pán)失敗次數(shù)統(tǒng)計(jì)疏之。

  1. 注冊(cè)指標(biāo)
func init() {
    // Metrics have to be registered to be exposed:
    prometheus.MustRegister(cpuTemp)
    prometheus.MustRegister(hdFailures)
}

使用prometheus.MustRegister是將數(shù)據(jù)直接注冊(cè)到Default Registry,就像上面的運(yùn)行的例子一樣暇咆,這個(gè)Default Registry不需要額外的任何代碼就可以將指標(biāo)傳遞出去锋爪。注冊(cè)后既可以在程序?qū)用嫔先ナ褂迷撝笜?biāo)了,這里我們使用之前定義的指標(biāo)提供的API(Set和With().Inc)去改變指標(biāo)的數(shù)據(jù)內(nèi)容

func main() {
    cpuTemp.Set(65.3)
    hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()

    // The Handler function provides a default handler to expose metrics
    // via an HTTP server. "/metrics" is the usual endpoint for that.
    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(":8080", nil))
}

其中With函數(shù)是傳遞到之前定義的label=”device”上的值爸业,也就是生成指標(biāo)類(lèi)似于

cpu_temperature_celsius 65.3
hd_errors_total{"device"="/dev/sda"} 1

當(dāng)然我們寫(xiě)在main函數(shù)中的方式是有問(wèn)題的其骄,這樣這個(gè)指標(biāo)僅僅改變了一次,不會(huì)隨著我們下次采集數(shù)據(jù)的時(shí)候發(fā)生任何變化扯旷,我們希望的是每次執(zhí)行采集的時(shí)候拯爽,程序都去自動(dòng)的抓取指標(biāo)并將數(shù)據(jù)通過(guò)http的方式傳遞給我們。

  1. Counter數(shù)據(jù)采集實(shí)例
    下面是一個(gè)采集Counter類(lèi)型數(shù)據(jù)的實(shí)例钧忽,這個(gè)例子中實(shí)現(xiàn)了一個(gè)自定義的毯炮,滿足采集器(Collector)接口的結(jié)構(gòu)體,并手動(dòng)注冊(cè)該結(jié)構(gòu)體后耸黑,使其每次查詢(xún)的時(shí)候自動(dòng)執(zhí)行采集任務(wù)桃煎。

我們先來(lái)看下采集器Collector接口的實(shí)現(xiàn)

type Collector interface {
    // 用于傳遞所有可能的指標(biāo)的定義描述符
    // 可以在程序運(yùn)行期間添加新的描述,收集新的指標(biāo)信息
    // 重復(fù)的描述符將被忽略大刊。兩個(gè)不同的Collector不要設(shè)置相同的描述符
    Describe(chan<- *Desc)

    // Prometheus的注冊(cè)器調(diào)用Collect執(zhí)行實(shí)際的抓取參數(shù)的工作为迈,
    // 并將收集的數(shù)據(jù)傳遞到Channel中返回
    // 收集的指標(biāo)信息來(lái)自于Describe中傳遞,可以并發(fā)的執(zhí)行抓取工作缺菌,但是必須要保證線程的安全葫辐。
    Collect(chan<- Metric)
}

了解了接口的實(shí)現(xiàn)后,我們就可以寫(xiě)自己的實(shí)現(xiàn)了伴郁,先定義結(jié)構(gòu)體耿战,這是一個(gè)集群的指標(biāo)采集器,每個(gè)集群都有自己的Zone,代表集群的名稱(chēng)蛾绎。另外兩個(gè)是保存的采集的指標(biāo)昆箕。

type ClusterManager struct {
    Zone         string
    OOMCountDesc *prometheus.Desc
    RAMUsageDesc *prometheus.Desc
}

我們來(lái)實(shí)現(xiàn)一個(gè)采集工作,放到了ReallyExpensiveAssessmentOfTheSystemState函數(shù)中實(shí)現(xiàn),每次執(zhí)行的時(shí)候租冠,返回一個(gè)按照主機(jī)名作為鍵采集到的數(shù)據(jù)鹏倘,兩個(gè)返回值分別代表了OOM錯(cuò)誤計(jì)數(shù),和RAM使用指標(biāo)信息顽爹。

func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() (
    oomCountByHost map[string]int, ramUsageByHost map[string]float64,
) {
    oomCountByHost = map[string]int{
        "foo.example.org": int(rand.Int31n(1000)),
        "bar.example.org": int(rand.Int31n(1000)),
    }
    ramUsageByHost = map[string]float64{
        "foo.example.org": rand.Float64() * 100,
        "bar.example.org": rand.Float64() * 100,
    }
    return
}

實(shí)現(xiàn)Describe接口纤泵,傳遞指標(biāo)描述符到channel

// Describe simply sends the two Descs in the struct to the channel.
func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
    ch <- c.OOMCountDesc
    ch <- c.RAMUsageDesc
}

Collect函數(shù)將執(zhí)行抓取函數(shù)并返回?cái)?shù)據(jù),返回的數(shù)據(jù)傳遞到channel中,并且傳遞的同時(shí)綁定原先的指標(biāo)描述符捏题。以及指標(biāo)的類(lèi)型(一個(gè)Counter和一個(gè)Guage)

func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
    oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState()
    for host, oomCount := range oomCountByHost {
        ch <- prometheus.MustNewConstMetric(
            c.OOMCountDesc,
            prometheus.CounterValue,
            float64(oomCount),
            host,
        )
    }
    for host, ramUsage := range ramUsageByHost {
        ch <- prometheus.MustNewConstMetric(
            c.RAMUsageDesc,
            prometheus.GaugeValue,
            ramUsage,
            host,
        )
    }
}

創(chuàng)建結(jié)構(gòu)體及對(duì)應(yīng)的指標(biāo)信息,NewDesc參數(shù)第一個(gè)為指標(biāo)的名稱(chēng)玻褪,第二個(gè)為幫助信息,顯示在指標(biāo)的上面作為注釋?zhuān)谌齻€(gè)是定義的label名稱(chēng)數(shù)組公荧,第四個(gè)是定義的Labels

func NewClusterManager(zone string) *ClusterManager {
    return &ClusterManager{
        Zone: zone,
        OOMCountDesc: prometheus.NewDesc(
            "clustermanager_oom_crashes_total",
            "Number of OOM crashes.",
            []string{"host"},
            prometheus.Labels{"zone": zone},
        ),
        RAMUsageDesc: prometheus.NewDesc(
            "clustermanager_ram_usage_bytes",
            "RAM usage as reported to the cluster manager.",
            []string{"host"},
            prometheus.Labels{"zone": zone},
        ),
    }
}

執(zhí)行主程序

func main() {
    workerDB := NewClusterManager("db")
    workerCA := NewClusterManager("ca")

    // Since we are dealing with custom Collector implementations, it might
    // be a good idea to try it out with a pedantic registry.
    reg := prometheus.NewPedanticRegistry()
    reg.MustRegister(workerDB)
    reg.MustRegister(workerCA)
}

如果直接執(zhí)行上面的參數(shù)的話带射,不會(huì)獲取任何的參數(shù),因?yàn)槌绦驅(qū)⒆詣?dòng)推出循狰,我們并未定義http接口去暴露數(shù)據(jù)出來(lái)窟社,因此數(shù)據(jù)在執(zhí)行的時(shí)候還需要定義一個(gè)httphandler來(lái)處理http請(qǐng)求。

添加下面的代碼到main函數(shù)后面绪钥,即可實(shí)現(xiàn)數(shù)據(jù)傳遞到http接口上:

gatherers := prometheus.Gatherers{
        prometheus.DefaultGatherer,
        reg,
    }

    h := promhttp.HandlerFor(gatherers,
        promhttp.HandlerOpts{
            ErrorLog:      log.NewErrorLogger(),
            ErrorHandling: promhttp.ContinueOnError,
        })
    http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
    })
    log.Infoln("Start server at :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Errorf("Error occur when start server %v", err)
        os.Exit(1)
    }

其中prometheus.Gatherers用來(lái)定義一個(gè)采集數(shù)據(jù)的收集器集合灿里,可以merge多個(gè)不同的采集數(shù)據(jù)到一個(gè)結(jié)果集合,這里我們傳遞了缺省的DefaultGatherer程腹,所以他在輸出中也會(huì)包含go運(yùn)行時(shí)指標(biāo)信息匣吊。同時(shí)包含reg是我們之前生成的一個(gè)注冊(cè)對(duì)象,用來(lái)自定義采集數(shù)據(jù)寸潦。

promhttp.HandlerFor()函數(shù)傳遞之前的Gatherers對(duì)象色鸳,并返回一個(gè)httpHandler對(duì)象,這個(gè)httpHandler對(duì)象可以調(diào)用其自身的ServHTTP函數(shù)來(lái)接手http請(qǐng)求甸祭,并返回響應(yīng)缕碎。其中promhttp.HandlerOpts定義了采集過(guò)程中如果發(fā)生錯(cuò)誤時(shí),繼續(xù)采集其他的數(shù)據(jù)池户。

嘗試刷新幾次瀏覽器獲取最新的指標(biāo)信息

clustermanager_oom_crashes_total{host="bar.example.org",zone="ca"} 364
clustermanager_oom_crashes_total{host="bar.example.org",zone="db"} 90
clustermanager_oom_crashes_total{host="foo.example.org",zone="ca"} 844
clustermanager_oom_crashes_total{host="foo.example.org",zone="db"} 801
# HELP clustermanager_ram_usage_bytes RAM usage as reported to the cluster manager.
# TYPE clustermanager_ram_usage_bytes gauge
clustermanager_ram_usage_bytes{host="bar.example.org",zone="ca"} 10.738111282075208
clustermanager_ram_usage_bytes{host="bar.example.org",zone="db"} 19.003276633920805
clustermanager_ram_usage_bytes{host="foo.example.org",zone="ca"} 79.72085409108028
clustermanager_ram_usage_bytes{host="foo.example.org",zone="db"} 13.041384617379178

每次刷新的時(shí)候咏雌,我們都會(huì)獲得不同的數(shù)據(jù),類(lèi)似于實(shí)現(xiàn)了一個(gè)數(shù)值不斷改變的采集器校焦。當(dāng)然赊抖,具體的指標(biāo)和采集函數(shù)還需要按照需求進(jìn)行修改,滿足實(shí)際的業(yè)務(wù)需求寨典。

  1. 添加自己的時(shí)間戳(NewMetricWithTimestamp)
    因?yàn)闃I(yè)務(wù)需要氛雪,數(shù)據(jù)上報(bào)需要嚴(yán)格按照自己的時(shí)間戳進(jìn)行數(shù)據(jù)的處理
desc := prometheus.NewDesc(
    "temperature_kelvin",
    "Current temperature in Kelvin.",
    nil, nil,
)

// Create a constant gauge from values we got from an external
// temperature reporting system. Those values are reported with a slight
// delay, so we want to add the timestamp of the actual measurement.
temperatureReportedByExternalSystem := 298.15
timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC)
s := prometheus.NewMetricWithTimestamp(
    timeReportedByExternalSystem,
    prometheus.MustNewConstMetric(
        desc, prometheus.GaugeValue, temperatureReportedByExternalSystem,
    ),
)

// Just for demonstration, let's check the state of the gauge by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := &dto.Metric{}
s.Write(metric)
fmt.Println(proto.MarshalTextString(metric))

參考資料:https://blog.csdn.net/u014029783/article/details/80001251
https://godoc.org/github.com/prometheus/client_golang/prometheus#NewMetricWithTimestamp

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耸成,隨后出現(xiàn)的幾起案子报亩,更是在濱河造成了極大的恐慌,老刑警劉巖井氢,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弦追,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡花竞,警方通過(guò)查閱死者的電腦和手機(jī)劲件,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人零远,你說(shuō)我怎么就攤上這事苗分。” “怎么了牵辣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵摔癣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我纬向,道長(zhǎng)供填,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任罢猪,我火速辦了婚禮,結(jié)果婚禮上叉瘩,老公的妹妹穿的比我還像新娘膳帕。我一直安慰自己,他們只是感情好薇缅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布危彩。 她就那樣靜靜地躺著,像睡著了一般泳桦。 火紅的嫁衣襯著肌膚如雪汤徽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天灸撰,我揣著相機(jī)與錄音谒府,去河邊找鬼。 笑死浮毯,一個(gè)胖子當(dāng)著我的面吹牛完疫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播债蓝,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼壳鹤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饰迹?” 一聲冷哼從身側(cè)響起芳誓,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啊鸭,沒(méi)想到半個(gè)月后锹淌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莉掂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年葛圃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡库正,死狀恐怖曲楚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情褥符,我是刑警寧澤龙誊,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布喷楣,位于F島的核電站趟大,受9級(jí)特大地震影響铣焊,放射性物質(zhì)發(fā)生泄漏逊朽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一叽讳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坟募,春花似錦岛蚤、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至她紫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜂奸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工扩所, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留围详,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓祖屏,卻偏偏與公主長(zhǎng)得像助赞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袁勺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Prometheus是什么雹食? Prometheus 是由 SoundCloud 開(kāi)源監(jiān)控告警解決方案,從 2012...
    tonyyan閱讀 6,962評(píng)論 0 5
  • Prometheus 是什么群叶? Prometheus是一套開(kāi)源的監(jiān)控&報(bào)警&時(shí)間序列數(shù)據(jù)庫(kù)的組合吃挑,起始是由Soun...
    上弦月Tt閱讀 10,784評(píng)論 1 6
  • 文章目的: 1街立、向沒(méi)聽(tīng)過(guò)或者剛聽(tīng)過(guò)但是還對(duì)這個(gè)監(jiān)控系統(tǒng)沒(méi)有任何概念的開(kāi)發(fā)者介紹Prometheus的應(yīng)用場(chǎng)景舶衬。2、...
    whaike閱讀 39,629評(píng)論 15 59
  • Prometheus Prometheus是一套開(kāi)源的監(jiān)控&報(bào)警&時(shí)間序列數(shù)據(jù)庫(kù)的組合,起始是由SoundClou...
    YichenWong閱讀 19,323評(píng)論 0 6
  • 我最?lèi)?ài)你的你 該怎么訴說(shuō) 衷腸是如此蒼白 我為你顫抖 在無(wú)聲無(wú)息的夜 所有的愛(ài)你的模樣和背包 都被寄去 下個(gè)路口 ...
    微木頭閱讀 406評(píng)論 8 3