使用程序向Prometheus Pushgateway推送指标(Protocol buffer版本)
概述
以下是介绍如何通过程序向Pushgateway推送指标的方法。
在推送可能度量的格式中,有两种选项:Text和Protocol buffer。
本文将介绍如何使用Protocol buffer格式进行推送。
请参考以下文章了解关于使用 Text 格式进行推送的内容。
通过程序向 Prometheus Pushgateway 推送指标 [Text format 篇]。
针对不同语言的库
由于「Prometheus」的客户端库已经悄悄地实现了向「Pushgateway」推送指标的方法,所以这可能是一个小小的遗漏,但是对于每种语言都有相应的库可用。
通常情况下,我认为以下库是最好的选择。
- Pushing metrics | Prometheus
此外,我并没有检查所有的库,但是我没有找到使用Protocol buffer格式进行推送的处理。
另外,在互联网上查阅了一下,我没有找到任何代码示例,即使用POST方法将Protocol buffer的字节流发送到Pushgateway,并附加上后续提到的分隔符信息,以便推送指标。如果有看到相关实现的朋友,请务必在评论区告诉我。
所以这一次我决定自己动手做。首先,我会介绍一下我参考的文件。
普罗米修斯客户端数据规范
以下文件中提供了Prometheus Client Data的格式说明。
- Prometheus Client Data Exposition Format – Google ドキュメント
如果您打算在协议缓冲区格式中进行推送,请注意以下内容。
32-Bit Varint-Encoded Record Length-Delimited をほどこす必要がある
Protocol buffer のバイト列全体のLengthをバイト列先頭に付与すると良さそうです。
protoファイル(metrics.proto)は以下リポジトリで公開されている
prometheus/client_model: Data model artifacts for Prometheus.
ちなみにGo言語用の構造体定義(metrics.pb.go)も用意されているのでGo言語で実装する場合はgo getして入手するだけで良いです。
念の為お使いのバージョンのPushGatewayの依存モジュールのclient_modelと同じバージョンのものを使うと良いかと思います。
Content-Typeヘッダの値はapplication/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimitedとする
下面是实施的示例。
实施示例
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/matttproud/golang_protobuf_extensions/pbutil"
dto "github.com/prometheus/client_model/go"
)
func main() {
// metrics
m := &dto.MetricFamily{
Name: proto.String("some_metric"),
Help: proto.String("Just an example."),
Type: dto.MetricType_COUNTER.Enum(),
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
{
Name: proto.String("label1"),
Value: proto.String("val1"),
},
{
Name: proto.String("label2"),
Value: proto.String("val2"),
},
},
Counter: &dto.Counter{
Value: proto.Float64(123.456),
},
},
},
}
fmt.Println("metric:", m)
// byte列を見てみよう
dump(m)
// push
push(m)
}
func dump(m proto.Message) {
// byte列に変換
marshaled, _ := proto.Marshal(m)
fmt.Println("metric marshaled: ", marshaled)
// 逆変換の例
unmarshaled := &dto.MetricFamily{}
_ = proto.Unmarshal(marshaled, unmarshaled)
fmt.Println("metric unmarshaled: ", unmarshaled)
}
func push(m proto.Message) {
// lenghをデリミタとしてbyte列の先頭に付与する
buf := &bytes.Buffer{}
_, err := pbutil.WriteDelimited(buf, m)
fmt.Println("metric delimited: ", buf.Bytes())
// post
err = post(buf.Bytes())
if err != nil {
fmt.Println("err: ", err)
}
}
func post(in []byte) error {
endpoint := "http://localhost:9091/metrics/job/some_job/instance/some_instance"
contentType := "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"
res, err := http.Post(endpoint, contentType, bytes.NewBuffer(in))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode >= 400 {
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return errors.New(res.Status)
}
return fmt.Errorf("%s: %s", res.Status, b)
}
return err
}
dump(m)是一段不需要用于指标推送处理的代码,而是用于字节序列比较的。
执行结果
运行后将输出如下日志。
metric: name:"some_metric" help:"Just an example." type:COUNTER metric:<label:<name:"label1" value:"val1" > label:<name:"label2" value:"val2" > counter:<value:123.456 > >
metric marshaled: [10 11 115 111 109 101 95 109 101 116 114 105 99 18 16 74 117 115 116 32 97 110 32 101 120 97 109 112 108 101 46 24 0 34 43 10 14 10 6 108 97 98 101 108 49 18 4 118 97 108 49 10 14 10 6 108 97 98 101 108 50 18 4 118 97 108 50 26 9 9 119 190 159 26 47 221 94 64]
metric unmarshaled: name:"some_metric" help:"Just an example." type:COUNTER metric:<label:<name:"label1" value:"val1" > label:<name:"label2" value:"val2" > counter:<value:123.456 > >
metric delimited: [78 10 11 115 111 109 101 95 109 101 116 114 105 99 18 16 74 117 115 116 32 97 110 32 101 120 97 109 112 108 101 46 24 0 34 43 10 14 10 6 108 97 98 101 108 49 18 4 118 97 108 49 10 14 10 6 108 97 98 101 108 50 18 4 118 97 108 50 26 9 9 119 190 159 26 47 221 94 64]
比较”metric marshaled”和”metric delimited”的字节序列时,我们可以发现”delimited”的字节序列开头附加了表示长度的值。
如果没有这个长度值,Pushgateway将无法进行反序列化,会返回400错误码的响应。
顺便说一下,当推送成功时,Pushgateway的WebUI将显示如下内容。
