go开发exporter并接入夜莺

开发与运维

0.前言

之前我们部署了夜莺,并且通过夜莺平台完成了监控指标的采集,仪表盘的指标演示,并且将监控告警通知发送给了企业微信。categraf集成了许多插件,可以采集许多种类的数据指标,包括mysql、redis和kafka,夜莺平台也集成了许多的仪表盘,只需要简单的操作就可以将采集的中间件数据展示出来。但是在日常工作中,很多时候要采集的数据并不能通过categraf或者其他探针直接采集到,这个时候就需要自行开发exporter来进行指标采集,今天我们就用go来开发一个exporter采集一些有趣的数据并集成到夜莺,然后再自制一个仪表盘展示采集的数据。

1.指标说明

下面有一条比较有趣的命令,用来采集今天测试用的指标,具体命令如下:

ss -tan | grep "^ESTAB" | grep -v "\[" | awk '{M[$NF]++}END{for(k in M){print(k, M[k])}}' | sort -nr

输出信息如下:

223.98.103.132:3856 1
223.160.228.8:54043 1
223.160.161.196:40056 1
223.160.161.196:40055 1
223.160.161.196:40054 1
223.160.161.196:39712 1
223.104.79.65:63968 1
223.104.74.188:63788 1
223.104.74.188:63787 1
223.104.74.188:63786 1
223.104.74.188:63785 1
223.104.74.188:63784 1
223.104.74.188:63783 1
223.104.69.177:4915 1
223.104.69.177:4914 1
223.104.69.177:4913 1
223.104.69.177:4912 1
.............

大家能看出来这个命令是做什么用的吗?
如果大家感兴趣的话,可以慢慢拆解命令,一点点的执行,就可以看出其中奥秘了,这个命令的主要作用是统计有哪些IP端口对该服务器的某些端口发起了请求,执行以下部分可以看的比较清楚:

ss -tan | grep "^ESTAB" | grep -v "\["
ESTAB      0      0      172.30.206.89:443                163.179.229.41:34300
ESTAB      0      0      172.30.206.89:443                218.59.236.18:50247
ESTAB      0      0      172.30.206.89:443                116.147.252.46:45995
ESTAB      0      0      172.30.206.89:443                116.147.252.46:58842
ESTAB      0      0      172.30.206.89:443                163.179.229.39:63710

显示结果里面,前面的IP端口是请求方,后面是接收方。
所以我们今天要采集的指标就是服务器接收的请求总量。

2.资源准备

IPCPU内存用途
10.173.100.158核32G夜莺平台、exporter所在服务器
172.30.206.898核32G用于数据采集

为了运行exporter,还需要在10.173.100.15上搭建好go语言环境。

3.exporter开发详解

接下来我们对exporter核心代码进行讲解,帮助大家理解。

3.1 核心依赖

prometheus为了让大家可以自行开发exporter,官方开发了对接模块,开发过程中需要导入的依赖有:

  • "github.com/prometheus/client_golang/prometheus"
  • "github.com/prometheus/client_golang/prometheus/promhttp"

3.2 核心代码

type insCollector struct {
    reqMetric   *prometheus.Desc
    countMetric *prometheus.Desc
}

以上代码为采集指标,当前需要采集两个指标,分别为:
(1)请求方详情(主要为每次请求的IP端口、请求数)
(2)请求总量。

func newInsCollector() *insCollector {
    return &insCollector{
       reqMetric: prometheus.NewDesc(
          fqName:"request_metrics",
          help:"collect request metrics from user",
          variableLabels:[]string{"ip", "val"}, 
          constLabels:nil,
       ),
       countMetric: prometheus.NewDesc("request_count",
          "collect request count from user",
          nil, nil),
    }
}

初始化采集指标,分别指定了指标名称(fqName)、指标帮助信息(help)、可变标签(variableLabels)和常量标签(constLabels)。

func (ic *insCollector) Describe(ch chan<- *prometheus.Desc) {
    ch <- ic.reqMetric
    ch <- ic.countMetric
}

func (ic *insCollector) Collect(ch chan<- prometheus.Metric) {
    collector := newInsCollector()
    instances, err := getRequestProcess()
    if err != nil {
       log.Fatal(err)
    }
    for _, instance := range instances {
       v, err := strconv.Atoi(instance.Value)
       if err != nil {
          log.Fatal(err)
       }
       metricValue := float64(v)
       m1 := prometheus.MustNewConstMetric(collector.reqMetric, prometheus.GaugeValue,
          metricValue, instance.IpAddr, instance.Value)
       ch <- m1
    }
    countValue := float64(len(instances))
    m2 := prometheus.MustNewConstMetric(collector.countMetric, prometheus.GaugeValue, countValue)
    ch <- m2
}

以上两个方法是每个采集器必须实现的,将指标的解释说明和具体数据传递给通道(channel),collect方法中的getRequestProcess函数用来采集具体指标,该函数返回的instances是一个Instance类型的列表,Instance类型和getRequestProcess函数具体代码如下:

type Instance struct {
    IpAddr string
    Value  string
}


func getRequestProcess() ([]Instance, error) {
    hostPort := fmt.Sprintf("%s:%s", sshHost, sshPort) 
    
    client, err := ssh.Dial("tcp", hostPort, &ssh.ClientConfig{
       User:            sshUser,
       Auth:            []ssh.AuthMethod{ssh.Password(password)},
       HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }) 
    if err != nil {
       return nil, err
    }
    defer client.Close()

    session, err := client.NewSession()
    if err != nil {
       return nil, err
    }
    defer session.Close()

    instanceList := []Instance{}
    
    cmd := "/usr/sbin/ss -tan | grep "^ESTAB" |grep -v "\[" | awk '{M[$NF]++}END{for(k in M){print(k, M[k])}}' | sort -nr"
    result, err := session.Output(cmd)
    if err != nil {
       return nil, err
    }
    for _, line := range strings.Split(string(result), "\n") {
       i := strings.Split(line, " ")
       if len(i) > 1 {
          instanceList = append(instanceList, Instance{i[0], i[1]})
       }
    }

    return instanceList, nil
}

在getRequestProcess中还用到了ssh模块,用来到指定服务器上运行命令,并获取命令结果。

var (
    sshUser  = "xxxxx"
    sshHost  = "172.30.206.89"
    sshPort  = "22"
    password = "xxxxxxxxxxx"
)

这个是ssh远程服务器会用到的配置,建议放到环境变量中。

http.Handle("/metrics", promhttp.Handler())

log.Println("Listening on :9091")
log.Fatal(http.ListenAndServe(":9091", nil))

代码的主函数,运行一个server服务,指定端口为9091。

import (
    "fmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "golang.org/x/crypto/ssh"
    "log"
    "net/http"
    "strconv"
    "strings"
)

代码的整体依赖,如果用goland编程工具的话,可以自动生成。

到此,所有的代码都过了一遍,整体还是很简单的,主要就是采集数据那一步,大家如果要采集其他指标,主要就是修改该部分逻辑,当然指标详情也得修改。

3.3 运行exporter

这里默认大家已经装了go语言环境,可以通过以下命令运行exporter:

cd /data/go/gopath/src/request_exporter (项目地址)
go mod init
go mod tidy
nohup go run main.go > server.log &

3.4 验证exporter服务

exporter正常运行之后,会暴露指标,上述代码上请求路径为http://主机IP:9091/metrics,我们可以请求一下,看能否获取想要的指标数据,我们可以先在exporter所在主机测试一下,命令如下:

curl 127.0.0.1:9091/metrics
....................
request_count 313
# HELP request_metrics collect request metrics from user
# TYPE request_metrics gauge
request_metrics{ip="100.100.30.25:80",val="1"} 1
request_metrics{ip="101.251.238.170:44607",val="1"} 1
request_metrics{ip="101.70.217.165:30454",val="1"} 1
request_metrics{ip="101.70.217.165:30558",val="1"} 1
request_metrics{ip="101.70.217.165:30659",val="1"} 1
request_metrics{ip="101.70.217.165:30702",val="1"} 1
request_metrics{ip="101.70.217.165:31069",val="1"} 1
request_metrics{ip="101.70.217.165:31269",val="1"} 1
.......................

可以看到确实获取到了我们需要的两个指标,request_count和request_metrics。

4.exporter接入prometheus

上面我们已经采集到了指标数据,接下来只需要将exporter接入prometheus,就可以在夜莺展示数据了。

4.1 修改prometheus配置

如果要将exporter接入prometheus,就需要修改prometheus配置,将exporter信息写入其中,如下所示:

static_configs:
      - targets: ["localhost:9090"]
       # The label name is added as a label `label_name=<label_value>` to any timeseries scraped from this config.
        labels:
          app: "prometheus"
      - targets: ["10.173.100.15:9091"]
        labels:
          app: "request-exporter"
          env: "test"

4.2 重启prometheus

修改完配置之后记得重启prometheus服务,命令如下:

kill -9 $PROPID(当前prometheus)
nohup ./prometheus --config.file=prometheus.yml --web.enable-remote-write-receiver &

4.3 指标查询

exporter接入prometheus之后,就可以在prometheus提供的简易界面查询指标了,promQL语句为:request_count和request_metrics,查询结果分别如下图所示:

picture.image

picture.image 可以看到以上两个指标数据都能查到。

5.夜莺添加仪表盘

之后就可以根据查询到的指标在夜莺新建仪表盘,如下图所示: picture.image 创建了两个图表,一个是时序图用来查看请求总数,一个是表格用来查看每条请求的具体信息。

6.总结

今天带大家开发了一个exporter,并接入夜莺,并通过夜莺的仪表盘展示数据。虽然篇幅不长,但是涉及的内容还是比较多的,需要掌握一些编程知识,还得熟悉prometheus和夜莺的使用。

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生环境下的日志采集存储分析实践
云原生场景下,日志数据的规模和种类剧增,日志采集、加工、分析的多样性也大大增加。面对这些挑战,火山引擎基于超大规模下的 Kubernetes 日志实践孵化出了一套完整的日志采集、加工、查询、分析、消费的平台。本次主要分享了火山引擎云原生日志平台的相关实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论