nodeJS开发exporter + Prometheus + Grafana

170 阅读2分钟

Exporter实现

server.js中注册路由:

app.use('/metrics',monitorRouter)

monitor.js完成业务逻辑:

const express = require('express')  
const Prometheus = require('prom-client')  
const DashboardRepository = require("../repository/dashboard.repository");  
const DashboardController = require('../controller/dashboard.controller')  
const Util = require("../utils/commonFunction")  
  
const labelNames = [  
'id',  
'taskName',  
'station',  
'model',  
'sn',
]  
const monitorRouter = express.Router()  
const register = new Prometheus.Registry()  
const injectSuccessRate = new Prometheus.Gauge({  
    name:'inject_success_rate',  
    help:'Inject success rate'  
})  
const injectSuccessNum = new Prometheus.Gauge({  
    name:'inject_success_num',  
    help:'Inject success number'  
})  
const injectFailedNum = new Prometheus.Gauge({  
    name:'inject_failed_num',  
    help:'Inject failed number'  
})  
const injectWarningNum = new Prometheus.Gauge({  
    name:'inject_warning_num',  
    help:'Inject warning number'  
})  
const averageInjectRate = new Prometheus.Gauge({  
    name:'inject_average_rate',  
    help:'Average inject rate'  
})  
const taskStatus = new Prometheus.Gauge({  
    name:'task_status',  
    help:'Task status',  
    labelNames  
})  
const taskProgress = new Prometheus.Gauge({  
    name:'task_progress',  
    help:'Progress',  
    labelNames  
})  
const taskBeginTime = new Prometheus.Gauge({  
    name:'task_begin_time',  
    help:'Begin Time',  
    labelNames  
})  
const taskEndTime = new Prometheus.Gauge({  
    name:'task_end_time',  
    help:'End Time',  
    labelNames  
})  
const taskDataRate = new Prometheus.Gauge({  
    name:'task_data_rate',  
    help:'Data Rate',  
    labelNames  
})  
const taskFileSize = new Prometheus.Gauge({  
    name:'task_file_size',  
    help:'File Size',  
    labelNames  
})  
const taskExpectDuration = new Prometheus.Gauge({  
    name:'task_expect_duration',  
    help:'Expect Duration',  
    labelNames  
})  
const taskDuration = new Prometheus.Gauge({  
    name:'task_duration',  
    help:'Duration',  
    labelNames  
})  
  
register.registerMetric(injectSuccessRate)  
register.registerMetric(injectSuccessNum)  
register.registerMetric(injectFailedNum)  
register.registerMetric(injectWarningNum)  
register.registerMetric(averageInjectRate)  
register.registerMetric(taskStatus)  
register.registerMetric(taskProgress)  
register.registerMetric(taskBeginTime)  
register.registerMetric(taskEndTime)  
register.registerMetric(taskDataRate)  
register.registerMetric(taskFileSize)  
register.registerMetric(taskExpectDuration)  
register.registerMetric(taskDuration)  
  
  
monitorRouter.get('/',async(req,res)=>{  
res.set('Content-Type',Prometheus.register.contentType);  
const data = await DashboardController.getUploadStatics()  
const {successRate,successNum,failedNum,warningNum,injectRate} = data;  
injectSuccessRate.set(+successRate);  
injectSuccessNum.set(+successNum);  
injectFailedNum.set(+failedNum);  
injectWarningNum.set(+warningNum);  
averageInjectRate.set(+injectRate);  
  
let tasks = await DashboardRepository.getAllUploadTasks();  
tasks = await DashboardController.generateTaskResult(tasks);  
async function processTasks() {  
    for (const task of tasks) {  
        let {  
            id,taskName,station,model,  
            sn,progress,status,beginTime,  
            endTime,expectedDuration,spendTime,dataRate,fileSize  
        } = task;  
        await taskStatus.labels({  
            id,  
            taskName,  
            station,  
            model,  
            sn,
        }).set(+status);  
        await taskProgress.labels({  
            id,  
            taskName,  
            station,  
            model,  
            sn,
        }).set(+progress)  
        const timeBegin = new Date(beginTime);  
        const timeEnd = new Date(endTime);  
        await taskBeginTime.labels({  
            id,station,model,sn,taskName  
        }).set(timeBegin.getTime());  

        await taskEndTime.labels({  
            id,station,model,sn,taskName  
        }).set(timeEnd.getTime());  

        await taskDataRate.labels({  
            id,station,model,sn,taskName  
        }).set(+dataRate)  

        await taskFileSize.labels({  
            id,station,model,sn,taskName  
        }).set(+fileSize)  

        await taskExpectDuration.labels({  
            id,station,model,sn,taskName  
        }).set(Util.getDurationSeconds(expectedDuration))  
        await taskDuration.labels({  
            id,station,model,sn,taskName  
        }).set(Util.getDurationSeconds(spendTime))  
    }  
}  
await processTasks();  
    register.metrics().then(data=>res.status(200).send(data))  
})  

  
module.exports = monitorRouter

访问后端地址+"/metrics"即可访问exporter数据。 注意:设置的labels尽量设置为不会发生变化的常量,Prometheus中会将(指标名+其labels)标识为一个时间序列,当labels发生变化后,则成为一个新的时间序列,在后面grafana的聚合展示中会出现问题。 image.png

部署Prometheus

prometheus的yaml文件

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "inject_task_monitor"
    static_configs:
      - targets: ["10.51.22.248:32235"]

image.png

Grafana配置

效果

image.png

表格配置

Query

image.png

Transform

image.png

Convert field type

顾名思义,是用来改变指标数据类型的;因为项目需要以ID倒序,因此需要将ID的数据类型改为Number

image.png

Group by

以ID进行分组:

image.png

Organize fields

重写排列表格的列: image.png

Sort by

以ID倒序:

image.png

All & Overrides

依据项目需求自行配置: image.png

其他

  • 部署方式不在本文展开,依据需求部署;
  • 每次更改完grafana配置后自己的保存JSON,别白干了
  • Grafana的可玩性真的很高,作为一名前端er,不得不感慨,他们前端太强了~