高可用与故障域
多实例部署能扛住哪些故障、扛不住哪些,以及怎么演练。
夜莺的高可用没有额外组件:多跑几个 n9e,配置一致,共享同一套 MySQL 和 Redis。
这页讲这套机制到底保护了什么,哪些东西它一点都没保护,以及怎么在没出事的时候先验一遍。
集群怎么组
在几台机器上分别部署 n9e,配置文件保持一致,指向同一个数据库和同一个 Redis。
没有主节点要选,没有集群要初始化,也没有第三个组件要装。
必须一致的只有三段:
[DB]
DSN = "n9e:<password>@tcp(mysql:3306)/n9e_v6?charset=utf8mb4&parseTime=True&loc=Local"
[Redis]
RedisType = "standalone"
Address = "redis:6379"
# 同一个引擎集群里的实例才会互相分担规则
[Alert.Heartbeat]
EngineName = "default"
[Alert.Heartbeat] IP 留空时会自动探测本机出向 IP,实例标识就是 <IP>:<HTTP 端口>。
容器里跑、或者机器有多张网卡时,显式写死它,避免实例标识跟着网络环境漂。
系统配置 → 告警引擎这页列的就是当前活着的实例,按引擎集群分组, 「上次心跳时间」不再往前走,就是这个实例出问题了。
规则是怎么分派的
每个实例每秒往 alerting_engines 表写一次心跳([Alert.Heartbeat] Interval,默认 1000 毫秒)。
30 秒内有心跳的实例算活的,活实例列表被组成一个一致性哈希环,
每条告警规则按规则 ID 落到环上的某个实例——所以一条规则在同一时刻只会有一个实例在跑,
不会重复告警。
哈希环是按数据源分别建的:一个实例只参与它关联的那些数据源的分派。 超过 600 秒没心跳的行,中心进程每 10 分钟清理一次。
故障接管的时间是这么来的:
| 阶段 | 耗时 |
|---|---|
| 实例挂掉到被判定为不活跃 | 最多 30 秒 |
| 哈希环重建(下一次心跳) | 约 1 秒 |
| 接管方开始判定 | 立即,除非它也是刚重启的 |
进程刚启动后会等 30 秒才开始判定([Alert] EngineDelay,默认 30 秒),
留给配置从数据库同步进内存。滚动重启时要把这一段算进去:一次重启一个实例,
等新实例出现在告警引擎页上再动下一个。
能扛住什么,扛不住什么
| 故障 | 结果 |
|---|---|
一个 n9e 实例挂了 | 30 秒内它的规则被别的实例接管;这段时间这批规则不判定 |
一个 n9e 实例被重启升级 | 同上,另加新实例启动后的 30 秒判定延迟 |
| 元数据库(MySQL)不可用 | 全站不可用——规则、用户、事件都在这里,没有降级模式 |
| Redis 不可用 | 启动时连不上 Redis 进程直接退出;运行中断开则登录态、心跳缓存大面积异常 |
| 某个数据源不可用 | 只影响用到它的规则;判定报错会记在判定记录里 |
| 通知媒介不可用 | 事件照常产生,通知记录里是失败状态 |
| 内置时序库所在实例挂了 | 这段时间的指标写不进去,历史数据也查不到 |
| 中心与边缘机房断链 | 边缘继续本地判定和通知,见边缘断网行为 |
结论很直接:多实例只解决了 n9e 进程这一个故障域。
MySQL 和 Redis 的高可用得各自单独做,夜莺不替它们兜底。
必须先关掉内置时序库
内置时序库的数据在单个 Center 进程的本地磁盘上。多副本时每个副本只有一部分数据, 而且它们会互相改写自动注册的那个数据源地址,查询结果会静默缺失——不报错,只是图上少一截。
[EmbeddedTSDB]
Enable = false
Center 启动时如果发现同一个引擎集群里还有别的活跃实例,会打一条 warning 日志提醒这件事。 迁移办法见内置时序库的单 Center 限制。
怎么演练
在没出事的时候做一遍,比出事时读文档强。
- 确认基线。 打开告警引擎页,记下实例列表;
curl --noproxy '*' http://n9e:17000/metrics | grep n9e_alert_rule_eval_total记下判定总次数。 - 停掉一个实例。
kill掉其中一个n9e进程(不要kill -9,正常退出才走优雅关闭)。 预期结果:30 秒内告警引擎页上这个实例消失。 - 确认规则还在跑。 在剩下的实例上再取一次
n9e_alert_rule_eval_total, 预期结果:这个数在继续增长,而且增速比停机前更快(它多接了一批规则)。 - 确认告警还能出来。 提前建一条阈值设得必然满足的规则,让它每个周期都触发, 预期结果:停掉一个实例后,这条规则仍然按周期产生事件并发出通知。
- 把实例拉回来。 预期结果:约 30 秒后它重新出现在告警引擎页上,
再过一个判定周期,
n9e_alert_rule_eval_total在两边都在涨。
演练里最容易被忽略的一步是第 4 步。实例活着、指标在涨,不等于通知链路是通的—— 通知媒介、模板、通知规则任何一环配错,前三步都会显示「正常」。