zoukankan      html  css  js  c++  java
  • 读取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

    --------------------------------------------------------- 恐惧是因为努力的还不够,加油 ~~---------------------------------------------
  • 相关阅读:
    c# 把对象加入队列,对象为全局变量,对象改变队列值也跟着改变
    C# 一个数组未赋值引发的错误
    c# 2016QQ自动登录程序
    当时钟事件声明为过程变量 让system.threading.timer时钟失效
    if 循环的深入理解 哈希表的一种应用
    VB6对象与地址相互转换
    VB6的函数指针传递
    .net framework 4.0 从 GAC 卸载 程序集
    .net framework 4.0 从 GAC 卸载 程序集
    GAC in CLR 3.0
  • 原文地址:https://www.cnblogs.com/zhaobowen/p/13527207.html
Copyright © 2011-2022 走看看