跳到主要内容

高可用与故障域

多实例部署能扛住哪些故障、扛不住哪些,以及怎么演练。

夜莺的高可用没有额外组件:多跑几个 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 限制。

怎么演练​

在没出事的时候做一遍,比出事时读文档强。

  1. 确认基线。 打开告警引擎页,记下实例列表;curl --noproxy '*' http://n9e:17000/metrics | grep n9e_alert_rule_eval_total 记下判定总次数。
  2. 停掉一个实例。 kill 掉其中一个 n9e 进程(不要 kill -9,正常退出才走优雅关闭)。 预期结果:30 秒内告警引擎页上这个实例消失。
  3. 确认规则还在跑。 在剩下的实例上再取一次 n9e_alert_rule_eval_total, 预期结果:这个数在继续增长,而且增速比停机前更快(它多接了一批规则)。
  4. 确认告警还能出来。 提前建一条阈值设得必然满足的规则,让它每个周期都触发, 预期结果:停掉一个实例后,这条规则仍然按周期产生事件并发出通知。
  5. 把实例拉回来。 预期结果:约 30 秒后它重新出现在告警引擎页上, 再过一个判定周期,n9e_alert_rule_eval_total 在两边都在涨。

演练里最容易被忽略的一步是第 4 步。实例活着、指标在涨,不等于通知链路是通的—— 通知媒介、模板、通知规则任何一环配错,前三步都会显示「正常」。

相关​