ry's Tech blog

Cloud Native技術などについて書いていきます。

Golangで簡単にPrometheusのExporterを作れる。

GolangによるPrometheusのExporterの作成

やること

今回は、自作のExpoterの作り方の情報がすごい少ないなと感じたので とても簡単に作れるよというのをお見せできたらと思います。

事前準備

まず、prometheusのライブラリを取ってきましょう。

$ go get github.com/prometheus/client_golang/prometheus

インストルメンテーション

promethusはプル型のモニタリングシステムであり、監視対象にHTTPでアクセスしメトリクスを収集します。

すなわち、監視される側でprometheus形式のメトリクスを投げるHTTPのエンドポイントが必要となります。

それを提供するのがExporterでなんですが、自分好みの値を格納したいと思ってた時に、 それに適したExporterがないなんてことはざらにあります。

その際に、自分でExporterを作ることになるのですが、そこで使うのがインストルメンテーションです。


◯主要インストルメンテーション

  • カウンター: イベントの数およびサイズ等を追跡する。
  • ゲージ: あらゆる数値を格納

この他に、サマリ・ヒストグラムがある。

Code

以下がコードの全体像です。

// 初期定義

package main

import (
    "flag"
    "log"
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

// Metricsの定義
const (
    namespace = "SampleMetric"
)

type myCollector struct{} // 今回働いてくれるインスタンス

// metricsの記述子 「metricsの中に埋め込む情報の1つ(名前、#HELP に乗せる情報)であり、後にグラフで表示させるための数値以外のもの」

var (
    exampleCount = prometheus.NewCounter(prometheus.CounterOpts{
        Namespace: namespace,
        Name:      "example_count",
        Help:      "example counter help",
    })
    exampleGauge = prometheus.NewGauge(prometheus.GaugeOpts{
        Namespace: namespace,
        Name:      "example_gauge",
        Help:      "example gauge help",
    })
)

// Describe と Collect

func (c myCollector) Describe(ch chan<- *prometheus.Desc) {
    ch <- exampleCount.Desc()
    ch <- exampleGauge.Desc()
}

func (c myCollector) Collect(ch chan<- prometheus.Metric) {
    exampleValue := float64(12345)

    ch <- prometheus.MustNewConstMetric(
        exampleCount.Desc(),        //ここと
        prometheus.CounterValue,    //ここは固定
        float64(exampleValue),      //ここが、グラフに表示させたい数値
    )
    ch <- prometheus.MustNewConstMetric(
        exampleGauge.Desc(),
        prometheus.GaugeValue,
        float64(exampleValue),
    )
}

var addr = flag.String("listen-address", "127.0.0.1:5000", "The address to listen on for HTTP requests.")

func main() {
    flag.Parse()

    var c myCollector
    prometheus.MustRegister(c)

    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(*addr, nil))
}

では細かく見ていきます。

初期定義(prometheus用moduleのimport)

ここでは、package定義moduleのimportを記述します。

package main

import (
    "flag"
    "log"
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

基本、このライブラリはデフォルトでimportすることになると思います。

Metricsの定義

初めの1歩

const (
    namespace = "SampleMetric"
)

type myCollector struct{}

ここでは、Namespace(metricに与える名前の一部)をSampleMetricとして固定します。

そして、今回働いてくれるインスタンスをstructで作っておきます。

Metricの中身を決定

今回は上で紹介した、カウントとゲージを定義します。

var (
    exampleCount = prometheus.NewCounter(prometheus.CounterOpts{
        Namespace: namespace,
        Name:      "example_count",
        Help:      "example counter help",
    })
    exampleGauge = prometheus.NewGauge(prometheus.GaugeOpts{
        Namespace: namespace,
        Name:      "example_gauge",
        Help:      "example gauge help",
    })
)

ここで定義したものは、後にmetricを取得した際に以下のように表示されます。

# HELP SampleMetric_example_count example counter help
# TYPE SampleMetric_example_count counter
SampleMetric_example_count 12345
# HELP SampleMetric_example_gauge example gauge help
# TYPE SampleMetric_example_gauge gauge
SampleMetric_example_gauge 12345

metric名は、NameSpace + Nameをしたものが表示されています。

#Helpにおいて、Helpで定義したコメントが記述されます。

#Typeにおいては、今回用いるインストルメンテーションがmetic名の後に記述されます。

12345に関しては、次に説明するところで説明いたします。

CollectorとDescriber

自作Exporterではこの2つを定義しなければなりません。

Describer

func (c myCollector) Describe(ch chan<- *prometheus.Desc) {
    ch <- exampleCount.Desc()
    ch <- exampleGauge.Desc()
}

ここは、ほぼテンプレです。

metricの中身を定義した際に決定した変数名(今回はexampleCountとexampleGauge).Desc()をchに入れます。

Collector

func (c myCollector) Collect(ch chan<- prometheus.Metric) {
    exampleValue := float64(12345)

    ch <- prometheus.MustNewConstMetric(
        exampleCount.Desc(),        //ここと
        prometheus.CounterValue,    //ここは固定
        float64(exampleValue),      //ここが、グラフに表示させたい数値
    )
    ch <- prometheus.MustNewConstMetric(
        exampleGauge.Desc(),
        prometheus.GaugeValue,
        float64(exampleValue),
    )
}

ここで、変える必要があるのは、exampleValueのみです。

ここの数値に、サーバから取得した数値等様々な値を入れて下さい。

ここで定義した数値が、先ほどの取得したmetricの12345の箇所に入り、prometheusに格納されます。 この際、単調増加でないものはCounterに入れないようにして下さい。

それ以外は、コピペで大丈夫です。

最後のもう一押し

今回、addressを標準入力で取得できるように、flagを用意しています。

var addr = flag.String("address", "127.0.0.1:5000", "The address to get the metrics on the HTTP requests.")

func main() {
    flag.Parse()

    var c myCollector
    prometheus.MustRegister(c)

    http.Handle("/metrics", promhttp.Handler())
    log.Fatal(http.ListenAndServe(*addr, nil))
}

main関数では、値をparseして、インスタンスを生成させてMustRegisterに渡します。

最後、port番号の後に続くものをhttp.Handle()で定義し、http.ListenAndServe() でHTTPサーバを立ち上げます。

Exporterの起動

今回は、flagに定義したdefaultのIPにて起動をしたいと思います。

$ go run exporter.go

後は以下にアクセスしてみて下さい。

prometheus metric URL (http://localhost:5000/metrics)

するとこのようなものが表示されれば、完成です。

prometheus metric from golang

prometheus metric

最後に

前回の記事で書いたGrafana+Prometheusに今回のExporterを組み合わせることで、Prometheusがより一層力を増すのでぜひ使ってみて下さい