友情提示:全5000多文字,预计阅读时间15分钟
BC-DeepWatch(大云运维管理平台)是中国移动云能力中心研发的一款集资源配置管理、监控告警、运维操作等功能于一体的运维监控产品。对公有云、私有云、混合云等不同形态的IT基础设施进行集中监控,为云计算环境安全、可靠的运行提供保障。
一、Graphite简介
1.1 Graphite组成、架构
时序数据库(Time Series Database)主要用于存储带时间标签(按照时间的顺序变化,即时间序列化)的数据,带时间标签的数据也称为时序数据。在运维领域主要存储带时间标签的监控指标数据。
Graphite是一款基于Python语言开发的开源时序数据库,架构见图1[1],由三部分组成:
图1 Graphite架构图
1、Carbon:由以下三个组件组成:
carbon-relay:对数据进行复制和分片,主要为分布式服务;
carbon-aggregator:根据规则对数据进行聚合;
carbon-cache:Carbon核心组件,可根据不同策略接收、缓存数据,并高效地将数据持久化到硬盘。
2、Whisper:一个简单的数据库,用于存储时序数据,在设计上和RRD(Round Robin Database)类似。
3、Graphite-Web:由三个组件组成:
webapp:读取和解析whisper文件中的时序数据,渲染成图表、仪表盘,并提供API供外部系统调用;
memcached:根据策略缓存被渲染的时序数据;
database:关系型数据库,存放Graphite-Web管理数据,如用户信息。
图中metrics是发送给Graphite的监控指标数据,这些数据首先通过Carbon模块进行收集,然后存储在Whisper中,最后Graphite-Web从Whisper中获取这些数据并进行渲染展示。
Graphite不负责采集数据,数据采集由第三方软件或系统实现。Graphite主要负责:
(1)存储数值型时序数据;
(2)使这些数据按需求渲染图表、仪表盘。
Graphite存储的时序数据文本格式定义如下:
metric_path value timestamp\n
(1)metric_path:指标存储路径及名称,以“.”进行分割,如:DW.vm.12345.cpu_util
(2)value:指标值,数值型数据
(3)timestamp:Unix时间戳
1.2 Graphite接口调用
1、监控指标数据入库
使用TCP协议向Graphite部署的服务器IP:Port发送文本监控指标数据。
(1)入库数据格式:metric_path value timestamp\n
(2)入库示例:echo "DW.vm.12345.cpu_util 30 1572441910" | nc IP Port,如图2为Grafana展示入库后的数据:
图2 grafana展示入库后的数据
2、监控指标数据查询
(1)Restful接口:
http://IP:Port/render/?target={graphite_expression}&from={start_time}&until={end_time}&format=json
(2)method: GET
(3)请求参数说明:
请求头
参数名 |
参数类型 |
说明 |
Content-Type |
header |
application/json |
请求参数
参数名 |
参数类型 |
是否必须 |
说明 |
graphite_expression |
String |
是 |
性能指标graphite表达式 |
start_time |
long |
是 |
开始时间(unix时间戳) |
end_time |
long |
是 |
结束时间(unix时间戳) |
format |
String |
是 |
|
(4)返回数据说明
响应参数
参数名 |
参数类型 |
说明 |
target |
String |
查询性能数据graphite表达式 |
datapoints |
Array |
数据点,一个二维数组,二维数组第一个元素是数值,第二个元素是时间 |
(5)请求接口示例
请求URL:
http://IP:Port/render/?target=openmetric.carbon.carbon-disk-node1.runtime.NumGoroutine&from=1572444150&until=1572444260&format=json
请求结果
[ { "target": "openmetric.carbon.carbon-disk-node1.runtime.NumGoroutine", "datapoints": [ [ 55, 1572444150 ], [ 57, 1572444160 ], [ 55, 1572444170 ], [ 55, 1572444180 ], [ 55, 1572444190 ], [ 55, 1572444200 ], [ 55, 1572444210 ], [ 55, 1572444220 ], [ 55, 1572444230 ], [ 57, 1572444240 ], [ 57, 1572444250 ], [ 55, 1572444260 ] ] }]
二、Graphite设计方案选择
2.1 Graphite社区方案
在图1架构中,Graphite核心模块Carbon负责监听指标数据并将其持久化到whisper文件中。每个监控指标在硬盘里是一个whisper文件,Carbon模块负责将每个指标的监控数据写入到对应的whisper文件中,在大规模资源监控场景下,需要监控的指标数量急剧增加,Carbon维护的whisper文件数也随之增加,在一个数据处理周期内,Carbon需要对硬盘的写操作次数也急剧增加,此时Carbon对硬盘写操作的IO性能问题就逐渐暴露出来。当监控资源规模特别巨大时,后端Carbon可以多实例运行,通过carbon-relay模块对监控指标数据进行分片,然后由不同的实例进行持久化,从而提高数据持久化效率,如图3为Graphite两集群,每个集群两个节点架构图。
图3 Graphite两集群架构图
在大规模应用场景下,社区Graphite有以下几个缺点:
一个单独的Carbon程序处理能力有限,因为它是用Python语言设计的。一台服务器不支持多个实例同时运行,所以可能出现Carbon刚刚启动时丢弃监控指标数据的情况。
每个监控指标在硬盘中以一个whisper文件进行存储,Carbon负责将每个监控指标数据写入到对应的whisper文件中;然而,Carbon没有持续打开whisper文件句柄,存储每个指标数据都需打开文件、写入数据、关闭文件等IO操作,在大规模资源监控场景下,一个数据采集周期内需要对海量的监控指标数据进行持久化操作,Carbon硬盘IO性能问题逐渐凸显出来。
Graphite-Web与Carbon类似,并没有持续打开whisper文件句柄,在查询每个指标时都需要重新打开whisper文件读取数据,在打开较大维度下监控指标数据时,通常比较耗时。
因此,社区Graphite无法在大规模资源监控场景下使用。
2.2 不同优化方案
针对以上问题,社区的不同组织提出了相应的优化方案。主要的两个方案如下:
方案一:使用Cassandra数据库替换Whisper
cyanite[2]项目实现了将监控数据写入Cassandra数据库,替换了Whisper。同时,cyanite提供了从Cassandra中查询监控指标数据的API。graphite-cyanite[3]项目在cyanite提供的API基础上将接口封装成Graphite-Web可以解析的形式,从而实现Graphite-Web对监控指标数据的查询、渲染,优化后架构如图4。
图4 Cassandra替换Whisper后的Graphite架构
优点:
解决了Carbon硬盘IO问题,可以在大规模监控资源场景下使用
提升了Graphite-Web查询监控指标数据的效率
缺点:
引入了新的组件:cyanite、graphite-cyanite、Cassandra数据库,增加了部署、维护的难度。
Carbon处理能力没有提升,依旧保持着原有的低效状态。
方案二:对Carbon、Graphite-Web进行重构、设计改进
carbon-c-relay[4]项目利用C语言对carbon-relay功能进行了重写,并集成了carbon-aggregator数据聚合功能;同时,优化了数据发送机制:当一个集群不可用时,缓存发往该集群的数据,待集群可用时,再将缓存的数据发往该集群;
go-carbon[5]项目利用Go语言对carbon-cache功能进行了重写,提供了访问whisper文件中时序数据的API。go-carbon维护了打开whisper文件的句柄,优化了数据持久化和查询的性能。
Carbonapi[6]项目利用Go语言实现了Graphite-Web中图表、仪表盘的渲染生成功能。同时,实现了对多节点数据查询、归并去重,并在go-carbon提供查询数据API基础上将接口封装成Graphite API。
组件优化前后具体功能对比见表1。
表1 Graphite优化前后的组件功能对比
Graphite原生组件 |
主要功能 |
Graphite优化后的组件 |
主要功能 |
carbon-relay |
Python语言开发,数据复制、分片 |
carbon-c-relay |
C语言重写,数据复制、分片、重发,根据规则聚合数据 |
carbon-aggregator |
Python语言开发,数据聚合 |
||
carbon-cache |
Python语言开发,缓存数据,持久化到磁盘 |
go-carbon |
Go语言重写,缓存数据,持久化到磁盘,读取、解析whisper文件中时序数据 |
graphite-web |
Python语言开发,读取、解析whisper文件中时序数据并渲染成图表、仪表盘,提供外部访问的Graphite API |
carbonapi |
Go语言重写,数据查询、归并去重、图表和仪表盘的渲染生成,提供外部访问的Graphite API |
Graphite组件优化后架构如图5。
图5 组件优化后的Graphite架构
优点:
维护了所有打开whisper文件的句柄,极大提升了数据持久化、查询效率;go-carbon利用go语言协程技术提升了程序数据处理能力;同一服务器可多实例运行;
减少了组件个数,简化了架构设计;易于部署、维护。
缺点:
维护了所有打开whisper文件的句柄,占据着大量硬盘IO,因此通常独栈部署,服务器复用率低。
三、Graphite在BC-DeepWatch中的设计实现
BC-DeepWatch主要面向公有云、私有云、混合云等形态的复杂云计算环境,监控的资源千差万别,因此在实际使用中需要对图3中Graphite架构做相应的调整。
3.1 增加数据适配层
在不同的云环境中,需要监控的资源种类繁多,监控数据采集方式及格式也不尽相同:
针对部分IaaS、PaaS资源:若监控数据采集客户端支持Graphite数据格式,则可以直接上报,如Sensu、Collectd、Prometheus等;
针对不能通过数据采集客户端上报Graphite格式数据的IaaS、PaaS资源:如OpenStack、Vmware可对其管理的资源进行监控数据采集、存储,开发对应数据解析适配器,将数据转换成Graphite格式再入库;
针对SaaS资源:如Hadoop、Redis、MySQL等,SaaS资源种类繁多,每种资源的监控指标数据组织形式各不相同,同样需要开发对应数据解析适配器,将数据转换成Graphite格式再入库即可。
因此BC-DeepWatch在Graphite中引入了数据适配层,以满足非Graphite格式监控数据资源的监控需求。
3.2 两层relay设计
社区Graphite中Carbon模块有两个组件:carbon-relay、carbon-aggregator,carbon-relay负责对数据进行复制、分片,主要为分布式服务;carbon-aggregator负责对数据进行聚合。这种设计的好处是可以灵活满足大规模资源监控场景下,面对高并发时可以对进行数据分片、降维,从而保证数据持久化的效率。因此BC-DeepWatch中的Graphite沿用了这种设计理念,利用carbon-c-relay组件做了两层设计,一层relay负责数据数据进行复制、分片,另一层relay负责对数据进行聚合。
3.3 增加监控数据内存存储区
在部分复杂的运维场景中,需要对最近某个时间段内的监控数据进行频繁的查询、计算,以便为诸如报表统计等业务提供数据支撑,直接在硬盘上进行大量频繁的数据查询,其效率通常比较低下,同时也会占据不小的硬盘IO,影响接收到的数据持久化性能,因此将一些核心指标(如CPU使用率、内存使用率、网络收发流量、磁盘读写速率等)的数据存放在内存中以提高查询、计算效率,同时不影响接收到监控数据的持久化性能。
3.4 引入Grafana进行图形、仪表盘渲染
虽然carbonapi组件可以将whisper文件中的时序数据渲染成对应图表、仪表盘,但是远不如数据可视化工具Grafana[7]灵活、方便,因此在BC-DeepWatch中采用Grafana对监控数据进行图表、仪表盘渲染,并作为Graphite的Dashboard。
Graphite在BC-DeepWatch中的架构设计详见图6。
图6 Graphite在BC-DeepWatch中的架构设计
relay集群:依赖carbon-c-relay组件实现,分为两层:一级relay、二级relay。
一级relay:将需要聚合的监控数据发送到二级relay,将不需要聚合的监控数据分发到所有集群(所有disk集群、memory集群)。在分发的过程中,relay根据集群数量对监控数据进行复制,并在每个集群中随机选择一个节点进行分发。
二级relay:接收一级relay发送的监控数据,并进行聚合,然后按照一级relay方式对数据复制并分发到所有carbon集群。
carbon集群(disk):依赖go-carbon组件实现,该集群接收relay集群发送的监控数据,并将数据持久化到硬盘上。通过对relay集群match规则的配置,通常将全量性能数据发送给carbon集群(disk),因为carbon集群(disk)保存数据的时间较长。carbon集群(disk)对集群中每个节点上whisper文件中的时序数据进行解析,并提供外部访问API。
carbon集群(memory):依赖go-carbon组件实现,该集群根据规则接收relay集群发送的监控数据,并将数据保存在内存中,一般只保存几小时,时间长短可根据具体需求配置。通常只会将用于其他程序计算的监控数据存储在内存中,以提高查询、聚合效率。
carbonapi(disk) / carbonapi(memory):分别指向所有的carbon集群(disk)/carbon集群(memory)节点,提供查询carbon集群(disk)/carbon集群(memory)whisper文件中时序数据接口,并将不同节点上的数据进行归并、去重。同时可根据参数将监控指标的时序数据渲染成图表、仪表盘。
grafana:可以灵活的将监控指标数据渲染成图表、仪表盘。用作BC-DeepWatch中Graphite的Dashboard。
四、参考资料
[1] http://www.graphiteapp.org
[2] https://github.com/pyr/cyanite
[3] https://github.com/brutasse/graphite-cyanite
[4] https://github.com/grobian/carbon-c-relay
[5] https://github.com/lomik/go-carbon
[6] https://github.com/go-graphite/carbonapi
[7] https://www.grafana.com
-End:)
往期精选
1、干货分享 | E-RocketMQ大云消息队列消息轨迹设计与实现
2、干货分享 | 堵塞 VS非堵塞REST服务在Spring MVC中的性能测试对比