Prometheus有很多第三方的exporter,它们做为数据源为prometheus提供了很多有意义的监控指标数据,本文就来研究一下如何创建一个自定义的exporter。但是在实战之前,我们得先普及一些理论知识,这可能有些枯燥,但了解一些基本概念会有助于我们去理解和正确创建指标数据。
数据类型
metric是prometheus监控的主要目标,prometheus对于metric规定了4种类型,分别是Counter,Gauge,Histogram,Summary,不过与其说规定,倒不如说是建议更合适,因为Prometheus并不强制一定要按照这种类型规范去定义,就连Prometheus在存储这些数据的时候也并没有按照类型分类,所以这只能说是一种规范标准。
Counter
适合持续递增或递减类度量值,如网站访问次数,api调用次数,对于业务系统而言比如订单数等;
Gauge
类似Counter,但数据也可反向,可递增也可递减,如当前可用内存数及使用率,房屋的出租率等
Histogram
直译过来叫柱状图,比较适合统计在某些指定数值范围内的数据分布情况。使用此种类型,需要预估范围---buckets,之后会自动创建事件发生的总数,所有事件产生的值的综合,事件产生的值命中buckets的数量。通常用于统计分位数的直方图。
Summary
类似Histogram,区别是在计算分位数方面,histogram是在服务器通过函数计算,summary则是在客户端就定义。所以summary在计算分位数时性能会优于histogram,当然了,summary给客户端的压力也会较大。
对于这些类型,Counter和Gauge还是比较好理解的,但是Histogram和Summary就没那么好理解了。不过大部分监控都可以通过Counter和Gauge实现,只有一些特殊场景才可能用到Histogram和Summary。
创建Exporter
Prometheus提供了大量的官方以及第三方的exporters:
https://prometheus.io/docs/instrumenting/exporters/
它们支持默认的pull模式获取数据,这也是官方推荐的方式,但如果因为一些网络或防火墙等原因无法直接pull到数据的情况,就要借助Pushgateway让Prometheus转换为push方式获取数据,下面将分别介绍。
先聊pull模式,要求被监控对象能够提供exporter服务给Prometheus,实际上就是一个host地址。对于官方或第三方提供的exporters他们都实现了这些,但如果我们要监控的对象是某一个具体的应用,Prometheus也提供了很多语言的客户端库:
https://prometheus.io/docs/instrumenting/clientlibs/
如Go,Java等,github上也有包括C#在内其他语言支持。在这里我们以C#
https://github.com/prometheus-net/prometheus-net
为例做一些测试。
metricServer =newMetricServer(port:1234);
metricServer.Start();
这将启动监听localhost:1234端口,一个exporter就这么简单的创建了。
提示:这里要注意将测试程序的运行模式提权为管理员模式运行,或者执行
netsh http add urlacl url=http://+:1234/metricsuser=DOMAIN\user
否则Start方法将产生拒绝访问异常
Prometheus照例要在prometheus.yml中加入该节点的配置,这里就此略过。
如图,可以正常pull到。可以看到默认地址仍然是metrics路径。
接下来我们来尝试加入一些metrics,比如我想统计不同按钮的点击次数,可以这样:
counter =Metrics.CreateCounter("pushinterface_button_click_total","Push Interface count buttion click total.","name");
privatevoidbtnTestButton1_Click(objectsender,EventArgse)
{
switch(((Button)sender).Name)
{
case"btnTestButton1":
counter.Labels("TestButton1").Inc();
break;
case"btnTestButton2":
counter.Labels("TestButton2").Inc();
break;
}
}
我创建了名为pushinterface_button_click_total的metric,并且加入了名为name的label,利用label特性,我们可以很好的过滤筛选和分组数据。
如图,在metrics地址也可以看到pushinterface_button_click_total创建,包括它的描述和metrictype等,并且也可通过name标签值来分别确定不同按钮被点击的总数。
接下来我们来尝试创建GAUGE类型的metric。
gauge =Metrics.CreateGauge("pushinterface_num_workthreads","Push Interface work threads");
privatevoidbtnThread_Click(objectsender,EventArgse)
{
Task.Factory.StartNew(() =>
{
gauge.Inc();
Thread.Sleep(newRandom().Next(10000,100000));
})
.ContinueWith(task => { gauge.Dec(); });
}
当我点击Thread按钮,将创建一个随机工作时间的线程,并且gauge指标+1,而当任务结束则gauge-1,结果如下:
可以看到pushinterface_num_workthreads已经创建,并且当前有3个工作任务正在运行。
提示:Prometheus对metric的命名也有一个推荐,一般是应用名称_监控对象_数值类型_单位,并且要符合
[a-zA-Z_:][a-zA-Z0-9_:]*的正则表达式,关于metric和label的命名规范请参照官方文档:https://prometheus.io/docs/practices/naming/
Pushgateay
接下来我们来聊聊push模式。虽然说pull模式是官方推荐的,但对于某些情况下,比如我们将prometheus搭建在外网去监控内网应用的情况下,由于内网有诸多安全限制使得无法穿透,这时就要借助push模式来解决问题。prometheus提供了Pushgateway组件来实现,这里仍然通过docker安装:
docker pull prom/pushgateway
docker run -d -p9091:9091 prom/pushgateway "-persistence.file=push_file"
这里我加入了-persistence.file=push_file参数,这是为pushgateway指定的参数,默认pushgateway是不持久化数据的,这意味着如果shutdown,过去的数据将丢失,对于监控系统来说这通常没有什么问题,但如果有需要持久化数据的情况,就需要加入这个参数。
prometheus.yml仍然也要加入pushgateway的9091端口配置,这里略过。重启prometheus,会看到pushjob已经连接成功:
push模式需要app主动推送数据到pushgateway,prometheus监控pushgateway来得到数据。所以应用端仍然需要client类库的支持来实现,这里仍然以C#为例:
metricPusher =newMetricPusher(endpoint:"http://localhost:9091/metrics", job:"push_job");
metricPusher.Start();
仍然只有两行代码,就这么简单。
通过浏览器访问pushgateway的metrics地址,可以看到先前的诸如pushinterface_button_click_total等metrics都在,所以对于client而言,从pull切换到push几乎没有开发成本。
注意,将多个节点数据汇总到pushgateway, 如果 pushgateway 挂了,受影响比多个 target 大。Prometheus 拉取状态 up 只针对pushgateway, 无法做到对每个节点有效。 Pushgateway 可以持久化推送给它的所有监控数据。因此,即使你的监控已经下线,prometheus 还会拉取到旧的监控数据,需要手动清理 pushgateway 不要的数据。
有关prometheus的metric以及自定义exporter等等就先这么多,最后我想分享一些docker的小技巧。docker真是一个好东西,需要什么直接从dockerpull就好。这里要注意,很多新人容易走进一个误区,总是想往run出来的container里装东西,总是舍不得删除container,这些都是没有领会docker容器化技术的精髓---即用即抛。如果你决定采用容器虚拟方案,那么你根本不用关心容器本身的运行状况,或者说我们只要关注容器是否在正常运行就好,而如果容器挂掉了,我们并不需要了解具体挂掉的原因,直接删除再创建一个就是了。那有人问了,这容器自身所产生的配置的更改和业务数据不也一起丢掉了吗?其实我们可以利用docker的数据卷(volume)解决这个问题。
docker cp :/etc/prometheus/prometheus.yml d:\docker_map
docker stop
docker rm
docker run -p 9090:9090 -v d:\docker_map\prometheus.yml:/etc/prometheus/prometheus.ymlprom/prometheus
比如我想在本地系统持久化prometheus的配置文件,我可以先从正在运行的容器中将配置文件copy到本地,然后停止并删除容器,最后通过run命令重新创建一个容器,并指定了配置文件为之前copy到本地的文件即可。这样是不是很方便?
领取专属 10元无门槛券
私享最新 技术干货