openstack的告警服务以告警策略(或告警器,英文alarm
)为中心,告警检测服务轮询所有的告警策略,获取数据判断每个告警策略是否触发告警,然后调用对应通知方式发送告警通知。其中获取数据、通知方式都在告警策略里定义。
一个告警策略
告警策略的数据结构在ceilometer.api.controllers.v2.alarms.Alarm
类里有定义,各个字段都有注释。这里拷弄一个告警策略例子出来看看长什么样子。
先看一下告警策略里都有哪些字段:
- _id: 告警策略存储在数据库里的id
- alarm_id: 告警策略的id
- alarm_actions: 数组结构,告警器为alarm状态时要执行的动作
- ok_actions: 数组结构,告警器为ok状态时要执行的动作
- severity: 严重级别,可以为critical,moderate,low
- state: 告警器状态,可以为alarm,ok,insufficient_data
- timestamp: 告警器被修改的时间
- enabled: 告警器是否启用,true/false
- state_timestamp: 告警器状态被更新的时间
- rule: 告警检测规则,根据type字段不同这里的内容也不同,下面是type == threshold时rule的字段(定义在
ceilometer.api.controllers.v2.alarm_rules.threshold.AlarmThresholdRule
)- meter_name: 检测指标的名字
- query: 定义在
ceilometer.api.controllers.v2.base.Query
- field: 指定要查询的域
- op: 查询时用的操作符
- value: 与field比较的值
- type: value的数据类型
- period: 查询的时间周期,单位秒
- comparison_operator: 跟阈值比较时用的操作符
- threshold: 阈值
- statistic: 用检测数据哪个统计值来与阈值比较,比如平均值、最大值等
- evaluation_periods: 检测几个周期的数据
- exclude_outliers: 是否要去除异常的数据
- name: 告警器名字
- time_constraints: 规定告警器在哪些时间段进行检测,具体字段内容注释见
ceilometer.api.controllers.v2.alarms.AlarmTimeConstraint
- description: 描述
- start: ‘min hour day month day_of_week’格式的开始时间点
- duration: 告警器在开始时间点后多久时间内均可被检测,单位秒
- timezone: 时区信息
- insufficient_data_actions: 数组结构,告警器为insufficient_data状态时要执行的动作
- repeat_actions: 告警器的状态持续时是否重复执行相应动作
- user_id: 创建该告警器的用户ID
- project_id: 拥有该告警器的租户ID
- type: 告警器类型,K版本里有threshold、combination、gnocchi几种类型
- description: 描述
下面是一个告警器的例子,这个告警器是阈值类型告警器(type
字段),告警检测仅在每天23:00开始检测,持续检测3小时(time_constraints
字段),告警器检测id为2a4d689b-f0b8-49c1-9eef-87cae58d80db的这个虚拟机,以60s为一个周期('period': 60
),检测两个周期('evaluation_periods': 2
),如果两个周期内该虚拟机平均cpu使用率都大于50%(threshold_rule
字段),就触发告警。当前已经触发了告警('state': "alarm"
),并且会执行告警动作http://site:8000/alarm
,如果在下一次检测内还检测到告警,那么依然会执行该告警动作('repeat_actions': True
)。
1 | { |
告警轮询流程
从服务启动脚本(ceilometer/cmd/alarm.py#evaluator
)开始看,这里载入命名空间为ceilometer.alarm.evaluator_service下名为”cfg.CONF.alarm.evaluation_service”的插件,我们看到setup.cfg该命名空间下还有不少定义的插件,比如singleton, partitioned。我们可以通过阅读代码看一下这些插件的区别(代码很少),在部署了多个告警检测服务的环境下,partitioned会通过coordination这个套件在多个服务间分配告警器,比如节点1上的服务分配到A、B两个告警器,节点2上的服务分配到C、D两个告警器。singleton则是每个服务都获取所有的告警器进行检测,多实例环境下可能会有重复检测的问题。我们可以在配置文件里选择合适的插件工作,也可以自定义自己的插件。
以singleton(ceilometer.alarm.service.SingletonAlarmService
)为例,该类继承了ceilometer.alarm.service.AlarmService,在父类的初始化函数里加载了ceilometer.alarm.evaluator命名空间的插件,获取目前定义的告警器类型列表。
在start函数里,增加了一个定时任务_evaluate_assigned_alarms
,继续跟踪代码(_evaluate_assigned_alarms
->_evaluate_alarm
),在_evaluate_alarm里执行了告警器的evaluate
函数:
1 | try: |
这个evaluate函数就是每种类型的告警器的不同之处所在了,我们可以根据需要添加自己的告警器类型,只需继承ceilometer.alarm.evaluator.Evaluator
,实现evaluate
函数即可。
evaluate函数的实现就不再赘述了,这里简单说一下阈值告警器(threshold)的实现,它调用ceilometer的statistics
接口,接口根据period、evaluation_periods等参数返回数据,举个例子可能容易理解点,以上面那个告警器为例,可能会返回以下数据:
1 | [<Statistics{ |
可以看到在检测的两个周期内,该虚拟机的平均cpu使用率都超过了阈值50(一个是60,一个是70)。
需要注意的是调用statistics接口需要在ceilometer的采样服务里添加一个采样指标,不然我们将无法得到数据,在上面的例子中,我们需要在采样服务里添加一个cpu_util的采样指标才能得到返回数据。
怎么发送告警通知?
假设我们现在已经判断出该告警器已经触发告警了,现在是怎么发送告警消息的问题了,我们跟踪代码,可以看到告警器状态发生变化时的流程(_transition
->_refresh
->self.notifier.notify
),然后这里会构造参数通过rpc发送消息到openstack-ceilometer-alarm-notifier
服务去,由这个服务来完成下一步的通知步骤。
openstack-ceilometer-alarm-notifier服务收到消息后,取出消息的actions参数,这个根据当前告警器的状态不同有不同的值(取ok_actions,alarm_actions,insufficient_data_actions中的一个),然后加载对应的插件调用notify方法。
ceilometer允许我们用插件方式实现自己的通知方式,只需我们继承ceilometer.alarm.notifier.AlarmNotifier
并实现notify
方法,并在setup.cfg中注册即可。
now
在openstack新版本里已经把告警服务从ceilometer挪到aodh
中实现了,但基本原理还是一样。在分离后ceilometer专注于数据采集,而aodh也只关心告警这一服务,更好的体现了职责单一,分工协作。