为自己的系统定制openstack ceilometer

时间:2022-07-22 23:52:20

一、目的

最近研究了一下ceilometer,目的做一个监控系统,对系统中发生的事件进行处理。ceilometer对openstack各个组件信息的收集方式主要由 推 和  拉 两种。

“推”: 就是openstack的各个组件将信息以notification的形式发送到message bus,然后有agent_notification对消息进行监听,agent_notification 在接收到消息后,经过一个pipeline的处理,然后再通过rpc发送给collector,

collector负责将接收这些消息并且存储到持久化介质中,如 database,file等;这样中间会经过两次rpc的发送;

“拉”:  openstack ceilometer会部署compute agent 还有 central agent 来主动的轮询(polling)一些组件的信息,compute agent主要负责收集虚拟机 cpu 内存、io等信息,compute agent会部署到每个虚拟机上,compute agent定期通过hyperviser api去获取信息。central agent 只部署在控制节点上,负责收集其他的信息image ,network等等,是通过restfulapi的方式调用;两种agent收集到消息后也是发送到rpc,然后collector收集;

二、设计方案

回到我们项目的需求上,我们只需要收集系统中的事件(event),其实ceilometer对收集信息的定义包括event和sample,event 就是一件发生的事情(例如: intance.create),sample是事件发生过程的产生的具体数据,(例如,为了instance分配了多少memory)。我们的系统中只需要处理event不用处理sample。所以删除了对sampe部分的处理。

第二个改动是,我们这边只有notification发出的消息,也就是只有推的机制,没有拉的机制,这样,其实就可以考虑不用对notification两次rpc传输了。考虑agent_notification接受到消息后直接存储到数据库,将collector做的事情放到agent_notification中,但最终程序的名字还是叫collector。。。。上图

为自己的系统定制openstack ceilometer

系统中的各个组件通过notification api 发送消息到message bus(rabbit mq),collector通过监听一定的exchange和topic来获取notification信息,

然后交给预先定义好的函数(event process pipeline)来处理,最后通过dispatcher转存储到持久化介质中;其中的event definition部分通过event_definition.yaml 来进行控制;

三、示例sample

(1) 发送notification消息,

具体请参见higgs/sample/manager.py中的_sample_notify_period函数
from openstack.common.notifier import api as notifier_api
…...
payload = {
                'samples': [
                            {
                                'name': 'test_notify_sample',
                                'type': 'gauge',
                                'unit': 'ns',
                                'volume': 1,
                            }
                            ],
                'reason': 'delete',
                'user_id': 'test',
                'tenant_id':'test',
                'instance_id':'1234567890',
            }
 
 
publisher_id = notifier_api.publisher_id('higgs_sample')
notifier_api.notify(context,publisher_id,'compute.instance.test.samples',notifier_api.CONF.default_notification_level,payload)
……

字段的具体定义如下:

a) publisher_id 是数据的发送方,openstack中是指"the source worker_type.host of the message, e.g. ‘compute.host1’ “,

获取值的顺序是 CONF.host > CONF.default_publisher_id > socket.gethostname(),也可以通过notifier_api的publisher_id来进行设置;
 
b) ‘compute.instance.test.samples’: 这个值是event_type,是string类型;如果要定义和发送一个新的事件,就起一个新的名字,例如:’scheduler.trigger’
 
c) payload:中文释义是“装载” 顾名思义,就是发送一个事件时附带的详细信息,这个参数比较重要,collector中对事件的处理大部分的信息都是从payload中获取的;

d) CONF.default_notification_level 默认的notification级别,级别为 DEBUG < INFO < WARNING < ERROR < CRITICAL;

(2)collector中对事件的处理

collector对事件的处理是通过yaml文件可配置的,具体的定义在event_definition.yaml中,截取一部分如下:
- event_type: compute.instance.*
  traits: &instance_traits
    tenant_id:
      fields: payload.tenant_id
    user_id:
      fields: payload.user_id
    instance_id:
      fields: payload.instance_id
    host:
      fields: publisher_id
      plugin:
        name: split
        parameters:
          segment: 1
          max_split: 1
    service:
      fields: publisher_id
      plugin: split
    memory_mb:
      type: int
      fields: payload.memory_mb
    disk_gb:
      type: int
      fields: payload.disk_gb
.......

具体含义:
a) event_type: 被识别的event_type名称,支持通配符,这里的意思是,凡是以compute.instance.开头的事件都会有这段逻辑来处理;
traits: trait的中文是“特点”,可以理解为是一个event所携带的一个特点或者属性;例如trait下面的tenant_id、user_id......等都是一个trait;
 
b) tenant_id:
      fields: payload.tenant_id 
这两行一起看,表示这个trait的名字是tenant_id,它的值从你发送的event所携带的payload中的tenant_id中提取,payload.tenant_id的写法符合jsonpath https://pypi.python.org/pypi/jsonpath/ 
 
c) host:
      fields: publisher_id
      plugin:
        name: split
        parameters:
          segment: 1
          max_split: 1 
这七行一起看,这个trait的名字是host,它的值是从event的payload中的publisher_id这一项提取,不同的是,它要对提取的值进行一定的处理,调用name 为split的插件,给插件提供两个参数:
segment和max_split;collector使用了stevedore来增加可拓展性,split的具体定义可以在setup.cfg中进行查看;
如果需要自己发送的event进行特殊的处理,可以自己仿照着写插件;
 
d) memory_mb:
      type: int
      fields: payload.memory_mb
这三行一起看,与前两个不同的是,有一个type参数。这个type是针对trait而言的,trait有四种类型,string、int、float、datetime;如果没有实际写明type的类型,默认是string类型;

总之,如果你有一个新的event要处理,首先你要定义这个event中的重要信息,然后在event_definition.yaml中写一个类似的将event转成trait的处理单元;

四、数据库结构

简单的介绍下 event 和 trait的数据库结构,对于理解这个两个概念更有好处;
event
+---------------+---------------+------+-----+---------+----------------+
| Field         | Type          | Null | Key | Default | Extra          |
+---------------+---------------+------+-----+---------+----------------+
| id            | int(11)       | NO   | PRI | NULL    | auto_increment |
| message_id    | varchar(50)   | YES  | UNI | NULL    |                |
| event_type_id | int(11)       | YES  | MUL | NULL    |                |
| generated     | decimal(20,6) | YES  | MUL | NULL    |                |
+---------------+---------------+------+-----+---------+----------------+
每一个 event 都会有一个message_id,系统会默认生成,通过uuid;
event_type_id是与另外一张event_type表相关联;
generated是时间戳;
 
 
event_type
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| desc  | varchar(255) | YES  | UNI | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
id表示event的type id,与event表中的event_type_id是外键关联;
 
trait
+---------------+---------------+------+-----+---------+----------------+
| Field         | Type          | Null | Key | Default | Extra          |
+---------------+---------------+------+-----+---------+----------------+
| id            | int(11)       | NO   | PRI | NULL    | auto_increment |
| t_string      | text          | YES  |     | NULL    |                |
| t_float       | double        | YES  | MUL | NULL    |                |
| t_int         | int(11)       | YES  | MUL | NULL    |                |
| event_id      | int(11)       | YES  | MUL | NULL    |                |
| trait_type_id | int(11)       | YES  | MUL | NULL    |                |
| t_datetime    | decimal(20,6) | YES  | MUL | NULL    |                |
+---------------+---------------+------+-----+---------+————————+
t_ 开头表示trait的类型,在openstack中t_string是varchar(255),bsp考虑到可能会发送比较长的字符值,所以改成了text;
event_id与event表中的id是外键关联;
trait_type_id与trait_type表中的id是外键关联;
 
trait_type
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| desc      | varchar(255) | YES  | MUL | NULL    |                |
| data_type | int(11)      | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+————————+
id 与 trait表中的trait_type_id是外键关联;