夜莺监控设计思考(一)整体定位、架构设计、单进程多进程选择、高可用设计

夜莺监控设计思考系列第一篇,从项目定位、告警引擎架构、webapi 与 alert 模块拆分、单进程和多进程取舍、以及高可用设计出发,解释夜莺为什么把核心定位放在统一告警引擎上。

作者 巴辉特

这将是一个系列,讲解 夜莺监控 的设计思考。你可以把它理解为:原理、最佳实践和产品设计时的折中取舍。

核心要点

  • 夜莺的核心定位是告警引擎,而不是完整运维平台,也不是替代所有数据源和采集器的监控大一统系统。
  • 夜莺通过对接 Prometheus、VictoriaMetrics、MySQL、ClickHouse、Postgres、ElasticSearch 等数据源,统一完成告警规则管理、异常判定和事件分发。
  • 从功能部件看,夜莺至少需要 WebAPI 和 alert 两类能力:前者负责配置和交互,后者负责周期性执行告警规则。
  • 对企业内部系统来说,多进程拆分更方便维护;对开源项目来说,单进程能明显降低普通用户部署复杂度。
  • 夜莺的高可用设计选择复用 MySQL 存储 alert 心跳信息,用简单机制完成实例存活判断和规则分片。

整体定位:夜莺首先是告警引擎

了解一个开源项目,最应该先搞清楚它的定位,也就是它到底解决什么问题。

夜莺的定位可以概括为四个字:告警引擎

夜莺对接多种数据源,比如 Prometheus、VictoriaMetrics、MySQL、ClickHouse、Postgres、ElasticSearch。用户在夜莺里配置告警规则,夜莺根据这些规则查询数据源,判定异常并产生告警事件。告警事件产生之后,再经过 Pipeline 处理,最终通过各类通知媒介发出去。

可以用 Grafana 做类比:Grafana 也对接多种数据源,但侧重数据可视化;夜莺同样对接多种数据源,但侧重告警。

夜莺监控架构图

没有夜莺时,告警通常怎么做

在没有夜莺之前,各个数据源往往各自处理告警:

  • Prometheus 的告警规则通常直接配置在 prometheus.yml 里,管理起来稍有不便。
  • VictoriaMetrics 使用 vmalert,逻辑和 Prometheus 类似。
  • ElasticSearch 社区里用得比较多的是 elastalert 开源项目做告警判定。
  • ClickHouse、MySQL、Postgres 等数据源,通常没有特别统一的告警引擎。

这样会带来一个问题:告警规则、通知媒介、消息模板、用户联系方式和历史事件都分散在不同系统里。数据源越多,规则治理越麻烦。

有了夜莺之后,就可以在一个地方统一管理告警规则、通知媒介、消息模板、用户联系方式等内容。同时,夜莺还可以对告警事件做 Pipeline 处理,例如:

  • Relabel:类似指标 Relabel,对告警事件做标签改写。
  • Enrichment:事件丰富,例如调用 CMDB 接口为事件补充上下文。
  • Drop:丢弃一些特定告警事件。
  • 其他自定义处理逻辑。

这里的核心不是“夜莺能不能接很多数据源”,而是“告警规则和事件处理能不能统一治理”。

夜莺的核心功能部件

确定项目定位之后,如果你是夜莺的设计者,接下来要回答的问题就是:一个告警引擎至少需要哪些功能部件?

首先,需要一个 WebAPI,用于和用户、第三方系统交互。用户需要在页面上做很多配置,例如:

  • 数据源配置。
  • 用户、角色和权限管理。
  • 用户联系方式管理,例如电话、手机号等。
  • 各类规则配置,例如告警规则、屏蔽规则、订阅规则。
  • 通知媒介和消息模板管理。
  • Pipeline 管理。
  • 历史告警事件查看、统计和分析。

其次,需要有后台任务执行逻辑。它根据用户配置的告警规则周期性执行,查询数据源,判定数据异常并生成告警事件,最终发送通知。

这个后台逻辑至少要考虑几件事:

  • 最简单的实现方式是一个告警规则一个 goroutine(轻量级线程)后台执行。
  • 如果执行失败,需要通过监控指标反映异常,同时打印执行失败日志。
  • 需要考虑高可用:某个实例挂掉后,其他实例要顶上来。
  • 需要考虑 sharding:比如有两个实例、1000 条规则,每个实例应处理约 500 条规则,不能重复执行,而且要尽量均匀分配。
  • 如果某个实例挂了,剩余实例要能接管原来由宕机实例负责的规则。
  • 对单个实例而言,它需要知道当前总共有多少实例、哪些实例存活、哪些实例已经挂掉,否则无法判断自己应该接管哪些规则。

这就需要一个中心状态存储,或者引入 Raft 等协议。夜莺选择了更简单的路径,后面高可用部分会展开。

这个负责告警执行的功能部件,姑且称为 alert。所以,夜莺至少有两个基础部件:webapi + alert。实际上,夜莺还有其他功能部件,但这两个是理解整体设计的入口。

单进程还是多进程

既然夜莺至少包含 webapi + alert 两类功能,那它们应该做成一个进程,还是两个进程?

如果是公司内部系统,我更倾向于做成两个进程,方便维护。但作为开源项目,还要考虑普通用户的部署复杂度,所以更倾向于做成一个进程。

这是一个典型的产品工程取舍:

方案 优点 代价
多进程 模块边界清楚,扩缩容和故障隔离更方便 部署、配置、排障复杂度更高
单进程 部署简单,普通用户更容易跑起来 模块边界不如多进程直观

对开源项目来说,“用户能快速部署起来”非常重要。一个项目如果第一步部署就很复杂,会劝退大量社区用户。因此夜莺在这个阶段选择把多个功能收敛在一个进程里。

高可用设计

对于 WebAPI 功能部件而言,它本质上是一个无状态组件:接收 API 请求,然后对数据库做 CRUD。因此 WebAPI 可以水平扩展,部署多个实例,前面架设负载均衡,就能实现高可用。

alert 模块则不同。它需要协调分配告警规则,是有状态的。既然夜莺不可避免要使用数据库存储各类配置信息,那么就顺便用数据库存储 alert 的心跳信息,这样最简单。

夜莺的设计是:所有 alert 复用一个 MySQL,周期性写心跳。这样数据库的心跳表里就可以查到所有实例列表,以及每个实例最近一次心跳时间,从而判断哪些实例还活着、哪些实例已经挂了。

这个方案的好处是架构极简:

  • 每个实例配置相同。
  • 部署多个实例即可实现高可用。
  • 实例存活状态可通过 MySQL 心跳表判断。
  • 规则分片和故障接管可以基于实例列表完成。

对社区用户来说,这个方案也容易理解和运维。

设计取舍总结

设计问题 夜莺选择 背后原因
项目定位 告警引擎 监控领域已有采集器、时序库、可视化工具,统一告警能力更缺
数据源策略 对接多种数据源 不重新造所有存储,利用已有生态
进程模型 单进程为主 降低开源用户部署复杂度
高可用协调 复用 MySQL 心跳 简单、可理解、依赖少
告警事件处理 Pipeline 化 给 relabel、enrichment、drop 等场景留扩展空间

这些选择不一定是所有系统的最优解,但它们符合夜莺的目标:做一个普通用户也能部署、企业团队也能扩展的统一告警引擎。

FAQ

夜莺为什么不定位成完整运维平台?

完整运维平台范围太大,资产、发布、变更、权限、监控、成本、流程都会牵扯进来。开源项目如果定位过宽,很容易什么都做一点但都不够深。夜莺选择把重点放在告警引擎上,是为了让项目边界更清晰。

单进程会不会影响扩展性?

单进程不等于单实例。夜莺可以部署多个相同配置的 n9e 实例,通过负载均衡承接 WebAPI 请求,并通过实例心跳和规则分片处理 alert 高可用问题。

为什么不用 Raft 做 alert 协调?

Raft 可以解决分布式一致性问题,但也会增加实现和运维复杂度。夜莺已经依赖 MySQL 存储配置,所以复用 MySQL 心跳表来做实例存活判断,是更简单、更适合社区用户的方案。

后记

本文介绍了夜莺的整体定位、核心架构、单进程还是多进程的抉择,以及高可用设计。

如果公司只有一个机房,或者有多个机房但机房之间有质量很好的网络专线,部署一套夜莺就可以了。如果有多个机房,但机房之间网络链路很差,就需要考虑夜莺的边缘机房架构模式。下一节详细介绍这个问题。

延伸路径

继续看解决方案和产品对比

如果你正在做监控、可观测性或故障定位相关选型,建议从解决方案和产品对比继续往下看。

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云