# 高可用

在负载均衡后面跑多个 Center 和 Alert 实例，以及哪些部分仍然必须单实例。

最后校对于 2026-10-10

夜莺的集群没有额外组件：**多跑几个 `n9e` 进程，配置文件完全一样，共享同一套 MySQL 和 Redis**。 这页讲怎么把这件事做对——前面的负载均衡怎么配、实例之间还需要什么、 以及哪些东西即使跑了多份也仍然是单点。

## 搭起来[​](#搭起来 "搭起来的直接链接")

在几台机器上各装一个 `n9e`（[二进制包](https://n9e.github.io/zh/docs/install/binary.md)），配置文件用同一份。 必须一致的是这三段：

```
[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"
```

启动完到**系统配置 → 告警引擎**看一眼，几个实例都应该在列表里，心跳时间在走。 规则会按一致性哈希分到各个实例上，一条规则只在一个实例上跑，不会重复告警。

**先把内置时序库关掉**，理由见下面「仍然是单点的部分」。

## 实例之间要能互通[​](#实例之间要能互通 "实例之间要能互通的直接链接")

这一步经常被漏掉：实例不只是各自连数据库，**它们之间还有直接的 HTTP 调用**。

查看告警规则的「判定执行记录」时，记录存在**当时负责这条规则的那个实例的本地磁盘上**， 你的浏览器连到哪个实例，那个实例就要向兄弟实例转发查询。转发用的是 `http://<对方 IP:端口>/v1/n9e/eval-records`，走的是服务间接口。

所以集群里每个实例都要打开这组接口，并且用同一组凭据：

```
[HTTP.APIForService]

Enable = true

[HTTP.APIForService.BasicAuth]

n9e-cluster = "<your-own-password>"
```

`[HTTP.APIForService]` **默认是关的**，配置文件里自带的那组 `user001` 口令是公开示例， 必须换成自己的。不打开的后果不是报错，而是你只能看到当前实例负责的那部分规则的执行记录。

实例的身份是 `<IP>:<HTTP 端口>`，IP 默认自动探测。容器里、或者机器有多块网卡时， 把它显式写死，否则兄弟实例可能拿到一个访问不到的地址：

```
[Alert.Heartbeat]

IP = "n9e-1.example.internal"
```

网络上也要放通：实例之间的 17000 端口互访。

## 负载均衡怎么配[​](#负载均衡怎么配 "负载均衡怎么配的直接链接")

前面挂一个四层或七层的负载均衡，把流量分到各实例的 17000：

* **健康检查**用 `GET /ping`，返回 `pong`；
* **不需要会话保持**。登录态是 JWT，签发和吊销都在共享的 Redis 里， 请求落到哪个实例都认；
* **别只转发页面路径**。写入端点 `/prometheus/v1/write`、心跳 `/v1/n9e/heartbeat`、 Web UI 和 `/api/n9e/*` 全在同一个 17000 端口上，采集器和浏览器走的是同一个入口；
* **超时给宽一点**。仪表盘和执行记录的查询可能跑十几秒，默认 60 秒的后端超时够用。

自愈任务用的 ibex RPC 在 20090，那是**长连接的 TCP**，不要放到七层代理后面， 四层转发或者让采集器直连都行。

## 仍然是单点的部分[​](#仍然是单点的部分 "仍然是单点的部分的直接链接")

多实例只消掉了「`n9e` 进程本身」这一个故障域，下面这些不在其中：

| 东西             | 状况                                                                         |
| ---------------- | ---------------------------------------------------------------------------- |
| 内置时序库       | 数据在单个实例的本地磁盘上，多实例必须关掉它，改用外部时序库                 |
| MySQL、Redis     | 它们挂了整个系统就挂，夜莺不做降级，高可用要它们自己做                       |
| 判定执行记录     | 存在各实例本地磁盘，不做副本；实例没了，它那份记录也没了                     |
| 钉钉 Stream 模式 | 只在 leader 实例上跑——leader 是活跃实例里 `IP:端口` 排序最小的那个，自动选举 |

关掉内置时序库：

```
[EmbeddedTSDB]

Enable = false
```

迁移办法见[外置时序库与双写迁移](https://n9e.github.io/zh/docs/install/external-tsdb.md)。

## 关于 n9e-alert 和 n9e-pushgw[​](#关于-n9e-alert-和-n9e-pushgw "关于 n9e-alert 和 n9e-pushgw的直接链接")

夜莺的告警判定和数据接收本来就在 `n9e` 里，扩容就是多跑几个 `n9e`。 把它们拆成独立进程是可选的，而且**发布包里没有这两个二进制**—— `Makefile` 里有 `build-alert`、`build-pushgw` 目标，需要拆分部署得自己从源码编译。

## 下一步[​](#下一步 "下一步的直接链接")

* [高可用与故障域](https://n9e.github.io/zh/docs/operations/high-availability.md)——能扛住什么、扛不住什么，以及怎么演练
* [外置时序库与双写迁移](https://n9e.github.io/zh/docs/install/external-tsdb.md)
* [升级](https://n9e.github.io/zh/docs/install/upgrade.md)——集群怎么滚动升级
