开源夜莺监控DIY发版时告警静默

网友投稿 2025-06-26 09:03:01

年初时,我司已使用开源夜莺 v8 版本构建了公司级统一监控系统,但一直遗留了一个问题未被解决,就是发版时告警降噪。

据我们观察,50% 的告警噪声是由发版导致的,如果能够把监控系统和变更系统联动,发版时自动静默相关告警,那就太棒了。

发版时,通常是有人盯着服务仪表盘的,此时服务如果有问题可以及时发现。发版时的告警通知意义不大,所以我们一直想静默掉。

原始思路

起初,我们设想的是:把发版事件转成 Prometheus 指标,然后给相关告警规则都加上 and 条件,只有不发版时才会触发告警,举例:

rate(http_requests_total{service_name="gohttpd", status=~"5.."}[1m]) > 10
and on(service_name, env) 
service_updating{} == 0

但由于我们的告警规则数量很多,上百个应用多维度告警都是精确到各小组,这种给相关规则配置 and 条件的做法工作量很大,还要处理好发版事件 Prometheus 指标的转换,所以迟迟未实施。

夜莺 Pipeline 新功能带来新的可能性

六月新发布的夜莺 v8.beta11 版,引入了告警事件 Pipeline 能力。简单阅读文档了解了下 Pipeline 功能,完美,正是我想要的,脑海中浮现出了一个个用法,当然首先是发版静默的实现。

下面我简单概况下 Pipeline 功能,方便大家理解后文理解,具体可以参阅夜莺官方文档(最新版本直接在页面上就可以看到文档)。

开源版本共放出了四个 Pipeline 处理器,分别是:

  • Relabel:对告警事件的标签进行各种操作,包括删除、保留、重命名、合并和修改标签值
  • Callback:通过 HTTP 调用将告警事件信息发送到外部服务。用于通知外部系统或触发自动化处理流程
  • Event Update:通过 HTTP 调用外部服务来动态更新告警事件的内容。当告警事件流经此处理器时,处理器会将事件数据发送到指定的 URL,并根据外部服务的响应来更新事件信息
  • Event Drop:根据自定义的逻辑条件来决定是否删除特定的告警事件

Relabel 和 Callback 功能其实之前版本就有了,这里不多介绍,新版本把它们封装成了事件管道处理器,使用上更便捷灵活。

重点是 Event Update 和 Event Drop 两个处理器,此次对我帮助很大。Event Update 用于附加一些额外信息到告警事件中,比如:把事件交给 AI 分析,得到一些结论性质的信息,附加到事件中;去 CMDB 查询一些元信息,附加到事件中。Event Drop 可以通过告警事件中的字段判断是否删除告警事件。

这些处理器是可以编排使用的,所以发版降噪的实现思路就有了。

🎯 分享一条近期思考:十年磨一剑,运维监控、可观测性领域创业,拼的是产品细节和交付迭代能力

新思路

通过 Event Update 处理器把告警事件发给发版外部接口,外部接口查询服务和环境所匹配的发版事件,查询到则给原始 event 事件标签中添加 updating=true 字段(据说更好的实践是把附加的信息放到 annotations 中);然后交给 Event Drop 处理器,Event Drop 处理器查询到 updating=true 则丢弃告警。

特别说明一下,任何发版事件都是可追溯的,如 Jenkins 和 ArgoCD 等都可以通过接口获取变更事件,没有 CICD 系统的也可以通过命令查询服务启动时间来获取。我们有自研发版系统,所有发版事件都已存入了MySQL,所以查询起来更方便点。此处的外部接口只要能获取到发版事件,并附加到原始 evnet 中返回即可。

发版静默实现

上面已经介绍了实现思路,这里展开介绍下实现流程。

1. 创建 Event Update 事件管道处理器

可以设置下过略条件,我这里设置了告警规则里包含 应用监控 的才匹配此规则。

重点是这个 /n9e_pipeline/cd_denoiser 接口的实现,下面再展开讲下,先往下配置。

2. 创建 Event Drop 事件管道处理器

Event Drop 的判断逻辑:使用 go template 语法,如果最后显示为 true,将会将 event 在此环节丢弃。

我这里的语法含义就是把带有 updating=true 标签的事件丢弃:

{{ if eq $labels.updating "true" }}true{{ else }}false{{ end }}

3. 修改或创建通知规则,关联事件管道处理器

事件处理那栏添加事件处理,选择上面添加的 Event Update 和 Event Drop 处理器即可,会自动按顺序执行处理器。

4. 告警规则里选上调整后的通知规则

哪些应用层面的告警规则想要关联这个通知规则,直接在告警规则里选择上面的通知规则即可。这里就算全部配置完了,配置很简单。

新版夜莺的“通知规则”、“通知媒介”、“消息模板”等一系列概念,使用很灵活便捷,理解之后能轻松配置各种想要的通知方式。但没有银弹,缺点就是对新手不友好,需要花时间理解下这里的流转逻辑,本文不作介绍,可参考官方文档。

5. Event Update 外部接口实现

下面具体讲下 Event Update 外部接口的实现。

Event Update 通过给原始 event 事件附加信息来更新事件,所以我们得先知道原始 event 长什么样,外部接口接收夜莺的 Event Update 事件,打印出body信息即是原始 event,如下:

{
  "id": 4107,
  "cate": "prometheus",
  "cluster": "prometheus-skywalking",
  "datasource_id": 2,
  "group_id": 13,
  "group_name": "🅰️应用监控|上游融资",
  "hash": "ea724a85d3c27d17e006c3a837046151",
  "rule_id": 1992,
  "rule_name": "【应用监控|receive-server】JVM-GC次数大于100",
  "rule_note": "",
  "rule_prod": "metric",
  "rule_algo": "",
  "severity": 3,
  "prom_for_duration": 180,
  "prom_ql": "instance_jvm_young_gc_count{service='receive-server', layer='GENERAL'}  > 100",
  "rule_config": {
    "algo_params": null,
    "inhibit": false,
    "prom_ql": "",
    "queries": [
      {
        "prom_ql": "instance_jvm_young_gc_count{service='receive-server', layer='GENERAL'}  > 100",
        "recover_config": {
          "judge_type": 0,
          "recover_exp": ""
        },
        "severity": 3,
        "unit": "milliseconds"
      }
    ],
    "severity": 0
  },
  "prom_eval_interval": 60,
  "callbacks": [],
  "runbook_url": "",
  "notify_recovered": 1,
  "target_ident": "SaaS-PRO",
  "target_note": "",
  "trigger_time": 1749722880,
  "trigger_value": "106",
  "trigger_values": "",
  "trigger_values_json": {
    "values_with_unit": {
      "v": {
        "value": 106,
        "unit": "ms",
        "text": "106.00 ms",
        "stat": 106
      }
    }
  },
  "tags": [
    "__name__=instance_jvm_young_gc_count",
    "ident=SaaS-PRO",
    "layer=GENERAL",
    "rulename=【应用监控|receive-server】JVM-GC次数大于100",
    "scope=ServiceInstance",
    "service=receive-server",
    "service_instance=instance1"
  ],
  "tags_map": {
    "__name__": "instance_jvm_young_gc_count",
    "ident": "SaaS-PRO",
    "layer": "GENERAL",
    "rulename": "【应用监控|receive-server】JVM-GC次数大于100",
    "scope": "ServiceInstance",
    "service": "receive-server",
    "service_instance": "instance1"
  },
  "annotations": {},
  "is_recovered": false,
  "last_eval_time": 1749722982,
  "last_sent_time": 1749722982,
  "notify_cur_number": 1,
  "first_trigger_time": 1749722880,
  "status": 0,
  "claimant": "",
  "sub_rule_id": 0,
  "extra_info": null,
  "rule_hash": "15efd67aa7a6c305a159f4b847ab256d",
  "extra_info_map": null,
  "notify_rule_ids": [
    57
  ],
  "notify_version": 0,
  "notify_rules": null
}

可以看到,event 中有告警信息的所有详情。所以接下来要做什么就很清晰了,可以把任何关注的信息塞进去,让告警发出来或者供夜莺的后续事件处理器处理,比如把 CMDB 元信息、Deepseek 分析结果塞进去。

我这里做的就是查询发版系统对应服务对应环境的发版记录,5分钟内有发版则添加 updating=true 标签,认为需要静默。由于发版系统是Django写的,所以 Event Update 接口 /n9e_pipeline/cd_denoiser 也直接写在了发版系统的 Django 代码里,如下:

def cd_denoiser(request):
    event = json.loads(request.body.decode('utf-8'))
    logger.info("Received event: %s", request.body.decode('utf-8'))

    # 提取n9e告警事件的信息,就是把上面打出的evnet body字段重新组织提取出重点信息
    key_info = extract_key_info(event)
    # 获取报警规则名、报警主机名
    rule_name = key_info.get('rule_name', 'Unknown') 
    hostname = key_info.get('hostname', 'Unknown')
    # 获取不到环境信息时,直接返回原事件
    if hostname == 'Unknown':
        return JsonResponse(event)
    env = HOST_ENV_MAP.get(hostname, 'Unknown')
    if env == 'Unknown':
        return JsonResponse(event)

    # 查询发版系统5分钟内相应发版事件
    job_count = 0
    if 'Dubbo掉线' not in rule_name:
        # 提取出应用名称,如receive-server,如未知应用则不处理。
        # rule_name格式如:【应用监控|receive-server】JVM-GC时间大于10s
        app_name = rule_name.split('|')[1].split('】')[0] if '|' in rule_name else 'Unknown'
        if app_name == 'Unknown':
            return JsonResponse(event)    
        # 对应app_name,对应env,5分钟之内的发版数量
        start_time = datetime.now() - timedelta(minutes=5)
        job_count = tw_jobs.objects.filter(
            job_servicename=app_name,
            job_envno=env,
            job_started__gte=start_time
        ).count()
    else:
        # dubbo掉线告警,查询下此环境5分钟内全部发版,有任何发版就丢掉
        start_time = datetime.now() - timedelta(minutes=5)
        job_count = tw_jobs.objects.filter(
            job_envno=env,
            job_started__gte=start_time
        ).count()

    # 查询结果为0,说明无发版,直接返回原事件
    if job_count == 0:
        return JsonResponse(event)
    # 查询结果不为0,说明有发版,打上标签
    if 'tags_map' not in event:
        event['tags_map'] = {}
    event['tags_map']['updating'] = 'true'
    if 'tags' not in event:
        event['tags'] = []
    event['tags'].append('updating=true')

    logger.info(f"发现近期发版事件,标签更新为: {event['tags_map']}")
    return JsonResponse(event)

其中这段代码是直接查询发版系统的 MySql,并根据不同告警规则做了些定制化的查询。因为我司所有发版记录都已经记录在 MySql 了,大家也可以对接 Jenkins、ArgoCD 等接口获取到发版历史。

    # 查询发版系统5分钟内相应发版事件
    job_count = 0
    if 'Dubbo掉线' not in rule_name:
        # 提取出应用名称,如receive-server,如未知应用则不处理。
        # rule_name格式如:【应用监控|receive-server】JVM-GC时间大于10s
        app_name = rule_name.split('|')[1].split('】')[0] if '|' in rule_name else 'Unknown'
        if app_name == 'Unknown':
            return JsonResponse(event)    
        # 对应app_name,对应env,5分钟之内的发版数量
        start_time = datetime.now() - timedelta(minutes=5)
        job_count = tw_jobs.objects.filter(
            job_servicename=app_name,
            job_envno=env,
            job_started__gte=start_time
        ).count()
    else:
        # dubbo掉线告警,查询下此环境5分钟内全部发版,有任何发版就丢掉
        start_time = datetime.now() - timedelta(minutes=5)
        job_count = tw_jobs.objects.filter(
            job_envno=env,
            job_started__gte=start_time
        ).count()

6. 实现效果

观察日志,发现外部接口服务打印出了如下日志,附加上了 'updating': 'true' 标签,流转给 Event Drop 处理器后,成功把发版期间的相关告警事件静默 🎉

2025-06-12 18:09:42,171 - n9e_pipeline - INFO - 发现近期发版事件,标签更新为: {'__name__': 'instance_jvm_young_gc_count', 'ident': 'SaaS-PRO', 'layer': 'GENERAL', 'rulename': '【应用监控|receive-server】JVM-GC次数大于100', 'scope': 'ServiceInstance', 'service': 'receive-server', 'service_instance': 'instance1', 'updating': 'true'}

如上便是整个发版时静默的实现逻辑,希望对社区各位小伙伴有所借鉴。你觉得事件 Pipeline 还有哪些玩法,欢迎一起交流分享。

标签: 夜莺监控
快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云
OpenSource
开源版
Flashcat
Flashcat