数据可视化Pyecharts的实际使用方式
作者:Meepoljd
一个多月前参加公司的一个产品会的时候,有和同事聊到日常巡检报表的一些东西,现在虽然项目上搭建的有监控平台、数据稽核平台、调度平台等业务系统,每天运维人员也做定期的巡检,但是细想来说这其中有2成左右的工作可能是一个重复性的工作,做完以后要生成巡检报告,有时候可能还要发给客户。这部分工作属于是做了没有成绩,不做可能出事的活。
本身这样的事情就应该尽量自动化,而且我们也用Grafana对数据做了图表展示,但是领导就是喜欢看报告,觉得大屏这种东西是线上看的,报告是留档看的,而且让我们天天出报告也能让我们累一点(懂的都懂)。
再说自动化报表这个事情,最好是能够有一个连接全局数据源的引擎来自动生成,对数据有一定分析能力,不过公司没这个开发能力,而且开源项目也调研过,对接自研的各个平台也需要开发投入。
综合来看,先临时做一个程序解决眼下问题会好点。
环境分析
做之前,先分析下实际环境上有的各种问题,能想到的大概有这几个:
- 数据来源不唯一,比如对集群资源进行监控的数据可能存在Prometheus,其他的可以通过各种后端接口获取
- 网络不一定通,由于网络限制,没有一台服务器能同时访问所有数据源,可能需要考虑数据获取时使用代理
- 可视化图形,通过接口获取的各种数据,需要自己再画图
最主要的几个问题就是上面的三个,各有各的解决方案,本文主要说的是可视化绘图这部分,而这里画图的话就是使用Pyecharts来做的。
Prometheus数据处理
先说下针对Prometheus的数据处理,Prometheus的查询接口常用的有query和query_range,按照这样进行封装,方便获取数据时直接调用,不用再做额外处理:
import sys import os import requests QUERY_ENDPOINT = "query" QUERY_RANGE_ENDPOINT = "query_range" class PrometheusQueryAPI: """Class Create Prometheus query api""" def __init__(self, api, proxy=None) -> None: self.api = api self.endpoint = "query" self.params = {} self.result = None self.proxy = proxy def clean(self): """清理查询参数 """ self.params = {} def query(self, query, time=None): """Create the query""" self.params["query"] = query if time: self.params["time"] = time self.endpoint = QUERY_ENDPOINT return self def query_range(self, query, start=None, end=None, step=None): """Create the Query range""" self.params["query"] = query if start: self.params["start"] = start if end: self.params["end"] = end if step: self.params["step"] = step self.endpoint = QUERY_RANGE_ENDPOINT return self def run(self): """Run the prometheus query""" url = os.path.join(self.api, self.endpoint) if sys.platform.startswith('win'): url = url.replace('\\', '/') res = requests.get(url=url, params=self.params, proxies=self.proxy, timeout=30) self.result = res.json() self.clean() return self
当执行query或者query_range的查询时,使用对应的方法构造查询语句,然后调用run进行语句运行即可,封装的比较。
Gauge图
Gauge图就是仪表盘图,这里就不说官方示例了,以我的用法来说,先通过Prometheus获取数据:
p = prometheus.PrometheusQueryAPI( PROMETHEUS, proxy=PROXY) p.query("yarn_exporter_allocateMem/(yarn_exporter_availableMem+yarn_exporter_allocateMem)") p.run() data = round(float(p.result["data"]["result"][0]["value"][1])*100, 2)
这里获取到一个瞬时向量的指标,然后做数据类型的转换和处理方便接下来绘图:
from pyecharts.charts import Gauge gauge1 = ( Gauge() .add( # 必要配置 series_name="内存使用率", data_pair=[["内存使用率", data]], # 设置仪表盘的条例 axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts( # 设置不同区间的颜色,color中划分不通区间的颜色,width为线条粗细 color=[(0.3, "#E9EB2E"), (0.9, "#37a2da"), (1, "#fd666d")], width=10 ) ), # tooltip的设置,进行显示的格式化 tooltip_opts=opts.TooltipOpts( is_show=True, formatter="{b} : {c}%"), max_=100, min_=0, # 设置相对位置,横纵坐标,当使用Grid组合Gauge时必须设置,否则图表会重合 center=["25%", "50%"], # 仪表盘内的指标名称字体设置 title_label_opts=opts.GaugeTitleOpts( font_weight="bold", font_size=20 ), # 仪表盘内的指标数值的设置 detail_label_opts=opts.GaugeDetailOpts( formatter="{value}%", color="#1267CB", font_weight="bold", font_size="20", offset_center=["0%", "40%"], ) ) .set_global_opts( # Gauge的标题设置 title_opts=opts.TitleOpts( title="内存使用率", ), # 设置图例格式 legend_opts=opts.LegendOpts(is_show=False), ) )
这里主要需要注意的是进行数据的格式化的时候,在TooltipOpts
中设置使用"{b} : {c}“这样的内置变量,如果是在GaugeDetailOpts
中进行设置,就要使用”{value}"这个内置变量。
Bar图-横向
有的时候在使用条形图的时候,需要使用横向的条形图,官方的示例比较少,可以这样设置:
bar = ( Bar(init_opts=opts.InitOpts(width="850px", height="800px")) .add_xaxis(xaxis_data=province) .add_yaxis("HDFS", values) .set_global_opts( title_opts=opts.TitleOpts(title="HDFS使用情况"), visualmap_opts=opts.VisualMapOpts( min_=0, max_=100, # 组件映射维度 dimension=0, # 是否为分段型,如果设置为False,会出现一个完整的分段条可以拖动调节 is_piecewise=True, # 设置不同取值区间的颜色 range_color=[ "#00EC00", "#00A600", "#FFD306", "#FF8000", "#FF2D2D"], orient="horizontal", pos_top="0%", pos_left="40%" ), legend_opts=opts.LegendOpts( is_show=False ), xaxis_opts=opts.AxisOpts( max_=100 ) ) .set_series_opts( # 设置标记线,如果进行了行列转置,这里的操作就要对X轴进行了而不是Y轴! markline_opts=opts.MarkLineOpts( data=[ opts.MarkLineItem( x=80, name="使用率过高", linestyle_opts=opts.LineStyleOpts( color="red", ) ) ] ), ) # 翻转XY轴 .reversal_axis() )
使用reversal_axis()
设置XY轴翻转,使用VisualMapOpts
设置视觉映射,这样可以根据自己的需要,对不同区间的数据设置颜色,使用MarkLineOpts
可以设置标记线,比如警戒线,尤其要注意的是,当进行行列转置后,警戒线的设置要对X轴的值设置了,不能对原先设置的Y轴进行设置。
效果图如下:
如何整合进Flask中
很多时候可视化并不只要图,报告还需要加入一些结论性的文字,也就是图文结合,类似这样:
在进行图表初始化的时候,可以添加InitOpts进行图表大小的设置:
Bar( opts.InitOpts(width="850px", height="800px") )
在做模板渲染的时候,这样进行设置:
return render_template("view.j2", data=bar.render_embed())
在对应的模板view.j2中只需要这样写就可以:
<div>{{ data|safe }}</div>
附录
- Echarts官网:Apache ECharts
- PyEcharts文档:简介 - pyecharts - A Python Echarts Plotting Library built with love.
- PyEcharts示例:中文简介 - Document (pyecharts.org)
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。