Prometheus 官方和社区提供了非常多的exporter,涵盖数据库、中间件、OS、存储、硬件设备等,具体可查看exportersexporterhub.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的更多相关文章

  1. 编写一个简单的基于jmespath 的prometheus exporter

    目的很简单,因为系统好多监控指标是通过json 暴露的,并不是标准的prometheus metrics 格式,处理方法 实际上很简单,我们可以基于jsonpath 解析json数据,转换为prome ...

  2. Go语言开发Prometheus Exporter示例

    一.Prometheus中的基本概念 Prometheus将所有数据存储为时间序列,这里先来了解一下prometheus中的一些基本概念 指标名和标签每个时间序列都由指标名和一组键值对(也称为标签)唯 ...

  3. prometheus exporter简介

    一.服务分类 在线服务:请求的客户端和发起者需要立即响应(高并发.低延迟:并发数.接口响应时间.错误数.延迟时间),面对突发流量能进行资源的自动伸缩 离线服务:请求发送到服务端但不要求立即获取结果(监 ...

  4. prometheus学习系列十一: Prometheus exporter详解

    exporter详解 前面的系列中,我们在主机上面安装了node_exporter程序,该程序对外暴露一个用于获取当前监控样本数据的http的访问地址, 这个的一个程序成为exporter,Expor ...

  5. prometheus+exporter小测试:

    1.golang中使用expoter import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) fu ...

  6. Prometheus exporter的Node exporter是可以独立安装,用来测试的

    现在慢慢在把prometheus operator的一些概念组织完整. https://github.com/coreos/prometheus-operator/tree/master/contri ...

  7. 使用grok exporter 做为log 与prometheus 的桥

    grok 是一个工具,可以用来解析非结构化的日志文件,可以使其结构化,同时方便查询,grok 被logstash 大量依赖 同时社区也提供了一个prometheus 的exporter 可以方便的进行 ...

  8. Prometheus之Exporter开发

    Prometheus开发Exporter简介 Exporter 本身是一个http 服务,其指标结果只要符合 Prometheus 规范就可以被 Prometheus 使用. Prometheus中m ...

  9. 使用 Prometheus + Grafana 对 Kubernetes 进行性能监控的实践

    1 什么是 Kubernetes? Kubernetes 是 Google 开源的容器集群管理系统,其管理操作包括部署,调度和节点集群间扩展等. 如下图所示为目前 Kubernetes 的架构图,由 ...

随机推荐

  1. 2021.12.19 eleveni的刷题记录

    2021.12.19 eleveni的刷题记录 0. 本次记录有意思的题 0.1 每个点恰好经过一次并且求最小时间 P2469 [SDOI2010]星际竞速 https://www.luogu.com ...

  2. docker基础_网络模式

    docker网络 网络模式: bridge:docker默认 自己创建会默认使用bridge模式 类似vmware中的NAT模式 其中192.168.1.203是本机在现实世界局域网的ip.172.1 ...

  3. HTML中的Hack手段之条件注释

    通常WEB的好处就是可以跨平台,但这个世界偏偏有个另类,就是IE浏览器.在平常做HTML设计时,有时需要为IE的表示差异而不得不使用一些Hack手段.条件注释就是这类手段之一. 条件注释是IE浏览器的 ...

  4. Bugku CTF练习题---MISC---贝斯手

    Bugku CTF练习题---MISC---贝斯手 flag:flag{this_is_md5_and_base58} 解题步骤: 1.观察题目,下载附件 2.下载后进行解压,得到了一张图片.一个压缩 ...

  5. Qt(QtWebEngine)加载本地网页跨域问题的总结

    目录 1. 概述 2. 详论 2.1. 传参 2.2. JS module 3. 建议 4. 参考 1. 概述 浏览器直接加载本地网页的时候,如果网页涉及到加载本地资源(如图片),会出现跨域的问题.Q ...

  6. 在windows上安装elasticsearch7.6

    在windows上安装elasticsearch7.6 安装Java1.8 下载Java1.8 提取码:yi0c 链接:https://pan.baidu.com/s/1mNd2Yg-k6ob91bO ...

  7. 2.1 安装Linux系统对硬件有什么要求?

    很多初学者在安装 Linux 系统时,都对自己的电脑配置存在质疑,担心其是否能够满足安装 Linux 的要求.本节就从 CPU.内存.硬盘.显卡等这些方面,详细介绍一下安装 Linux 系统的最低配置 ...

  8. NLP教程(6) - 神经机器翻译、seq2seq与注意力机制

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www.showmeai.tech/article-det ...

  9. 力扣算法:125-验证回文串,131-分割回文串---js

    LC 125-验证回文串 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写. 说明:本题中,我们将空字符串定义为有效的回文串. 注:回文串是正着读和反着读都一样的字符串. ...

  10. 890. Find and Replace Pattern - LeetCode

    Question 890. Find and Replace Pattern Solution 题目大意:从字符串数组中找到类型匹配的如xyy,xxx 思路: 举例:words = ["ab ...