概述
Nightingale 集数据采集、可视化、监控告警、数据分析于一体,与 Prometheus、VictoriaMetrics、Grafana、Telegraf、Datadog 等生态紧密协同集成,提供开箱即用的企业级监控分析和告警能力。
架构介绍
可以接收各种采集器上报的监控数据,转存到时序库(可以支持 Prometheus、M3DB、VictoriaMetrics、Thanos 等),并提供告警规则、屏蔽规则、订阅规则的配置能力,提供监控数据的查看能力,提供告警自愈机制(告警触发之后自动回调某个 webhook 地址或者执行某个脚本),提供历史告警事件的存储管理、聚合查看的能力。
橙色的部分是服务端,包含四个产品功能,拆分成两个服务模块。服务端整体架构如下:
- n9e-webapi 是 API 层,响应 ajax 请求
- n9e-server 是一个纯后端模块,跟随时序库,一套时序库对应一套 n9e-server,作为告警引擎,从数据库同步告警规则、屏蔽规则、订阅规则,在内存里处理,生成告警事件,另外 n9e-server 也提供各种数据接收接口,接收推模式的数据上报(后续章节给大家讲讲监控系统中的推拉模型,这是在监控领域一个挺有意思的话题),转发给时序库。
- MySQL 或 PG:保存用户配置的告警规则、监控大盘、自愈脚本、用户权限信息等
- Redis:要处理 jwt token,存储一些心跳信息。
- Prometheus :监控系统最核心的就是时序库了,支持多种时序库,比如 Prometheus、VictoriaMetrics、Thanos、M3DB 等,通过 remote write 方式把监控数据发往时序库,假设这里我们选择 Prometheus 作为时序库,即让 n9e-server 把数据通过 remote write 方式写给 Prometheus,那 Prometheus 就要开启 remote-write-receiver,通过 Prometheus 的启动命令参数传入:
--enable-feature=remote-write-receiver
采集器
架构上来说,指标监控系统除了依赖一个时序库,还必然需要一个采集器去采集各种指标数据,其次就是告警引擎和可视化展示,典型的系统架构如下:
采集器有很多,这里罗列一些常见的采集器,并做一些简单的主观评价,帮助各位选型。
telegraf
telegraf 是 influxdb 生态的产品,因为 influxdb 是支持字符串数据的,所以 telegraf 采集的很多 field 是字符串类型,另外 influxdb 的设计,允许 labels 是非稳态结构,比如 result 标签,有时其 value 是 success,有时其 value 是 timeout,在 influxdb 中都可以接受。但是上面两点,在类似 prometheus 的时序库中,处理起来就很麻烦。
exporters
prometheus 生态有各种 exporters,但是设计逻辑都是一个监控类型一个 exporter,甚至一个实例一个 exporter,生产环境可能会部署特别多的 exporters,管理起来略麻烦。不过这种方式架构最简单,最适合落地 IaC。
grafana-agent
大家只知道 Grafana 是绘图神器,可能未必知道 Grafana 其实还提供了一个客户端采集器。
grafana-agent import 了大量 exporters 的代码,对 exporter 有些改动,不过有些中间件,仍然是一个 grafana-agent 一个目标实例,管理起来也略麻烦。
不过 grafana-agent 主打的是三合一采集,不止 metrics,也可以采集 logs、traces。
datadog-agent
datadog-agent 确实是集大成者,但是大量代码是 python 的,整个发布包也比较大,有不少历史包袱,而且生态上是自成一派,和社区相对割裂。
datadog 毕竟是商业公司,估值几百亿美金,整体是非常成熟的,在采集侧落地了很多最佳实践,值得看一下。
prometheus
prometheus 在某个版本开始,引入了 agent mode 模式,可以作为一个采集器使用,针对 Kubernetes 平台的监控,prometheus 提供了 kubernetes 的服务发现机制,非常方便。
categraf
categraf 是近期开源的一款采集器,我们做的事情有些类似 grafana-agent,想做一个 all-in-one 的采集器。集成多种 exporter 的能力,同时,也希望集成一些进程、端口、插件、日志采集能力,在物理机虚拟机环境下,这些能力还是很关键的。同时,也希望设计成类似datadog那样,一个采集器可以采集很多个实例,而非一对一的关系。
上面这些采集器,相对比较开放,有些采集器可能只是服务于自己的体系,比如zabbix-agentd,这里就不罗列了。
UI
说完采集器,我们直接跳到最上面的 UI 层(先把简单的易于理解的模块说完~)。UI 层提供一个用户和系统交互的接口(用于看图、配置告警规则等),比如 Zabbix 的那个 PHP 写的 WEB模块、比如夜莺的 Webapi 模块,比如 Datadog 的 Cloud UI,Prometheus 也有,是集成到自己的唯一二进制中了。
UI 侧的功能非常直观,最核心就是看图、做配置、查看事件,比如配置各种 Dashboard、配置告警规则、屏蔽规则等,查看告警事件。
UI 模块通常和数据库打交道,把用户的配置写入数据库,也会和时序库打交道,因为要看图就要拉取时序库的数据,比如 Zabbix 的 WEB 模块要读取数据库的时序数据,把配置写入数据库,比如夜莺的 Webapi 模块,要读取时序库的数据,同时把用户配置的信息写入关系库。
引擎侧
监控系统通常有个可以水平扩展的服务端引擎,用于判断监控数据是否触发阈值,生成告警事件。比如 Zabbix 的 Server 模块,比如 Open-Falcon 的 judge 模块,比如夜莺的 Server 模块,当然,Datadog 这种云服务我们看不到架构,但是理论上,肯定也是类似的架构。
引擎侧要想处理告警规则,那首先得从某个地方拿到这些规则,显然是从数据库获取的,但是每次频繁查询数据库又会对数据库造成压力,所以一般都是选择周期性同步,在内存里缓存的方式。
引擎侧产生告警事件之后,可以自己去发送,也可以交给单独的模块处理,比如夜莺是 Server 生成事件直接就发送了,Open-Falcon 则是把事件交给 alarm 模块去做后续处理。Prometheus 生成的事件是交给 alertmanager 做后续处理。
对于单独有个模块处理后续事件这种架构,可以在这个模块里做一些聚合降噪类的事情,像 Bigpanda 这种产品,就是专门做这个事情的。
时序库
时序库是监控系统架构的重要组成部分,比如 Prometheus 内置的时序库,VictoriaMetrics、M3DB、Open-Falcon 的 Graph 模块、TDEngine、InfluxDB 等,还有像 Zabbix 这种直接使用 MySQL、PG 作为时序库。
不同的时序库架构不同,但既然是存取数据的,有些通用的问题是必须要解决的,比如数据分片逻辑、副本管理、wal、随机写转换为顺序写等等。这里我们拿 VictoriaMetrics(简称VM) 来举例:
VM 的架构非常简单清晰,会有其局限取舍,但简单的架构确实更稳定,上图就是 VM 的架构,核心有3个模块,每个模块都可以部署多个实例,vmselect 和 vminsert 都是无状态的,前面架设负载均衡:
- vmselect:用于响应查询请求。
- vminsert: 用于响应插入请求。
- vmstorage: 是用于存储数据的。
VM 的架构之所以简单,是因为其 merge read 机制,不关心数据迁移问题。比如某个机器的CPU.UTIL数据,有些数据分布在vmstorage01节点,有些分布在vmstorage02、vmstorage03节点,查询的时候,会同时发请求给这3个节点,拿到查询结果之后,merge在一起返回给client。所以,扩容很方便,接收到数据之后做分片完全可以不用维持一个稳态的对应关系(指series监控数据和vmstorage的对应关系)。如果某个节点挂了,会丢失一部分数据,如果是3副本的,就还会有其他副本在其他机器上,仍然可以读取。
分布式部署方案
监控系统5.0版本开始,架构已经非常简单,核心包括三个组件:采集器categraf、和前端交互的模块webapi、数据转发和告警引擎模块server。
- categraf:采集器,用于采集监控数据,比如OS的CPU、内存、IO相关指标,数据库的指标,中间件的指标等等,都统一使用categraf来采集,当然,也可以使用telegraf、datadog-agent、grafana-agent等社区的其他采集器。categraf要采集OS的指标,通常要在所有目标机器上都要部署一个,对于中间件、数据库的监控数据采集,大都是通过远程查询的方式,categraf倒是不必一定和中间件、数据库部署在同一台机器上
- webapi:用于和前端JavaScript交互的模块,对数据库做增删改查,webapi可以水平扩展,部署多个实例,一般是部署在中心IDC,前面放置nginx或者lvs,这样如果有某个webapi模块挂掉了,nginx或lvs可以自动感知并摘除故障实例,对用户无感
- server:server模块是随着时序库走的,一套时序库对应一套server,server核心有两个作用,一个是接收监控数据的推送,转发给时序库,另一个是作为告警引擎,从数据库同步告警规则,然后为每个告警规则启动goroutine,周期性的查询时序库,判断是否应该生成告警事件。
除了自身的3个组件,夜莺还依赖MySQL和Redis,MySQL用于存放各类用户配置,比如监控大盘、告警规则、屏蔽规则、订阅规则等,Redis有两个作用,一个是存放jwt token,另一个是作为一个注册中心,存放存活的server和ident列表,各个server模块就可以从Redis这里,知道全局活着的server列表,知道有哪些ident存活,便于生成target_up的指标数据。
单机部署方案
这个方案在之前的章节已经讲解过了,不过为了内容完整性,这里再重复一下。对于绝大部分公司,机器规模小于1000台,基本上单机部署方案就够用了,如果单机扛不住可以升配,运维成本是最低的。
虚线框的部分就是要在单机部署的组件,n9e-server 和 n9e-webapi 是夜莺的核心模块,MySQL、Redis、Prometheus是依赖的其他开源项目。
在所有要监控的目标机器上,安装agent,采集监控数据,agent自然是首推categraf,当然,也可以用telegraf、grafana-agent、datadog-agent。习惯使用exporter的朋友,也可以继续使用各类exporter作为采集器。
中心集群部署方案
很多公司都是使用公有云的服务,公有云大都提供托管的rds、redis、prometheus,我们可以直接使用,这样就只需要自己创建虚拟机部署夜莺的server和webapi模块即可,假设我们有3台机器,部署方案就是在每台机器上分别部署server和webapi模块,然后在server和webapi前面分别配置负载均衡。
server的负载均衡地址暴露给agent,agent用来推送监控数据,webapi的负载均衡地址可以配置一个域名,让终端用户通过域名访问夜莺的UI。此时,前端静态资源文件是由n9e-webapi来serve,也可以搭配一个小的nginx集群,把webapi作为nginx的upstream,前端静态资源文件由nginx来serve。
或者,时序库不采用云平台托管的,自己来搭建时序库集群,此时就需要有能支持分布式部署的时序库,且兼容 Prometheus 的查询接口,常用的是M3DB和VictoriaMetrics,前文已经有介绍,建议使用VictoriaMetrics,相对更简单一些。
多地域拆分简单方案
实际工作环境下,很多公司会把 Prometheus 拆成多个集群,按照业务线或者按照地域来拆分,此时就相当于夜莺接入多个 Prometheus 数据源。中心端部署 webapi 模块,而 server 模块是随着时序库走的,所以,时序库在哪个机器上,server 模块就部署在哪个机器上就好,架构图如下:
多地域拆分集群方案
最后一种部署方式,最为复杂,是把 server 和 webapi 模块都做了集群高可用,也就是 github 放的那张架构图:
中心端是 webapi 集群、redis、mysql,每个地域是时序库、redis、server集群,redis实际可以复用中心的那个,但是不推荐,担心网络链路可能不好影响通信,最好是和 server 集群放到一个地域。
参考
龙渊秦五专栏连载:《说透运维监控系统》