为了提升promthues的服务可用性,通常用户会部署两个或者两个以上的promthus server,它们具有完全相同的配置包括job配置,以及告警配置等。当某一个prometheus server发生故障后可以确保promthues持续可用。
同时基于alertmanager的告警分组机制即使不同的prometheus sever分别发送相同的告警给alertmanager,alertmanager也可以自动将这些告警合并为一个通知向receiver发送。
但不幸的是,虽然alertmanager能够同时处理多个相同的prometheus server所产生的告警。但是由于单个alertmanager的存在,当前的部署结构存在明显的单点故障风险,当alertmanager单点失效后,告警的后续所有业务全部失效。
如下所示,最直接的方式,就是尝试部署多套alertmanager。但是由于alertmanager之间不存在并不了解彼此的存在,因此则会出现告警通知被不同的alertmanager重复发送多次的问题。
为了解决这一问题,如下所示。alertmanager引入了gossip机制。gossip机制为多个alertmanager之间提供了信息传递的机制。确保及时在多个alertmanager分别接收到相同告警信息的情况下,也只有一个告警通知被发送给receiver。
gossip协议
gossip是分布式系统中被广泛使用的协议,用于实现分布式节点之间的信息交换和状态同步。gossip协议同步状态类似于流言或者病毒的传播,如下所示:
一般来说gossip有两种实现方式分别为push-based和pull-based。在push-based当集群中某一节点a完成一个工作后,随机的从其它节点b并向其发送相应的消息,节点b接收到消息后在重复完成相同的工作,直到传播到集群中的所有节点。而pull-based的实现中节点a会随机的向节点b发起询问是否有新的状态需要同步,如果有则返回。
在简单了解了gossip协议之后,我们来看alertmanager是如何基于gossip协议实现集群高可用的。如下所示,当alertmanager接收到来自prometheus的告警消息后,会按照以下流程对告警进行处理:
1.在第一个阶段silence中,alertmanager会判断当前通知是否匹配到任何的静默规则,如果没有则进入下一个阶段,否则则中断流水线不发送通知。
2.在第二个阶段wait中,alertmanager会根据当前alertmanager在集群中所在的顺序(index)等待index * 5s的时间。
3.当前alertmanager等待阶段结束后,dedup阶段则会判断当前alertmanager数据库中该通知是否已经发送,如果已经发送则中断流水线,不发送告警,否则则进入下一阶段send对外发送告警通知。
4.告警发送完成后该alertmanager进入最后一个阶段gossip,gossip会通知其他alertmanager实例当前告警已经发送。其他实例接收到gossip消息后,则会在自己的数据库中保存该通知已发送的记录。
因此如下所示,gossip机制的关键在于两点:
silence设置同步:alertmanager启动阶段基于pull-based从集群其它节点同步silence状态,当有新的silence产生时使用push-based方式在集群中传播gossip信息。
通知发送状态同步:告警通知发送完成后,基于push-based同步告警发送状态。wait阶段可以确保集群状态一致。
alertmanager基于gossip实现的集群机制虽然不能保证所有实例上的数据时刻保持一致,但是实现了cap理论中的ap系统,即可用性和分区容错性。同时对于prometheus server而言保持了配置了简单性,promthues server之间不需要任何的状态同步。
搭建本地集群环境
为了能够让alertmanager节点之间进行通讯,需要在alertmanager启动时设置相应的参数。其中主要的参数包括:
—cluster.listen-address string: 当前实例集群服务监听地址
—cluster.peer value: 初始化时关联的其它实例的集群服务地址
例如:
定义alertmanager实例a1,其中alertmanager的服务运行在9093端口,集群服务地址运行在8001端口。
定义alertmanager实例a2,其中主服务运行在9094端口,集群服务运行在8002端口。为了将a1,a2组成集群。 a2启动时需要定义—cluster.peer参数并且指向a1实例的集群服务地址:8001。
为了能够在本地模拟集群环境,这里使用了一个轻量级的多线程管理工具goreman。使用以下命令可以在本地安装goreman命令行工具。
创建alertmanager配置文件/etc/prometheus/alertmanager-ha.yml, 为了验证alertmanager的集群行为,这里在本地启动一个webhook服务用于打印alertmanager发送的告警通知信息。
本地webhook服务可以直接从github获取。
示例结构如下所示:
创建alertmanager.procfile文件,并且定义了三个alertmanager节点(a1,a2,a3)以及用于接收告警通知的webhook服务:
在procfile文件所在目录,执行goreman start命令,启动所有进程:
启动完成后访问任意alertmanager节点http://localhost:9093/#/status,可以查看当前alertmanager集群的状态。
当集群中的alertmanager节点不在一台主机时,通常需要使用—cluster.advertise-address参数指定当前节点所在网络地址。
注意:由于goreman不保证进程之间的启动顺序,如果集群状态未达到预期,可以使用goreman -f alertmanager.procfile run restart a2重启a2,a3服务。
当alertmanager集群启动完成后,可以使用send-alerts.sh脚本对集群进行简单测试,这里利用curl分别向3个alertmanager实例发送告警信息。
运行send-alerts.sh后,查看alertmanager日志,可以看到以下输出,3个alertmanager实例分别接收到模拟的告警信息:
查看webhook日志只接收到一个告警通知:
由于gossip机制的实现,在promthues和alertmanager实例之间不要使用任何的负载均衡,需要确保promthues将告警发送到所有的alertmanager实例中:
创建promthues集群配置文件/etc/prometheus/prometheus-ha.yml,完整内容如下:
同时定义告警规则文件/etc/prometheus/rules/hoststats-alert.rules,如下所示:
本示例部署结构如下所示:
创建prometheus.procfile文件,创建两个promthues节点,分别监听9090和9091端口:
使用goreman启动多节点promthues:
promthues启动完成后,手动拉高系统cpu使用率:
注意,对于多核主机,如果cpu达不到预期,运行多个命令。
当cpu利用率达到告警规则触发条件,两个prometheus实例告警分别被触发。查看alertmanager输出日志:
3个alertmanager实例分别接收到来自不同prometheus实例的告警信息。而webhook服务只接收到来自alertmanager集群的一条告警通知: