OpenStack云平台监控数据采集及处理的实践与优化

描述

陈云炜 李伟波 陈蔡涛

睿哲科技股份有限公司

在OpenStack云平台中,对资源的监控与计量是确保云平台稳定运行的标准配置,也恰恰是日常最让人烦恼的问题。尤其在公有云平台中,对资源的监控与计量不仅可以向业务使用者展现业务对资源的使用情况,还可以成为按需计费模式下的计费依据,但是监控数据的准确性、实时性以及海量监控数据的处理、储存和索引性能等都具有挑战性的工作。说到运维监控,大家熟知的有Zabbix、Cacti、Nagios等传统的开源运维监控系统;这些监控系统都很强大也很灵活,从普通的业务使用上来说,经过相关的配置、插件定制甚至是二次开发,完全可以完成对系统资源的监控与计量功能。但这些监控系统毕竟是独立的监控系统,并没有与OpenStack云平台进行契合,在OpenStack云平台中增删业务资源时,这些监控系统是无感知的,也就是不能自动地对OpenStack云平台的资源进行自动监控。并且OpenStack是一个多租户的云平台,以上这些开源的监控系统中要做到不同租户的资源数据分别监控与计量以及索引,还是比较复杂的。所幸OpenStack社区有Ceilometer项目来实现OpenStack集群资源监控与计量的功能。Ceilometer项目从OpenStack Folsom版本开始发布,经过不断的迭代,功能也逐渐丰富,包含了监控、计量与告警等功能,并且通过OpenStack的endpoint RESTful API以及消息队列,可以非常好地与OpenStack中的其他项目相结合,实现分租户的自动化的资源监控和计量。然而由于Ceilometer在运行性能上的一些原因,社区逐渐地Ceilometer项目进行了功能的拆分:Ceilometer主要实现资源数据的采集,将计量和数据存储功能分拆成为Gnocchi项目,将告警功能拆分成为Aodh项目。本文仅涉及Ceilometer数据采集和Gnocchi数据处理和存储两个部分,并且基于OpenStack Ocata版及之后版本的Ceilometer和Gnocchi进行分析。

二、Ceilometer与Gnocchi架构

1.Ceilometer架构和基本概念

在OpenStack中Ceilometer主要负责数据采集,其采用类似于agent加server的结构,大致的架构如下图1所示:

图 1

其数据采集是由agent端来完成的,在Ceilometer中有compute、central、ipmi等类型的agent,一般常用的是compute和central两种类型的agent:

Compute agent:负责收集OpenStack部署中各个计算节点上VM实例的资源使用数据。Compute agent须在每个计算节点上安装,并且需要与虚拟机管理程序(Hypervisor)进行密切的交互以获得VM虚拟机实例的相关资源数据。不同类型的Hypervisor提供了不同的API,因此Compute agent所能收集的数据受限于Hypervisor API所能提供的数据类型。

Central agent:负责轮询公共RESTful API,以收集未通过消息队列发布消息的OpenStack组件的相关资源情况,还可以通过SNMP收集硬件相关的资源。例如:OpenStack Networking、OpenStack Object Storage、OpenStack Block Storage等资源的使用情况均由Central agent轮询RESTful API来进行收集。

IPMI agent:负责收集OpenStack部署的各个计算节点上的IPMI传感器数据和Intel节点管理器数据。此agent需要节点安装了ipmitol程序以支持IPMI数据收集。

Ceilometer agent采集到的数据需要发送到server端进行汇总、分析和储存,在ceilometer中数据的发送方式是由publisher来定义和处理的,收集到的每种类型的数据都可以使用一个或多个publisher发送,在OpenStack的Ocata版本中,Ceilometer agent采集到的数据依然默认是采用notifier://类型的publisher发送到消息队列中。Ceilometer agent采集到的原始数据称为Meter,Meter是资源使用的某个计量项,它的属性包括:名称(name)、单位 (unit)、类型(cumulative:累计值,delta:变化值、gauge:离散或者波动值)以及对应的资源属性等;某些数据在采集到时可能还不符合相关格式,因此可以在发送数据前进行一些转换,这个转换称为Transformer,一条Meter数据可以经过多个Transformer处理后再由publisher发送,流程简图如下图2所示:

图 2

在消息队列中由Ceilometer collector充当一个server的角色来消费消息队列中的收集到的监控数据消息。Ceilometer collector可将采集到的数据进一步加工和处理,然后将数据通过HTTP方式发送到Gnocchi组件中进行处理和储存,并且后续的数据索引读取也是由Gnocchi进行处理。但Ceilometer也保留了旧版本的一些功能,可以选择旧版本Ceilometer的方式将数据直接存入一些数据库中。

2.Gnocchi架构和基本概念

Gnocchi 提供数据存储服务,是一个时间序列数据库,为Ceilometer提供存储后端,致力于解决Ceilometer应用中所面临的大规模存储和索引时间序列数据的性能问题。Gnocchi不仅解决了大规模时间序列数据存取的性能问题,同时还把OpenStack云平台中多租户等特性考虑在内。引用Gnocchi官方的一张图,其架构如下图3所示:

图 3

由以上架构图可以看出Gnocchi主要有两个核心部件:API和Metricd,并且依赖于三个外部组件:Measure Storage、Aggregate Storage、Index。

Measure Storage:measures是Gnocchi对数据的第三层划分,是实际的监控数据。因此Measure Storage用于保存实际监控数据,并且是临时保存的,在Gnocchi处理后会删除其中已处理的数据。

Aggregate Storage:首先要理解Aggregate是什么。Gnocchi采用了一种独特的时间序列存储方法:它不是存储原始数据点,而是在存储它们之前对它们按照预定义的策略进行聚合计算,仅保存处理后的数据。所以Gnocchi在读取这些数据时会非常快,因为它只需要读取预先聚合计算好的结果。因此Aggregate Storage是用于保存用户时间看到的聚合计算后的结果数据。

Index:通常是一个关系型数据库,用于索引resources和metrics,以使得可以快速地从Measure Storage或Aggregate Storage中取出所需要的数据。

API:即gnocchi-api服务进程,通过Indexer和Storage的driver,提供查询和操作ArchivePolicy,Resource,Metric,Measure的接口,并将新到来的Measure存入Measure Storage。

Metricd:即gnocchi-metricd服务进程,根据Metric定义的ArchivePolicy规则,周期性地从Measure Storage中汇总聚合计算measures,以及对Aggregate Storage中的数据执行数据聚合计算和清理过期数据等动作,并将聚合的结果数据保存到Aggregate Storage。

结合图2和图3来看,在Ceilometer collector中收集到的数据通过Gnocchi这个publisher发到到gnocchi-api,再由gnocchi-api将收集到的数据写入到Measure Storage。Metricd会周期性地检索Measure Storage是否有数据需要处理,并将Measure Storage中的数据依次取出进行聚合计算,在将计算结果保存到Aggregate Storage后也将Measure Storage中已处理的measures原始数据删除。

在Gnocchi中,将API和Metricd均设计成无状态的服务,因此可以很方便地进行横向扩展。并且对于运行的gnocchi-metriccd守护程序或gnocchi-API端点的数量没有限制,可以根据系统的负载大小调整相关服务进程的数量即可提升系统的处理能力。

三、Ceilometer与Gnocchi的实践与优化

上文简述了Ceilometer和Gnocchi的基本架构和一些基本概念,下文将讲述这两个组件在实际系统中的一些应用,以及遇到的一些问题和解决方法。

1.Ceilometer的实践与优化

Ceilometer的部署按照官方文档进行安装和配置,一般在控制节点运行ceilometer-central、ceilometer-collector和ceilometer-notification服务、在计算节点运行ceilometer-compute服务。然而官方默认的配置并不能完全符合我们的业务需求,需要进一步优化配置。

首先,Ceilometer agent所需要收集的数据是由polling.yaml配置文件来定义的,配置文件路径为:/etc/ceilometer/polling.yaml,而默认的配置是执行所有定义在ceilometer Python包entry_points.txt中的收集器来收集发送所有数据:

图 4

然而这个“全量”的配置可能会导致ceilometer agent代码层面的错误,使得收集数据的流程中断。尤其是对于Ceilometer的Compute agent,上文讲到,Ceilometer的Compute agent所能收集的数据受限于Hypervisor API所能提供的数据类型,而ceilometer entry_points.txt中定义了所有不同平台的收集器,那么肯定会有一些收集器不适用当前平台环境的,从而导致在执行这些收集器时程序出错:

图 5

从数据层面来看,我们应该只收集那些业务系统关心的数据;因为收集过多的无用数据时会给传输、处理和储存都带来额外的性能开销,尤其是在使用消息队列传输监控数据时,消息队列中大量的消息堆积将会导致消息队列服务占用大量的内存。因此,我们需要优化配置,定制化地执行收集器收集我们所需的数据。例如,在计算节点,我们如果仅需收集实例虚拟机的CPU、内存、磁盘还有虚拟网卡的资源使用情况,并根据各种资源的所需求的实时性定制其发送数据的频率,简要配置示例如下图6所示:

图 6

其中的interval是发送数据间隔,单位是秒。而meters则是需要收集的数据类型,其参数值需要根据当前Ceilometer所能收集的数据类型来设定,当前Ceilometer所支持采集的数据类型在/etc/ceilometer/gnocchi_resources.yaml文件中的metrics域所定义,其值同时会对应到Gnocchi中的资源类型,然后才可以在后续的Gnocchi中检索和处理。

其次的优化是我们需要增加Ceilometer collector的进程数。在上文中提及,在OpenStack Ocata版本中,ceilometer agent收集到的数据依然是先通过消息队列发送给ceilometer collector处理然后再发送到Gnocchi。而在Ceilometer中collector默认的进程数(即collector workers数量)是1,当集群的虚拟机数量越来越多时以及采集的数据量越来越大时,因Ceilometer collector处理消息速度过慢就会出现消息堆积的情况,由于collector响应不及时还可能导致大量的Unacked消息的出现,如下图7:

图 7

出现此类情况后可通过修改/etc/ceilometer/ceilometer.conf的配置,增加collector的workers进程数即可解决。Collector的workers进程数可根据集群的规模以及收集的数据量以及数据上报频率来设定,建议在满足消息队列中的消息不会持续堆积的情况下再增加1~2个workers进程,以满足未来一段时间内集群虚拟机不断增加所带来的监控数据增长。

而在OpenStack Ocata版本之后的Ceilometer中,还可以通过修改所有Ceilometer agent中的/etc/ceilometer/pipeline.yaml配置文件,将其中的publishers从notifier://改为gnocchi://,然后ceilometer agent收集到的数据即可直接发送到Gnocchi的API中,不再需要Ceilometer collector作中转,避免了通过消息队列发送给Ceilometer collector处理再转发到Gnocchi带来的额外性能消耗:

图 8

2.Gnocchi的实践与优化

Gnocchi是一个致力于解决Ceilometer应用中所面临的大规模存储和索引时间序列数据的性能问题的组件,因此在Gnocchi中涉及到比较多关于性能方面的参数。

首先是Gnocchi API,其API不仅承担了接收上报的原始监控数据并储存到Measure Storage的任务,还承担着业务系统通过API从Aggregate Storage索引和取出所需数据的任务。在生产环境中,Gnocchi API一般以WSGI或uWSGI应用的形式来运行,可通过Apache的mod_wsgi来运行gnocchi-api,官方的默认配置如下图9:

图 9

可根据集群规模的大小调整单个API实例中的进程数和线程数来提高API的并发量;并且Gnocchi API是无状态的,因此在集群规模较大时,可通过部署多个gnocchi-api实例,然后通过负载均衡分发请求到每个gnocchi-api,以此提升gnocchi-api的并发量。

然后是Gnocchi的Metricd,即gnocchi-metricd服务进程,是Gnocchi中最核心的部分。gnocchi-metricd不仅负责了周期性地到Measure Storage中取出并计算聚合新的监控数据,还负责了按照预定策略,周期性地到Aggregate Storage中重新计算聚合旧的监控数据,并且删除已过期的监控数据。因此gnocchi-metricd服务属于计算与I/O都是密集型的进程,需要配置好恰当的gnocchi-metricd workers进程数。如果gnocchi-metricd进程数过少,则会导致Measure Storage有大量的meansures积压,并且也会导致Aggregate Storage中有待重新计算聚合的meansures出现积压,可在OpenStack控制节点中执行gnocchi status命令查看gnocchi-metricd当前的数据处理状态,如图10:

图 10

一般来说,gnocchi-metricd的workers进程数应该在满足meansures不会持续增加的情况下再增加2个以上的workers进程,以满足未来一段时间内集群虚拟机不断增加所带来的监控数据增长。gnocchi-metricd服务也是无状态的,因此在集群规模较大时,可通过在多个机器上部署gnocchi-metricd实例,然后协同处理集群的meansures监控数据。

然后是Gnocchi中依赖的一个Index数据库和两个存储数据的Storage。Index可选MySQL或PostgreSQL等关系型数据库,并且其保存的数据是规整的关系数据,仅用于查询索引,数据量不会很大,因此一般不会出现性能瓶颈。而Measure Storage和Aggregate Storage承载了Gnocchi的大部分的I/O操作,在海量监控数据前,其性能至关重要。Gnocchi官方支持的Measure Storage和Aggregate Storage类型有普通的本地文件File、Ceph、Swift、Amazon S3等,在Gnocchi 4.0版本中还增加了Redis。官方的配置中推荐使用Ceph作为Measure Storage和Aggregate Storage,而在实践的过程中发现,如果Gnocchi 4.0以下的版本直接使用性能一般的Ceph集群来当作Measure Storage和Aggregate Storage时,在运行一段时间后就可能会出现一些奇怪的性能问题。如图11,Ceph集群会频繁地出现OSD ops blocked的警告,甚至出现OSD 自动down的问题:

图 11

进而查看OSD的日志发现OSD之间有大量的heartbeat check no reply以heartbeat_map出现had timed out的情况:

图 12

图 13

在此情况下Ceph集群几乎在一种不可用状态,大量的读写请求被Blocked,运行在Ceph集群中的虚拟机和Cinder Volume也大量失去了响应,而此时gnocchi-api和gnocchi-metricd日志中也出现大量的rados读写Ceph集群超时的错误:

图 14

在此类情况下需要把gnocchi-api和gnocchi-metricd进程退出,并把Ceph中存储gnocchi数据的pool删除,待数据重新平衡才可恢复。

通过调试和摸索,发现其原因是Ceph对海量小文件的存储支持还比较差,尤其是在多副本的情况下储存Gnocchi的监控数据,其总数据量是成倍的增加,究其原因这还得从Ceph的存储原理来进行分析。首先需要了解在Ceph中是以Object来储存文件的,Object的大小由RADOS限定(通常为2MB或者4MB),当文件超过这个容量大小之后就会按照每个Object的容量对文件进行拆分,每个对象都会有一个唯一的OID用于寻址,由ino与ono生成,ino即是文件的File ID,用于在全局唯一标示每一个文件,而ono则是分片的编号。Ceph分割文件为对象后并不会直接将对象存储进OSD中,因为对象的size很小,在一个大规模的集群中可能有数以亿计个对象,这么多对象光是遍历寻址,速度就已经很缓慢了。因此Ceph中引入PG(Placement Group)的概念,这是一个逻辑上的组,Object通过crush算法映射到一组OSD上,每个PG里包含完整的副本关系,比如3个数据副本分布到一个PG里的3个不同的OSD上。下面引用Ceph论文中的一张图(图15)可以比较直观地理解,将文件按照指定的对象大小分割成多个对象,每个对象根据hash映射到某个PG,然后再根据crush算法映射到后端的某几个OSD上:

图 15

不同的对象有可能落到同一个PG里,在Ceph实现里,当PG存在op(operations)时,在OSD的处理线程中就会给PG加锁,一直到queue_transactions里把事务放到journal的队列里(以filestore为例)才释放PG的锁。从这里可以看出,对于同一个PG里的不同对象,是通过PG锁来进行并发的控制,好在这个过程中没有涉及到对象的I/O,效率还是很高的;对于不同PG的对象,可以直接进行并发访问。

在Ceph中,每一个pool的PG数都是在创建pool的时候根据集群规模大小计算得出的合理值来设置的,也就是说每一个pool里的PG数是有限的。在Gnocchi的监控数据中,每条数据的内容都很小,并且每条监控数据就是一个Object,当海量小文件存到一个PG数量较少的Ceph pool中时,就会出现单个PG中包含太多的Object的情况,虽然PG锁的效率很高,但是在大量小文件读写时依然有可能出现处理不过来的情况,从而就会出现op blocked。

另一方面,OSD端处理op时是将op划分为多个shard,然后每个shard里可以配置多个线程,PG按照取模的方式映射到不同的shard里进行处理。一般来说,系统给每个OSD配置的处理线程都是比较少的,如果Gnocchi pool的PG在OSD端一直占用大量处理线程,那么其他Ceph pool的 PG的op就会处于等待处理状态,这时也会出现op blocked的情况,而OSD线程占用严重时甚至可能导致OSD进程异常退出。在OSD端,除了将数据写入磁盘(Filestore)外还需要写入文件的Extended Attributes (XATTRs)到文件系统或omap(Object Map)中,面对海量的小文件读写,OSD的压力I/O压力会明显增重。

因此在Gnocchi中的数据量达到一定程度时,就可能会对Ceph存储集群产生不利的影响。在出现这种情况下,以下是一些解决的方案:

方案一:在Gnocchi 4.0以下的版本中,使用Swift或本地文件File作为Measure Storage和Aggregate Storage。亦或者使用Swift或本地文件File作为Measure Storage,而Aggregate Storage继续使用Ceph;因为Measure Storage中保存的是原始的监控数据,数据的文件条目量大并且都是小文件,而Aggregate Storage中保存的是聚合计算后的结果数据,数据量相对较小。但如果Ceph集群的性能不是很好,尤其是Ceph中的OSD数量较少时,并且监控的数据量相对较大,以及需要保存较长一段时间时,不建议使用Ceph。

方案二:升级OpenStack中的Gnocchi版本到4.0及以上,继续使用Ceph作为Measure Storage和Aggregate Storage。Gnocchi在4.0及之后的版本代码中做了优化,当使用Ceph作为Measure Storage时,Measure Storage中保存的measures是保存在Ceph OSD的omap中而不是OSD的Object文件,omap中的数据是保存在Leveldb、Rocksdb等数据库中,所以measures的数据并不会写到磁盘中,以此缓解OSD的I/O压力。Aggregate Storage中的数据依然是保存为Ceph的Object文件。但是在小型Ceph集群中,如果监控数据量比较大时,依然会对Ceph集群产生一定的性能影响。

方案三:升级OpenStack中的Gnocchi版本到4.0及以上,使用Redis作为Measure Storage,使用Swift或Ceph作为Aggregate Storage。此方案性能最优,因为Measure Storage中保存的measures文件量大但容量小,measures需要经过gnocchi-api写入Measure Storage,gnocchi-metricd读出处理,然后gnocchi-metricd从Measure Storage删除等步骤。使用Redis这个内存型的数据库不仅可以解决Measure Storage海量小文件读写频繁的需求,并且内存高速I/O带宽的优势使得gnocchi-api和gnocchi-metricd在I/O处理上性能更高,系统并发性能更好。

四、结语

以上对Ceilometer监控数据采集和Gnocchi数据处理的架构和流程作了简要的分析,并且分析了笔者在将Gnocchi和Ceph存储结合使用时出现的一些问题,这些问题在出现在生产环境中是非常致命的,不仅导致大规模的业务不可用,甚至还可能导致数据丢失的风险。因此在搭建OpenStack集群时,预先按照集群规模、采集的数据量等等规划好监控数据的保存方案,对生产环境上线后的稳定性意义深远。

审核编辑:符乾江

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分