回滚与恢复
撤销一次升级或一次配置改动,以及元数据库损坏之后怎么恢复。
有三件事都叫「回滚」,但机制上毫无共同之处:回退版本、回退 etc/ 里的某个文件、
撤销某个人在界面上做的改动。三者各有各的撤销方式,其中只有第一件在
回滚里给了命令。这页讲的是在一套正在运行的部署上分别怎么做,
外加元数据库自己坏掉的情况。
把集群回退到上一个版本
决定一切的约束是:schema 迁移只增不减、单向,所以不存在反向迁移。旧二进制在新 schema 上 能起来,它不认识的列会被忽略——大多数时候如此。什么时候「大多数」不成立、 以及那时该怎么办,见回滚。
集群多出来的东西是混版本窗口。两个版本的实例共用一个库和一个 Redis, 规则按一致性哈希在它们之间分派,所以回滚过程中同一条规则可能落在任意一边。 往前升的时候这没问题:新版本能读懂旧版本写的一切。往回退不对称—— 新版本运行期间创建的配置可能用了旧版本没实现的字段,而旧版本照样会去判定这些规则, 什么都不说。
所以顺序上和升级有一处不同:
- 一个一个回退,和往前升一样——30 秒的接管时间和启动后 30 秒的判定延迟都还在,见 升级与数据库迁移。
- 不要退到一半停下。 混版本状态持续几分钟没问题,持续几天是坏主意。 决定了就做完。
integrations/要跟着二进制一起换回去。那些模板是随版本发布的。- 强刷浏览器。前端静态资源就是你刚换掉的那个进程提供的。
往回退的时候没有哪个实例是特殊的。升级时跑迁移的那一个,没有什么要撤销。
撤销 etc/ 里的改动
config.toml 只在进程启动时读一次。没有 reload 信号,也没有热加载,
所以撤销一处配置改动永远是「把文件放回去 + 重启这个实例」——这让它成了最好撤销的一类改动,
代价是每次都要重启一下。
把 etc/ 放进版本库,撤销就是一条 git checkout 加一次重启,见备份与恢复。
一个实例一个实例地重启,和滚动升级一样,这样告警不会断。
要认出来的失败现象是:你把 config.toml 改回去、重启了,那个设置还在。
这说明它压根就不在文件里。站点设置、变量设置、单点登录、通知回调地址和通知脚本
都存在数据库的 configs 表里,不在 etc/ 里。它们不需要重启就生效——
配置类的内存缓存每 9 秒重新读一次数据库——撤销也在设置它的那个地方撤销。
这张表记了 update_by 和 update_at,所以「谁什么时候改的」是能查到的。
撤销界面上做的改动
告警规则、仪表盘、通知配置都没有版本历史。你和别人同时编辑同一条规则时表单会提示, 那是防止互相覆盖,不是把上一版还给你。所以恢复手段按优先级排是这样:
- 手工改回去。 规则列表里有最后修改人和修改时间,单次改动一般靠这个就能还原。
- 改之前导出的那份。 批量改之前把选中的规则导出成 JSON 是最便宜的保险。 注意导出会丢掉生效时段,重新导入后要检查这个字段。
- 昨晚的数据库备份。 从备份里捞单张表出来,得先停掉所有实例, 按备份与恢复来。
直接改数据库来修规则的话,有一个坑:告警引擎可能永远不会察觉。
规则缓存只在「启用规则的条数」或「最大 update_at」变了的时候才刷新,
所以一次两者都没动的修改会躺在表里,永远到不了引擎。改的时候顺手把 update_at
写成当前的秒级时间戳。想确认某个实例到底加载到了什么:
curl --noproxy '*' http://127.0.0.1:17000/dumper/sync
预期结果:alert_rules 那条是 success 并带着记录条数。写着 not changed
就说明引擎认为什么都没变——正是上面说的那个症状。这个接口只接受本机请求。
元数据库自己坏了的时候
动手之前先停掉所有 n9e 和 n9e-edge 进程。 还活着的实例在不停地写心跳和事件,
修复或恢复要是和它并行进行,你拿到的会是两边混在一起的东西。这一步是最容易被跳过的。
停完之后这就是一个普通的数据库问题,用数据库自己的工具处理。如果结论是「恢复备份」, 带顺序的完整流程——包括先起哪个实例、怎么确认判定恢复了——在备份与恢复里。
SQLite 值得单独说,因为它的成因很具体:两个进程同时怼一个 n9e.db 会把库写坏,
报 database disk image is malformed。这没有什么可修的。这个库是三个文件——
n9e.db、n9e.db-wal、n9e.db-shm——它们要一起动:要删三个一起删,要恢复三个一起恢复。
只删 n9e.db 会让新库配上一份旧的预写日志。起新进程之前先确认没有旧进程还在,
lsof 一下这个文件就知道。
什么是回滚拿不回来的
| 东西 | 原因 |
|---|---|
| 新版本加上的列和表 | 迁移只增不减、单向。它们会留着,而且无害 |
| 新版本写进新表里的配置 | 旧版本读不到;恢复备份会把它们删掉 |
| 备份之后产生的告警事件和通知记录 | 恢复备份会丢掉它们 |
因为调小 RetentionDuration 或 MaxBytes 而被内置时序库删掉的样本 | 删除是立刻发生的,不可逆 |
| 这段窗口里没写进来的指标 | 没有东西会补写 |
上面每一行的量级都等于窗口的长度——这就是「快点做决定」比「做对决定」更重要的理由。