简要
针对于一些云计算的产品来讲,实现资源的计量/计费功能是一个比较大的工程,很多公司都是基于OpenStack开发自己的计费服务作为其产品化的一部分。本文主要是针对OpenStack的计量计费根据自己的学习认知做一个总结
文中涉及到的一些链接:
cloudkitty运用
Ceilometer的认知
一 如何实现
1.1 总述
完成资源的计费需要经过以下几个步骤:
- 步骤一:资源数据的收集工作,包含资源的使用对象(what),使用者(who),使用时间(when),使用量(how much)等基本信息,Ceilometer是专门为OpenStack环境提供一个获取和保存各种测量值的统一框架
- 步骤二:对【步骤一】的资源数据根据产品的计费策咯进行计费
- 步骤三:在【步骤一】【步骤二】已经完成资源的计费和存储的前提下,我们可以根据业务需求去合理的使用已经完成计费的数据,开发针对于业务层面的API
1.2 Ceilometer介绍
Ceilometer是专门为OpenStack环境提供一个获取和保存各种测量值的统一框架,明确目标,同时将其告警功能拆分到aodh项目,采集数据存储到gnocchi时间序列数据库,事件相关的服务拆分到panko项目,相关数据存储在MongoDB中
1.2.1 框架
- Ceilometer:数据采集服务,采集资源使用量相关数据,并推送到Gnocchi,采集操作事件数据,并推送到Panko。
- Gnocchi:时间序列数据库,保存计量数据。
- Panko:事件数据库,保存事件数据。
- Aodh:告警服务,基于计量和事件数据提供告警通知功能。
1.2.2 数据收集方式
数据收集方式从大方向上来看分为两种:主动/被动,下图为Ceilometer收集数据的方式,主要分为三大类:
- 通知【被动】:所有的OpenStack服务都会在执行了某种操作或者状态变化时发送通知消息到oslo-messaging(OpenStack整体的消息队列框架),一些消息中包含了计量所需要的数据,这部分消息会被Ceilometer的ceilometer-agent-notification服务组件处理,并转化为samples。通知数据采集方式是被动地采集计量数据。
- 轮询【主动】:Ceilometer中的服务组件根据配置定期主动地通过OpenStack服务的API或者其他辅助工具(如Hypervisors)去远端或本地的不同服务实体中获取所需要的计量数据;Ceilometer的轮询机制通过3种类型的代理实现,即ceilometer-agent-central、ceilometer-agent-compute和ceilometer-agent-ipmi服务组件。每种代理使用不同的轮询插件(pollster)从不同的命名空间来收集数据。
- REST ful API【被动】:用户可以通过调用RESTful API直接把利用其他方式获取的任意测量数据送达给Ceilometer。
1.2.3 数据处理方式
Ceilometer获取到测量值数据后,会把它转化为符合某种标准格式的数据采样(Sample)通过内部总线发送给Notification agent。然后Notification Agent根据用户定义的Pipeline来对数据采样进行转换(Transform)和发布(Publish)。如果根据Pipeline的定义,这个数据采样(Sample)最后被发布给Collector的话,Collector会把这个数据采样保存在数据库中。Ceilometer的计量数据经过数据采集(agent)、数据处理(流水线数据转换及发布)、数据存储(Storage)。Gnocchi是一个多租户时间序列,计量和资源数据库。提供了REST API接口来创建和操作数据,用于超大规模计量数据的存储,同时向操作者和用户提供对度量和资源信息的访问。
1.3 CloudKitty 计费
1.3.1 框架
整体上Cloudkitty计费引擎以定时(CONF.collect.period)task的形式执行计费任务;每一轮计费任务之初要知道需要为哪些租户进行计费get_tenants(),再依次对每个租户的每一项服务进行计费;首先会通过collector模块从计量数据源中获得计费数据data;其次将数据交给计费模型根据定价规则完成费用计算;最后使用storage模块将费用数据持久化存储保存。
1.3.2 CloudKitty - orchestrator(数据处理)
def run(self):
LOG.debug('Started worker {}.'.format(self._worker_id))
while True:
self.tenants = self.fetcher.get_tenants()
random.shuffle(self.tenants)
LOG.info('[Worker: {w}] {n} tena nts loaded for fetcher {f}'.format(
w=self._worker_id, n=len(self.tenants), f=self.fetcher.name))
for tenant_id in self.tenants:
lock_name, lock = get_lock(self.coord, tenant_id)
LOG.debug(