告警聚合技术介绍及Drools实现

背景

随着互联网产业的发展,IT设备逐渐云化,海量告警产生已经成为一个很常态化的现象,并且在不作任何处理的情况下告警数据冗余度较大,数据可读性不强,这对运维人员提出较大挑战,尤其是告警风暴出现的情况。这则情况下,需要对告警进行降噪。一般来说,降噪的基本方式有静默、抑制还有聚合。静默和抑制使用时只能针对特定告警进行使用,并且不建议长期对特定告警进行静默和抑制。而告警聚合只需要根据专家经验指定规则,不影响正常告警的生成,可以长期使用。

在进行告警聚合时,目前主流的方法是使用Drools规则引擎。drools是一款由JBoss组织提供的基于java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。

告警聚合方式

由于涉及到具体业务,告警聚合的实现结果可能大相径庭。但是聚合的方式无非是两种。一种是通过时间进行聚合,一种是以空间进行聚合。

时间聚合

告警的发生时间是告警的重要属性之一。将相关的告警根据发生时间进行聚合,有利于运维人员直接分析短时间内集中发生的告警,快速定位故障。目前常见的时间聚合手段包括滑动窗口和自扩展窗口。

滑动窗口

滑动窗口是一种直接了当的对告警进行处理的方法,即将固定时间内的发生的告警进行汇聚。时间窗口的设定问题是其中的难点,窗口设置过大或过小都会影响告警聚合的有效性。如果时间窗口过大,则导致属于多个故障的告警被聚合在一起,即增加错误聚合的个数,也可能导致上报时间过晚,影响运维人员修复; 如果时间窗口过小,则无法聚合单次故障所引发的所有告警,即我的时间窗口为5min,那么我第6分钟发生的告警也会被进行聚合。以下为Drools规则引擎实现:

//开始采集告警
declare StartCollect
    @role(event)
end
//结束采集告警
declare EndCollect
    @role(event)
end



rule "Insert AlarmList"
when 
	not(List())
then
	List alarms = new ArrayList();
	insert(alarms);
end

//开始监听告警
rule "Start collect"
when
    $alarm : Alarm()
	not(StartCollect())
	$alarms:List()
then
	$alarms.add($alarm)
    insert(new StartCollect());
	insert($alarms);
	retract($alarm);
end

//计时器
rule "End collecting alarms after 5 minutes"
timer(int:5m)
when
    $startEvent : StartCollect() 
then
	retract($startEvent);
    insert(new EndCollect());
end

//采集告警
rule "Collect"
when
	$alarm2 : Alarm()
	$alarms:List()
    $startEvent : StartCollect() 
then
    insert($alarms);
    $alarms.add($alarm)
	retract($alarm2);
end

//处理告警信息
rule "deal"
when
    $alarms:List()
	$endCollect : EndCollect()
then
    retract($endCollect);
    retract($alarms);
	//处理告警
end

也有人对该方法进行一定的进行改进,先利用较长的时间窗口存储当前的告警信息,再在该滑动时间窗口内按照时间进行告警聚合。该方法如果对告警聚合的及时性有影响,笔者不推荐进行使用。
也有人会将窗口时间改为根据产生告警时的时间差的均方差进行变动,实现时间阈值的动态更新。但是,该方法适用于时间间隔波动程度越来越大的原始报警序列,计算得出的动态时间窗口是单调递增的,实际环境中的告警可能性很多,最后的方差不肯能收敛,如果没有人员进行维护,最后的窗口事件可能被无限扩大,告警聚合也就失去意义。

自扩展窗口

自扩展事件窗口也是对滑动窗口的一种改进。其主要设计到两个数据:1.窗口长度T1.2.最大采集次数N。其具体思路是当告警进来,等待一个窗口时间,如果窗口时间内还有告警发生,那么重新等待一个时间窗口,如果没有告警产生,那么就直接直接采集,如果时间窗口的开启次数超过最大值n,那么也直接结束采集。(以下未考虑到最大采集次数n)

//开始采集告警
declare StartCollect
    @role(event)
end
//结束采集告警
declare EndCollect
    @role(event)
end
//判断时间窗内是否还有告警产生
declare JudgeTimeWindow
    @role(event)
end
//有新告警产生
declare ExistNewAlarm
    @role(event)
end


rule "Insert AlarmList"
when 
	not(List())
then
	List alarms = new ArrayList();
	insert(alarms);
end

//开始监听告警
rule "Start collect"
when
    $alarm : Alarm()
	not(StartCollect())
	$alarms:List()
then
	$alarms.add($alarm)
    insert(new StartCollect());
	insert($alarms);
	retract($alarm);
end

//计时器
rule "End collecting alarms after 5 minutes"
timer(int:5m)
when
    $startEvent : StartCollect() 
then
	retract($startEvent);
    insert(new JudgeTimeWindow());
end

//需要重新开启时间窗
rule "jude new timewindow"
when
    $judgeTimeWindow: JudgeTimeWindow() 
    $alarms:List()
    $existNewAlarm: ExistNewAlarm() 
then
    retract($existNewAlarm);
	retract($judgeTimeWindow);
    insert(new StartCollect());
end


//不需要开启时间窗
rule "jude new timewindow"
when
    $judgeTimeWindow: JudgeTimeWindow() 
    $alarms:List()
    not(ExistNewAlarm()) 
then
	retract($judgeTimeWindow);
    insert(new EndCollect());
end


//采集告警
rule "Collect"
when
	$alarm2 : Alarm()
	$alarms:List()
    $startEvent : StartCollect() 
then
    insert($alarms);
    $alarms.add($alarm)
	retract($alarm2);
end

//处理告警信息
rule "deal"
when
    $alarms:List()
	$endCollect : EndCollect()
then
    retract($endCollect);
    retract($alarms);
	//处理告警
end


当然还有一种思路可能更加合适,即一个告警发生之后,判断接下来一个时间窗口内是否有告警产生。如果有,那么这个时间窗口重新开始计时。这里主要问题是时间窗计时器的需要感知到Alarm是否被修改或者被重新插入,按照上面的方法直接修改是不可能的,因为timer计时器只能在计时结束时再判断一下条件是否满足,是否被修改则无法实时感知。这里提供一下如何进行计时的操作:

import org.kie.api.runtime.rule.FactHandle;
import org.drools.core.time.SessionPseudoClock;

rule "Start or Reset Alarm Timer"
when
    $alarm : Alarm()
    $handle : FactHandle() from entry-point "AlarmStream"  
then
    SessionPseudoClock clock = (SessionPseudoClock) kcontext.getKieRuntime().getSessionClock();
    clock.removeJob((JobHandle) $alarm.getJobHandle());  
    JobHandle newJobHandle = clock.scheduleJob(new AlarmJob(), $alarm, Duration.ofMinutes(5));
    $alarm.setJobHandle(newJobHandle);  
    update($handle, $alarm);  
end

rule "Execute Action on Alarm Timer Expiry"
when
    $alarm : Alarm()
    $handle : FactHandle() from entry-point "AlarmStream"
    $timerFired : Boolean() from entry-point "TimerStream" 
then
    System.out.println("Alarm timer expired. Executing action...");
   
end

为每个Alarm实例设置一个与之关联的定时器。当一个Alarm被modify或一个新的Alarm被插入时,现有的定时器需要被取消,并创建一个新的定时器。在Drools中,可以通过动态设置定时器并使用handle来跟踪和取消它们来实现这种行为。

这个例子使用了SessionPseudoClock,它是一个手动可控制的计时器,主要用于测试。在实际情况下,可能需要使用真实的时间触发器,例如SessionClock的实现。此外,AlarmJob类需要实现org.drools.core.time.Job接口,以便在定时器执行时进行适当的操作。

自扩展方法由于涉及到的时间比较长,笔者不建议在所有告警聚合完成之后再进行处理。如果后发生的告警对告警聚合的逻辑时间复杂度和空间复杂度的影响不高的情况下,笔者是建议之后每次发生告警直接进行处理,部分聚合后对之前的告警聚合的结果进行修改.

空间聚合

告警的空间聚合通常和具体业务相关,笔者很难进行完全的阐述,仅能选择几种常见的告警的空间关系进行描述。通常情况下,告警的空间聚合的drools实现比较简单(主要是业务复杂),笔者此处就不提供具体代码。

重复告警

大量重复告警是干扰运维人员的重要问题之一。这里的重复告警有两种情况。

1.同一告警。这种告警本质上是一个告警(即告警的id不变)在不停地反馈给运维人员。但是通常情况,告警是通过kafka上报到告警聚合。所以drools在处理时无法直接判断这条告警是否已经上报,这里注意需要根据该告警的id来进行判断告警是否直接上报,再判断该告警的其他的属性是否被修改,在进行处理。

2.某种属性相同的告警。比如监测系统定时对某设备进行检测,一直发现问题,上报的告警可能就是id不同的告警,但是告警的位置等信息相同,这类告警是有必要进行聚合的。这种告警聚合起来比较简单,在此不必赘述。当然,某些文本相似的内容,可以借助gpt等工具进行辅助聚合。

关联关系告警

告警和告警之间可能存在关联关系,这种关联关系比较复杂,需要一定的专家经验,根据专家经验去编写drools规则。当然比较简便的方式是将关联关系配置到另外的文件中并编写对应的drt文件,drools能自动将drt文件自动加载为drl文件,这也是推荐处理告警聚合时适用drools的一个原因。不过此时编写drt文件,需要考虑加载后的drl文件之间是否会存在干扰。此时规则和规则之间的通信不建议直接适用实体类,建议作为value将之put到一个Map中,key值为drl文件名称。

设备组告警

由于设备之间的互相联系,单个设备的故障也有可能会影响其他设备,或者外力因素,比如地震,停电等原因,导致某一地区的设备同时上报多个重复或者存在关联关系的告警。这类告警也需要一定的专家经验作为支撑,除了drools除了需要告警的关联关系之外,还需要设备之间的联系。

总结

告警聚合通常和业务紧密相关,不管是时间聚合和空间聚合,在编码时必须跟业务紧密结合。Drools作为一个规则引擎,其能力还是比较强大,但是处理复杂告警聚合问题时,必须注意规则和规则之间是否相互冲突的问题。

参考文献

[1]李洪成,吴晓平.基于自扩展时间窗的告警多级聚合与关联方法[J].工程科学与技术, 2017(001):049.

[2]冯学伟,王东霞,黄敏桓,等.一种基于马尔可夫性质的因果知识挖掘方法[J].[2024-02-20].

[3]晏少华.网络入侵检测系统中报警数据融合技术研究[D].沈阳航空航天大学,2011.

[4]智能运维之告警聚合技术介绍-CSDN博客

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
该资源真实可靠,代码都经测试过,能跑通。 快速:Apache Spark以内存计算为核心。 通用 :一站式解决各个问题,ADHOC SQL查询,流计算,数据挖掘,图计算完整的生态圈。只要掌握Spark,就能够为大多数的企业的大数据应用场景提供明显的加速。存储层:HDFS作为底层存储,Hive作为数据仓库 (Hive Metastore:Hive管理数据的schema) 离线数据处理:SparkSQL (做数据查询引擎<===> 数据ETL) 实时数据处理:Kafka + Spark Streaming 数据应用层:MLlib 产生一个模型 als算法 数据展示和对接:Zeppelin 选用考量: HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块:加载原始数据,清洗,加工,为模型训练模块 和 推荐模块 准备所需的各种数据。 模型训练模块:负责产生模型,以及寻找最佳的模型。 推荐模块:包含离线推荐和实时推荐,离线推荐负责把推荐结果存储到存储系统中实时推荐负责产生实时的消息队列,并且消费实时消息产生推荐结果,最后存储在存储模块中。 数据展示模块:负责展示项目中所用的数据。 数据流向:数据仓库怎么理解?两种东西,其一是IBM微软数据产品为代表的,其二是Hadoop+Hive+Apache Hive数据仓库软件有助于使用SQL读取,写入和管理驻留在分布式存储中的大型数据集。 可以将结构投影到已经存储的数据上。 提供了命令行工具和JDBC驱动程序以将用户连接到Hive。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值