实现一个Prometheus exporter
Prometheus 官方和社区提供了非常多的exporter,涵盖数据库、中间件、OS、存储、硬件设备等,具体可查看exporters、exporterhub.io,通过这些 exporter 基本可以覆盖80%的监控需求,依然有小部分需要通过自定义脚本或者定制、修改社区exporter实现。本文我们将学习如何通过go编写一个简单的expoter用于暴露OS的负载。
要实现的三个load指标如下:
exporter的核心是http服务,对外暴露exporter本身运行时指标和监控信息。我们可以直接通过net/http
暴力实现,更好的方式是使用Prometheus 官方提供的client library 来简化一部分工作。
client library官方支持语言:
也有社区支持的其他语言库如C、C++、PHP等
获取数据源
在使用client library暴露数据之前,我们得先找到数据源,以linux为例要获取系统负载我们可以读取/proc目录下的loadavg文件。涉及到各类操作系统指标的获取可以参考官方的node-exporter,这里我们给他写成load包,等会直接调用GetLoad()就能拿到数据了。
package collect
import (
"fmt"
"io/ioutil"
"strconv"
"strings"
)
// The path of the proc filesystem.
var procPath = "/proc/loadavg"
// Read loadavg from /proc.
func GetLoad() (loads []float64, err error) {
data, err := ioutil.ReadFile(procPath)
if err != nil {
return nil, err
}
loads, err = parseLoad(string(data))
if err != nil {
return nil, err
}
return loads, nil
}
// Parse /proc loadavg and return 1m, 5m and 15m.
func parseLoad(data string) (loads []float64, err error) {
loads = make([]float64, 3)
parts := strings.Fields(data)
if len(parts) < 3 {
return nil, fmt.Errorf("unexpected content in %s", procPath)
}
for i, load := range parts[0:3] {
loads[i], err = strconv.ParseFloat(load, 64)
if err != nil {
return nil, fmt.Errorf("could not parse load '%s': %w", load, err)
}
}
return loads, nil
}
通过client_golang暴露指标
开通我们提到exporter要暴露的指标包含两部分,一是本身的运行时信息,另一个监控的metrics。而运行时信息client_golang
已经帮我们实现了,我们要做的是通过client_golang
包将监控数据转换为metrics后再暴露出来。
一个最基础使用client_golang
包示例如下:
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":2112", nil)
}
promhttp.Handler()
封装了本身的 go 运行时 metrics,并按照metircs后接value的格式在前端输出。
当我们访问2112端口的metrics路径时得到如下数据:
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 7
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.15.14"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
...
如何暴露自定义metrics呢?
先看如下的示例:
package main
import (
"net/http"
"time"
"log"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func recordMetrics() {
go func() {
for {
opsProcessed.Inc()
time.Sleep(2 * time.Second)
}
}()
}
var (
opsProcessed = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "myapp",
Name: "processed_ops_total",
Help: "The total number of processed events",
})
)
func main() {
prometheus.MustRegister(opsProcessed)
recordMetrics()
http.Handle("/metrics", promhttp.Handler())
log.Print("export /metrics on port :8085")
http.ListenAndServe(":8085", nil)
}
示例来自于官方仓库,做了稍加修改。可以看到使用NewCounter方法可以很快地帮我们创建一个Prometheus Counter数据类型实例。
Counter接口的定义包含了Counter本身的特性-只能增加即Inc和Add,同时还包含Meterics、Collector接口
Collector还包含2个方法,待会我们写自己的Collector时需要实现这两个方法。
type Collector interface {
Describe(chan<- *Desc)
Collect(chan<- Metric)
}
CounterOpts 来源于metrics.go 的Ops结构体定义了构成metrics的基本结构。
接着将opsProcessed这个Counter进行注册,所谓注册也就是让Handler
跟踪这个Counter中的metircs和collector
运行后,访问/metircs可以看到自定义指标myapp_processed_ops_total通过定时的Inc()调用来更新value
# HELP myapp_processed_ops_total The total number of processed events
# TYPE myapp_processed_ops_total counter
myapp_processed_ops_total 15
下面我们通过自定义collector实现一个简易的exporter
目录结构如下:
# tree .
.
├── collect
│ ├── collector.go
│ └── loadavg.go
├── go.mod
├── go.sum
└── main.go
loadavg.go即上面的获取数据源。
collector.go如下:
package collect
import (
"log"
"github.com/prometheus/client_golang/prometheus"
)
var namespace = "node"
type loadavgCollector struct {
metrics []typedDesc
}
type typedDesc struct {
desc *prometheus.Desc
valueType prometheus.ValueType
}
func NewloadavgCollector() *loadavgCollector {
return &loadavgCollector{
metrics: []typedDesc{
{prometheus.NewDesc(namespace+"_load1", "1m load average.", nil, nil), prometheus.GaugeValue},
{prometheus.NewDesc(namespace+"_load5", "5m load average.", , nil), prometheus.GaugeValue},
{prometheus.NewDesc(namespace+"_load15", "15m load average.", nil, nil), prometheus.GaugeValue},
},
}
}
//Each and every collector must implement the Describe function.
//It essentially writes all descriptors to the prometheus desc channel.
func (collector *loadavgCollector) Describe(ch chan<- *prometheus.Desc) {
//Update this section with the each metric you create for a given collector
ch <- collector.metrics[1].desc
}
//Collect implements required collect function for all promehteus collectors
func (collector *loadavgCollector) Collect(ch chan<- prometheus.Metric) {
//Implement logic here to determine proper metric value to return to prometheus
//for each descriptor or call other functions that do so.
loads, err := GetLoad()
if err != nil {
log.Print("get loadavg error: ", err)
}
//Write latest value for each metric in the prometheus metric channel.
//Note that you can pass CounterValue, GaugeValue, or UntypedValue types here.
for i, load := range loads {
ch <- prometheus.MustNewConstMetric(collector.metrics[i].desc, prometheus.GaugeValue, load)
}
}
collector中每一个要暴露的metrics都需要包含一个metrics描述即desc,都需要符合prometheus.Desc结构,我们可以直接使用NewDesc来创建。这里我们创建了三个metircs_name分别为node_load1、node_load5、node_15以及相应的描述,也可以加上对应的label。
接着实现collector的两个方法Describe、Collect分别写入对应的发送channel,其中prometheus.Metric的通道传入的值还包括三个load的value
最后在主函数中注册collector
prometheus.MustRegister(collect.NewloadavgCollector())
在Prometheus每个请求周期到达时都会使用GetLoad()获取数据,转换为metircs,发送给Metrics通道,http Handler处理和返回。
实现一个指标丰富、可靠性高的exporter感觉还是有一些困难的,需要对Go的一些特性以及Prometheus client包有较深入的了解。本文是对exporter编写的简单尝试,如实现逻辑、方式或理解不准确可参考开源exporter和官方文档。
文章涉及代码可查看:exporter
通过博客阅读:iqsing.github.io
实现一个Prometheus exporter的更多相关文章
- 编写一个简单的基于jmespath 的prometheus exporter
目的很简单,因为系统好多监控指标是通过json 暴露的,并不是标准的prometheus metrics 格式,处理方法 实际上很简单,我们可以基于jsonpath 解析json数据,转换为prome ...
- Go语言开发Prometheus Exporter示例
一.Prometheus中的基本概念 Prometheus将所有数据存储为时间序列,这里先来了解一下prometheus中的一些基本概念 指标名和标签每个时间序列都由指标名和一组键值对(也称为标签)唯 ...
- prometheus exporter简介
一.服务分类 在线服务:请求的客户端和发起者需要立即响应(高并发.低延迟:并发数.接口响应时间.错误数.延迟时间),面对突发流量能进行资源的自动伸缩 离线服务:请求发送到服务端但不要求立即获取结果(监 ...
- prometheus学习系列十一: Prometheus exporter详解
exporter详解 前面的系列中,我们在主机上面安装了node_exporter程序,该程序对外暴露一个用于获取当前监控样本数据的http的访问地址, 这个的一个程序成为exporter,Expor ...
- prometheus+exporter小测试:
1.golang中使用expoter import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) fu ...
- Prometheus exporter的Node exporter是可以独立安装,用来测试的
现在慢慢在把prometheus operator的一些概念组织完整. https://github.com/coreos/prometheus-operator/tree/master/contri ...
- 使用grok exporter 做为log 与prometheus 的桥
grok 是一个工具,可以用来解析非结构化的日志文件,可以使其结构化,同时方便查询,grok 被logstash 大量依赖 同时社区也提供了一个prometheus 的exporter 可以方便的进行 ...
- Prometheus之Exporter开发
Prometheus开发Exporter简介 Exporter 本身是一个http 服务,其指标结果只要符合 Prometheus 规范就可以被 Prometheus 使用. Prometheus中m ...
- 使用 Prometheus + Grafana 对 Kubernetes 进行性能监控的实践
1 什么是 Kubernetes? Kubernetes 是 Google 开源的容器集群管理系统,其管理操作包括部署,调度和节点集群间扩展等. 如下图所示为目前 Kubernetes 的架构图,由 ...
随机推荐
- Jenkins+Allure测试报告+飞书机器人发送通知
一.前言 之前讲了jenkins如何设置定时任务执行脚本,结合实际情况,本篇讲述在jenkins构建成功后,如何生成测试报告,以及推送飞书(因为我公司用的是飞书,所以是发送到飞书机器人). 本次实践搞 ...
- python基础练习题(题目 学习使用auto定义变量的用法)
day28 --------------------------------------------------------------- 实例042:变量作用域 题目 学习使用auto定义变量的用法 ...
- properties、yml配置文件映射对象
1.properties文件内容映射到类对象(属性),如Resource目录下的1.properties文件已配置前缀为com.imooc.people相关的信息,然后: pom添加依赖:spring ...
- B08. BootstrapBlazor实战 Menu 导航菜单使用(2)
接上篇: B08. BootstrapBlazor实战 Menu 导航菜单使用(1) 3.项目模板 节省时间,直接使用 Bootstrap Blazor App 模板快速搭建项目 传送门: https ...
- 配置Linux的时钟同步
公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ Ubuntu系统默认的时钟同步服务器是ntp.ubuntu.com,Debian则是0.debian.pool.ntp.or ...
- 从旧金山到上海, HTTP/3 非常快!
HTTP/3 是超文本传输协议 (HTTP) 的第三个版本,它对 Web 性能来说意义重大, 让我们看看HTTP/3 如何让网站的速度变得更快! 等等,HTTP/2 发生了什么? 不是几年前才开始推广 ...
- 流量治理神器-Sentinel 究竟是怎么做到让业务方接入简单?
大家好,我是架构摆渡人,这是流量治理系列的第10篇原创文章,如果有收获,还请分享给更多的朋友. 做业务开发,需要考虑业务的扩展性.做基础框架开发,需要考虑如何让业务方接入,使用简单,尽量不要耦合在业务 ...
- 【Java面试】Zookeeper中的Watch机制的原理?
一个工作了7年的粉丝,遇到了一个Zookeeper的问题. 因为接触过Zookeeper这个技术,不知道该怎么回答. 我说一个工作了7年的程序员,没有接触过主流技术,这不正常. 于是我问了他工资以后, ...
- Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ...
- el-form 中的数组表单验证(数组可动态添加删除)
除了一些简单的表单验证之外,我们还会有一些稍微复杂点的多层级表单的验证,如下图所示可点击添加,删除对数组进行操作,当点击确定时需要验证每一条form-item不能为空 其tempalte部分主要代码如 ...