读取prometheus的api生成报表输出到xls

一、prometheus的http_api使用

官方API
中文文档

Prometheus API 使用了 JSON 格式的响应内容。 输入时间戳可以由 RFC3339 格式或者 Unix 时间戳提供,后面可选的小数位可以精确到亚秒级别。输出时间戳以 Unix 时间戳的方式呈现。所有的 API请求返回的格式均使用JSON 格式。主要有以下几种查询类型:瞬时查询、范围查询、元数据查询、配置查询。

  • 其中瞬时查询和范围查询统称为表达式查询,都是对通过promQL进行的数据结果查询。别通过 /api/v1/query 和 /api/v1/query_range 查询 PromQL 表达式瞬时或者一定时间范围内的查询结果。当 API 调用成功后,Prometheus 会返回 JSON 格式的响应内容。表达式查询结果会在 data 部分的 result 字段中返回以下的响应值。详见下一节promql语法。
  • 元数据查询、配置查询是对prometheus配置的信息的查询比如节点、主机名、状态、报警规则等的查询。
名称 语句
查询分组 ip:prot/api/v1/label/job/values
查询节点 ip:prot/api/v1/targets
查询瞬时向量 ip:prot/api/v1/query?query=node_disk_io_now
区间向量 ip:prot/api/v1/query_range?query=sum(node_memory_MemFree_bytes)&start=1596211200&end=1598716800&step=24h
查询server标签 ip:prot/api/v1/labels
查询存活的主机 ip:prot/api/v1/series?match[]=up
查询节点 ip:prot/api/v1/label/nodename/values
查询标签值 ip:prot/api/v1/label/<label_name>/values
查询节点标签 ip:prot/metrics
查询节点元数据 ip:prot/api/v1/targets/metadata --data-urlencode 'match_target={instance="127.0.0.1:9090"}'
警报规则查询 ip:prot/api/v1/rules
警报查询 ip:prot/api/v1/alerts
配置查询 ip:prot/api/v1/status/config
flags标志查询 ip:prot/api/v1/status/flags
超级管理员查询 快照、删除序列、CleanTombstones

二、promQl

promQl就是上述使用的表达式查询的语句,和我们使用的SQL不一样,他使用定义好的时间序列选择器来读取瞬时或一定时间段内的数据。同样对查询结果支持基本的二元运算,集合运算和聚合函数。

1、语句格式

sum(1-<series_selector1><duration>+<series_selector2><duration>)
<series_selector>占位符指的是Prometheus时间序列选择器,如http_requests_total或http_requests_total{method =〜"(GET|POST)"}。
占位符指的是[0-9]+[smhdwy]形式的Prometheus持续时间字符串。 例如,5m指的是5分钟的持续时间。
占位符引用布尔值(字符串true和false)。
{匹配器}label = ~"values"。

2、时间序列选择器

时间序列选择器,可以理解为一张单一功能的表,列只有时间和值。可以通过查询节点的标签查看支持些什么选择器,不同的版本支持的选择器略有不同。

http://192.168.2.29:9100/metrics

3、表达式查询结果类型

瞬时查询

http://ip:prot/api/v1/query?query=node_disk_io_now
http://ip:prot/api/v1/query?query=node_disk_io_now[7d]

范围查询
http://ip:prot/api/v1/query_range?query=sum(node_memory_MemFree_bytes)&start=1596211200&end=1598716800&step=24h

Scalars标量
还没用到,大概是做这种图用的

字符串
还没用到

4、算数运算

算数运算,包括二元运算+-*/
集合运算包括常见的一对一,一对多,多对一
聚合函数包括:

  • sum() : 求和
  • min() : 求最小值
  • max() : 求最大值
  • avg() : 求平均值
  • stddev() : 标准差
  • stdvar() : 方差
  • count() : 计数
  • count_values() : 对value进行计数
  • bottomk() : 对样本值排序,取后n条时序
  • topk() : 对样本值排序,取前n条时序
  • quantile() : 分布统计
    具体参考官方文档或者附件。

三、程序实现思路

目标:获取prometheus监控节点的CPU、内存等消息近一个月的使用率,存入execl,并定时执行发送。
显示上并不复杂,难点在于花功夫搞定promQL查询。我对promQL也不太熟悉,总之实现下来觉得prometheus这种监控用于服务器的监控并不是太理想,一是不便捷,二是感觉数据的准确度不高还,但对于K8来说有天然的优势。建议读服务器的监控数据还是用咱必须的好。

  1. 获取主机IP,区分待机节点
def getTargetsStatus(serverip):
    '''
    获取主机IP,去掉宕机的节点
    :param address:http api地址
    :return: 存活主机列表,宕机主机列表
    '''

    url = 'http://' + serverip + ':9090' + '/api/v1/targets'
    logger.info("获取节点IP地址:%s" % url)
    response = requests.request('GET', url)
    aliveNum, totalNum = 0, 0
    #存活主机列表
    uplist = []
    #宕机主机列表
    downList = []
    if response.status_code == 200:
        targets = response.json()['data']['activeTargets']
        # print(type(targets))
        # print(targets)
        for target in targets:
            totalNum += 1
            if target['health'] == 'up':
                aliveNum += 1
                uplist.append(target['discoveredLabels']['__address__'].split(':')[0])
            else:
                downList.append(target['labels']['instance'].split(':')[0])

    #去掉localhost
    #todo 需要将去掉localhost改成IP
    # print(uplist.index('localhost:9090'))
    if uplist.index('localhost') != None:
        uplist.remove('localhost')

    return uplist,downList
  1. 获取cpu
def getCpuavage(serverip,nodeip,interval):
    '''
    cpu的使用率是空闲和使用时间的比例,并不是利用率
    获取cpu30天内的使用率 1-(avg(irate(node_cpu_seconds_total{instance="192.168.2.29:9100",mode="idle"}[30d]))) * 100
    http://10.61.150.104:9090/api/v1/query?query=\
    (1 - (avg(irate(node_cpu_seconds_total{instance="192.168.2.29:9100",mode="idle"}[30d])))) * 100
    :param serverip apiserver地址
    :param nodeip   节点地址
    :param interval 瞬时向量间隔30
    :return value of cpu使用率
    '''

    heard = 'http://'+serverip+':9090'+'/api/v1/query?query='
    expr = '(1-(avg(irate(node_cpu_seconds_total{instance="'+ nodeip+ ':9100",mode="idle"}[' + str(interval) +'d])))) * 100'
    url = heard+expr
    logger.info("执行查询CPU使用率:%s" % url)

    response = requests.request('GET', url)
    if response.status_code == 200:
        # todo 有没有别的方法
        if len(response.json()['data']['result']) != 0:
            value = response.json()['data']['result'][0]['value'][1]
        else:
            value = 0
            logger.error("获取结果为空")
    else:
        value = 0
        logger.error("请求失败")

    return value
  1. 获取内存
    类似第二步骤:有一点要注意内存的使用率prometheus在centos7中可以使用node_memory_MemAvailable_bytes在centos6中需要使用(1-((node_memory_Buffers_bytes{job=".*"}+node_memory_Cached_bytes{job="."}+node_memory_MemFree_bytes{job=~"."})/ (node_memory_MemTotal_bytes{job=~"."}))) 100
    在用接口请求的时候,报错“parse error: unexpected identifier "node_memory_Cached_bytes”
    分别请求的时候可以获取到值,还没弄清楚原因#todo。所以只好选择分开计算然后用python做二元运算。

  2. 获取磁盘
    max((node_filesystem_size_bytes{job=~"$job",fstype=~"ext.?|xfs"}-node_filesystem_free_bytes{job=~"$job",fstype=~"ext.?|xfs"}) *100/(node_filesystem_avail_bytes {job=~"$job",fstype=~"ext.?|xfs"}+(node_filesystem_size_bytes{job=~"$job",fstype=~"ext.?|xfs"}-node_filesystem_free_bytes{job=~"$job",fstype=~"ext.?|xfs"})))by(instance)
    磁盘有个比较麻烦的地方无法获取全部的磁盘容量。比如/dev/sda /dev/sdb 总有一块没有获取到数据。也有待研究。#todo

  3. 获取GPU、负载
    avg(irate(dcgm_gpu_utilization{instance="192.168.2.29:9100"}[1m]))
    max_over_time(node_load5{instance="192.168.2.29:9100"}[30d])

  4. 根据IP查询cpu、内存、磁盘、GPU的30天的平均值,并返回一个execl行数据的列表

def getnodeinfo(serverip,nodeip,interval,devploment):
    '''

    :return: ["部门", "IP", "CPU使用率", "RAM使用率", "Disk使用率","Gpu使用率","负载"]
    '''
    nodeinfolist = []
    logger.info("执行节点信息查询 %s" % nodeip)
    # 获取CPU使用率
    cpuinfo = format_data(getCpuavage(serverip, nodeip, interval))
    # 获取内存使用率
    raminfo = format_data(getRamavage(serverip, nodeip, interval))
    # 获取磁盘根目录使用率
    diskinfo = format_data(getDiskavage(serverip, nodeip))
    # 获取GPU使用率
    gpuinfo = format_data(getGpuavage(serverip, nodeip, interval))
    # 获取负载信息
    uptimeinfo = format_data(getUptime(serverip, nodeip, interval))


    for i in [devploment, nodeip, cpuinfo, raminfo,diskinfo, gpuinfo, uptimeinfo]:
        nodeinfolist.append(i)

    return nodeinfolist
  1. 新建execl,按execl模块要求重构数据成二位数组[[]],并写入execl
def write_excel_xls(path, sheet_name, value):
    index = len(value)  # 获取需要写入数据的行数
    workbook = xlwt.Workbook()  # 新建一个工作簿
    sheet = workbook.add_sheet(sheet_name)  # 在工作簿中新建一个表格
    for i in range(0, index):
        for j in range(0, len(value[i])):
            sheet.write(i, j, value[i][j])  # 像表格中写入数据(对应的行和列)
    workbook.save(path)  # 保存工作簿
    logger.info("xls格式表格写入数据成功!")
#追加
def write_excel_xls_append(path, value):
    index = len(value)  # 获取需要写入数据的行数
    workbook = xlrd.open_workbook(path)  # 打开工作簿
    sheets = workbook.sheet_names()  # 获取工作簿中的所有表格
    worksheet = workbook.sheet_by_name(sheets[0])  # 获取工作簿中所有表格中的的第一个表格
    rows_old = worksheet.nrows  # 获取表格中已存在的数据的行数
    new_workbook = copy(workbook)  # 将xlrd对象拷贝转化为xlwt对象
    new_worksheet = new_workbook.get_sheet(0)  # 获取转化后工作簿中的第一个表格
    for i in range(0, index):
        for j in range(0, len(value[i])):
            new_worksheet.write(i+rows_old, j, value[i][j])  # 追加写入数据,注意是从i+rows_old行开始写入
    new_workbook.save(path)  # 保存工作簿
    logger.info("xls格式表格【追加】写入数据成功!")
  1. 定时发送,可以使用python的定时任务模块,也可以打包成py可执行程序或docker,使用系统cron实现
  2. 制作图表补充到第7步

五、完整代码

git clone https://github.com/zhaobw520/prometheusApi.git
修改发送邮件部分就可以使用了

六、参考文档

API
promeql语法
函数表达式
https://www.jianshu.com/p/07d20f3144ba

posted @ 2020-08-29 15:38  名字很长容易被惦记  阅读(5008)  评论(3编辑  收藏  举报