简介
服务的功能
获取openstack集群中cinder,nova,neutron, swift,swift-account-usage中的关于虚机,云盘,网络,存储相关的数据整理发送给prometheus server
代码地址
github链接: https://github.com/CanonicalLtd/prometheus-openstack-exporter.git
分支: master(和tag 0.1.4的版本一致)
它是怎么工作的
- prometheus server端周期性来exporter端抓取数据 (这里的周期是在prometheus server中用scrape_interval定义的)
- 我们的exporter服务启动后,会开启port的监听,一旦监听到有请求,会调用exporter里的一些列函数,将整理好的metrics返回给prometheus server,数据采集成功
源码
在看源码之前,或者之中需要知道的prometheus_client的一些概念
关于prometheus_client需要知道的这些
registry:就是一个监控项仓库,可以有很多个collector
collector:就是一个自定义的监控项收集器,一个collector中可以有很多个metrics
metrics: 监控项
metrics类型:count,gauge,summary,histogram
- count类型:计数器 特点:只增不减,可以是浮点型,不一定非要是整数
- gauge类型:瞬时值 特点:随意增减,浮点整数
- summary类型:存储了 quantile数据,针对于长尾效应设计的,可画出准确的正态分布图
- histogram类型: 收集正态分布数据,通过划分bucket,但是只是计数,比较粗略,客户端性能开销跟count和gauge差不多,图的准确性不如summary高,可画出粗略一点的正态分布图
其中 gauge、histogram、summary这三种metrics都没在这个exporter中出现,为了metrics类型完整(一家人就是要整整齐齐),所以把这三个也贴出来了;
写一个简单的collector举例:
1 | from prometheus_client import Counter, Gauge, Histogram, Summary, \ |
获取到的metric如下,我省略掉了一些prometheus_client自带的变量
1 | # HELP summary_metric_name A summary description test |
代码层逻辑实现
好了,现在开始干正事,代码的逻辑是下边这样的:
- 开启一个线程,周期性(这里的周期是配置文件中的cache_refresh_interval定义的)的在线程内
- 通过环境变量获取到openstack的client()
- 使用openstack的组件client()获取我们想要的数据,并将数据缓存至本地的二进制文件中
- 开启一个事件监听循环:
- 当有请求 /metrics的时候,在/metrics路由下 将各个组件的collector监控数据组合到一起,返回给客户端
- 各个组件(nova,cinder,neutron,swift)的collector类从上边已经缓存的缓存文件中纷纷读取自己的数据,做处理,放到自己的registry里,再返回到metrics路由
从程序入口开始
1 | if __name__ == '__main__': |
1.先加载到配置文件,2.初始化记录log,3.开启数据收集(data_gatherer.start()),4.启动事件监听(server.serve_forever())
接下来先从数据收集开始
开启线程收集数据 | DataGatherer(Thread)
单独开启一个线程,周期性通过client端获取我们所想要的数据,重写Thread的run()方法来完成我们想要在线程中进行的操作
1 | class DataGatherer(Thread): |
Q: 为什么要加缓存呢,实时获取数据不好吗?
A:实时获取数据对于监控来说当然更精确,但是因为我们的查询比较繁琐,在集群规模比较大的情况下,查询一次可能需要几分钟,频繁的调API去抓取或者多个exporter抓取集群数据,也可能会导致集群响应延迟更高,还会给集群造成负面影响。
HTTPServer的框架 | ForkingHTTPServer(..)
单独把起HTTPServer的框架摘(二声)出来, 起一个异步进程HTTPserver并监听, 相关的代码是这样的
1 | class OpenstackExporterHandler(BaseHTTPRequestHandler): |
自己创建的ForkingHTTPServer:
ForkingHTTPServer:这里的ForkingHTTPServer继承自HTTPServer和ForkingMixIn,主要作用,异步起个进程,监听socket;
引用的模块中的两个类,这俩类的功能:
HTTPServer:继承自SocketServer.TCPServer, HTTPServer主要实现启动事件监听和address port的绑定,并将请求交于handler处理
ForkingMIxIn是继承于SocketServer用来做异步处理的类,它重载了process_request()方法,当出现新请求时创建一个新的进程,把具体工作放到新的进程中执行.
再来看一下我们的处理请求的Handler类
OpenstackExporterHandler:继承自BaseHTTPRequestHandler
OpenstackExporterHandler中定义do_GET来处理请求
BaseHTTPRequestHandler 是http handler的基类,无法直接使用,需要定义请求处理函数,所以我们重写do_GET()函数处理请求
关于server和handler的关系
python http框架主要有server和handler组成,server主要是用于建立网络模型,例如利用epoll监听socket;handler用于处理各个就绪的socket,就是server是用来启动事件监听,handler是来处理事件的。
总结一下就是当执行到下面这段代码时:
1 | server = ForkingHTTPServer(('', config.get('listen_port')), handler) |
开启一个事件循环监听,(如果address是本地的话,可以在本地看到绑定的address和port),可以在本地看到并将开启一个新进程来处理接受到的请求;
获取client | get_clients()
接着看在线程中是怎么获取client()的
从当前环境变量中获取需要的认证信息,然后获取各个openstack组件的client()
1 | def get_clients(): |
一个openrc示例:
1 | export OS_USERNAME=admin |
Handler返回数据 |OpenstackExporterHandler(..)
在OpenstackExporterHandler中
1 | # 在这把collector的和具体实现collector的类的映射关系放到了字典里 |
OpenstackExporterHandler集成自BaseHTTPRequestHandler,通过重写do_GET()函数,响应请求
self.wfile也是BaseHTTPRequestHandler里方法,用来写入应答信息
self.send_header(..), self.send_response(..) 这些都是BaseHTTPRequestHandler中的方法
Collector怎么收集数据的 | Nova()
1 | class Nova(): |
通过get_stats()返回registry
再来看一下DataGatherer的get_stats()
1 | class DataGatherer(Thread): |
metrics列表
待补充
grafana-dashboard
对应的dashboard都写好了:
https://grafana.com/grafana/dashboards/7924/reviews
无奈我没有swift,所以不show图了;
监控swift那块好像还是有点精彩的,我没有swift环境,
参考链接:
关于HTTPServer的:http://luodw.cc/2016/11/05/python-http/
metrics参考: https://segmentfault.com/a/1190000018372390
metrics参考2: https://www.infoq.cn/article/Prometheus-theory-source-code
源码解析:https://blog.csdn.net/qingyuanluofeng/article/details/84779737