函数计算场景中使用Prometheus SDK上报时序数据

SLS SDK支持写入时序数据,但手动维护一系列自定义指标的方式较为繁琐。Prometheus能自动生成多种维度的监控指标并内置维护标签信息,然而,它通常要求对外暴露一个HTTP接口,通过第三方采集器以Pull模式拉取时序数据。在函数计算场景中,由于计算服务无法直接提供此类HTTP接口,无法通过前述Pull模式上报时序数据。本文介绍一种利用Prometheus SDK并基于Push模式上报时序数据的方案。

前提条件

集成Prometheus

Prometheus能够自动生成多种维度的监控指标并内置维护了各种标签信息,结合Prometheus内置维护的指标信息,上报时序数据到时序库,根据不同场景,有两种模式。

方式

说明

Pull模式

Pull模式采集并上报指标数据,包含以下四个阶段:

  1. 在原项目中引入Prometheus SDK,使用SDK创建并维护各种指标项。

  2. 对外暴露一个/metrics的HTTP接口,采集进程(Prometheus、VMAgent、iLogtail等)定时访问该接口以获取编码后的指标数据。

  3. 采集进程解析从HTTP接口获取到的指标数据并转换成符合Prometheus RemoteWrite PB协议的编码数据。

  4. 采集进程请求SLS时序库的RemoteWrite HTTP接口写入时序数据。

Push模式

Push模式表示主动推送指标数据到时序库中。日志服务时序库支持两种主动上报时序数据的方式:

  1. 通过SLS SDK以日志格式将时序数据写入到时序库中。

  2. 使用Prometheus SDK以开源标准格式将时序数据写入到时序库中,详细见代码示例

代码示例

使用Prometheus SDK,可以将Pull模式中采集进程所执行的操作全部合并到原项目进程中。

Go语言

安装Go SDK。通过调用Gather()接口获取所有时序指标,之后再调用EncodeDecode接口即可实现。

package main

import (
	"bytes"
	"fmt"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/golang/snappy"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/expfmt"
	"github.com/prometheus/prometheus/util/fmtutil"

	"io/ioutil"
	"math/rand"
	"net/http"
)

var durationHistogram = prometheus.NewHistogram(
	prometheus.HistogramOpts{
		Name:    "server_handling_seconds",
		Buckets: prometheus.DefBuckets,
	},
)

var requestCount = prometheus.NewCounter(
	prometheus.CounterOpts{
		Name: "server_requests_count",
	},
)

func main() {
	prometheus.MustRegister(durationHistogram)

	// 分别替换 your-project-name, endpoint, your-project-name, your-metricstore-name
	u := fmt.Sprintf("https://%s.%s/prometheus/%s/%s/api/v1/write", "your-project-name", "endpoint", "your-project-name", "your-metricstore-name")

	for i := 0; i < 300; i++ {
		// 更新指标数据
		for j := 0; j < 100; j++ {
			val := rand.Float64() * 10
			durationHistogram.Observe(val)
			requestCount.Inc()
		}

		// 收集已注册的所有指标数据
		mfs, err := prometheus.DefaultGatherer.Gather()
		if err != nil {
			panic(err)
		}

		// 对指标数据进行编码处理
		// 注意:由于Histogram类型的指标是在Encode过程中添加 "+Inf" 这个le标签,所以此处的Encode过程不可省略。
		buf := bytes.NewBuffer(nil)
		encoder := expfmt.NewEncoder(buf, expfmt.NewFormat(expfmt.TypeTextPlain))
		for _, mf := range mfs {
			if err := encoder.Encode(mf); err != nil {
				panic(err)
			}
		}

		// 对编码后的指标数据做解析处理,并转化成RemoteWrite PB协议的Request
		// 当前示例对于错误处理直接结束进程,实际使用中需根据业务特点选择合适的处理方式,例如跳过或者退出重试等
		request, err := fmtutil.MetricTextToWriteRequest(buf, nil)
		if err != nil {
			panic(err)
		}

		// 对*prompb.WriteRequest做序列化并压缩
		data, _ := proto.Marshal(request)
		bufBody := snappy.Encode(nil, data)
		rwR, err := http.NewRequest("POST", u, ioutil.NopCloser(bytes.NewReader(bufBody)))
		if err != nil {
			panic(err)
		}
		rwR.Header.Add("Content-Encoding", "snappy")
		rwR.Header.Set("Content-Type", "application/x-protobuf")
		// 设置basic auth信息,即AccessKeyId和AccessKeySecret。
		rwR.SetBasicAuth("LTAI5t*************", "kaOAL9*************")

		// 往SLS时序库发送时序数据
		resp, err := http.DefaultClient.Do(rwR)
		if err != nil {
			panic(err)
		}
		d, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			panic(err)
		}
		fmt.Println(string(d))

		time.Sleep(time.Second)
	}

}

验证采集数据

  1. 登录日志服务控制台,在Project列表区域,单击目标Project。

  2. 时序存储 > 时序库页签中,单击目标MetricStore。image

  3. 在页面右上角,设置查询和分析的时间范围。输入PromQL语句,计算整体耗时情况的P50百分位数值,单击立即执行

    histogram_quantile(0.5, sum by (le) (rate(server_handling_seconds_bucket[1m])))

    image