读取prometheus的api生成报表输出到xls
一、prometheus的http_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)"}。
{匹配器}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来说有天然的优势。建议读服务器的监控数据还是用咱必须的好。
- 获取主机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
- 获取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
-
获取内存
类似第二步骤:有一点要注意内存的使用率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做二元运算。 -
获取磁盘
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 -
获取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])
-
根据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
- 新建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格式表格【追加】写入数据成功!")
- 定时发送,可以使用python的定时任务模块,也可以打包成py可执行程序或docker,使用系统cron实现
- 制作图表补充到第7步
五、完整代码
git clone https://github.com/zhaobw520/prometheusApi.git
修改发送邮件部分就可以使用了