foms/docs/deploy-operations.md
yumoqing fa257f36b1 feat(failover): 补全主备切换闭环 — 六步状态机 + 逆向回滚 + 健康判定器
依据现有实现补全「自动识别故障 → 人工确认切换 → 切回」运维核心闭环。
触发模式 1A+2B+3A:VIP 浮动 + 半自动 + 切前隔离原主(防脑裂)。

【表结构】
  clusters 加 vip/failover_mode;node_commands 加 command_type
  新增 switchover_steps(切换步骤级审计)

【Agent 六动作(脚本化、幂等、结构化返回)】
  fence(停MySQL+摘VIP)/unfence(恢复)/promote_master(STOP+RESET SLAVE)
  revert_promote(降回从库)/flip_sync(反转同步)/verify(可写+VIP校验)

【切换编排器 foms/failover.py】
  六步状态机:precheck→fence→promote→flip_sync→verify→converge
  失败 → 已完成步骤逆向回滚 → switchover_logs=failed
  回滚也失败 → rollback_failed + 最高级告警冒泡人工
  防脑裂不变量:fence 先于 promote,fence 失败硬中止绝不 promote

【健康判定器】
  后台循环(默认5s)扫 active 集群,心跳超时/复制异常判定
  防抖:连续 failover_threshold 次命中才判故障(防瞬时抖动误判)
  半自动:只告警(写 switchover_logs + hostwatch_events)不切,等人确认

【切回 + 回滚语义】
  trigger_switchback 反向六步;回滚用 unfence/revert_promote/反转/换回

【部署文档】
  docs/deploy-operations.md:架构/三机部署/keepalived/Agent 环境变量/
  日常运维/失败语义/防脑裂/已知限制

验证:语法全过;编排器↔Agent 六命令类型对齐;六步顺序正确;
防脑裂不变量成立(fence idx=1 < promote idx=2);注册链完整。
2026-08-26 18:33:42 +08:00

174 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FOMS 部署与运维手册
> 版本2026-08-26补全主备切换闭环后
> 适用MySQL 主从 + rsync 文件同步的双机主备VIP 浮动1A+2B+3A
## 一、架构
```
管理中心 Cfoms 应用 + 管理库 foms
▲ HTTPBearer token
┌────────────┴────────────┐
主机 Amaster 主机 Bstandby
· MySQL 主库 · MySQL 从库binlog 复制)
· 业务 + keepalived · 业务 + keepalived
· foms-agent · foms-agent
└──── VIP浮动业务连 VIP────┘
```
三个角色:
- **管理中心 C**:跑 foms核心 + dbbackup/filesync/hostwatch 模块),存配置与切换记录。
- **主机 A / 备机 B**:跑业务 + MySQL + keepalived + foms-agent守护进程
- **VIP**:业务流量入口,平时在 A故障时漂到 Bkeepalived 兜底 + FOMS 主动操作)。
## 二、管理中心 C 部署
```bash
cd foms
./build.sh # 建 venv、装依赖含三模块、生成 DDL/CRUD
```
**建库**
```bash
mysql -uroot -p < models/mysql.ddl.sql # 核心表
mysql -uroot -p < pkgs/dbbackup/models/mysql.ddl.sql # dbbackup 表
mysql -uroot -p < pkgs/filesync/models/mysql.ddl.sql # filesync 表
mysql -uroot -p < pkgs/hostwatch/models/mysql.ddl.sql # hostwatch 表
```
**配置数据库连接**:编辑 `conf/config.json``databases.foms`host/user/password/db
**启动**
```bash
py3/bin/python app/foms.py # 监听 conf 里 website.port默认 9080
```
启动时 `load_foms()` 会自动注册切换编排器并拉起健康判定器后台任务。
## 三、管理中心登记拓扑(部署后必做)
在 FOMS 界面(或 API依次创建
1. **节点**nodesA、B 各一条,填 host、ip、ssh_port、ssh_user、rolemaster/standby
2. **集群**clustersmaster_node_id=A、standby_node_id=B、`vip`(如 `192.168.1.100/24`)、
`check_interval`(检测间隔秒)、`failover_threshold`(连续故障次数)、`failover_mode=semi`
3. **复制配置**dbbackup_replications主库/从库地址、复制账号。
4. **文件同步**filesync_directories要 rsync 的目录source→dest
## 四、被管主机 A / B 部署
### 4.1 安装依赖
```bash
apt install -y mysql-server rsync keepalived ipvsadm python3
pip3 install psutil # Agent 采集系统指标需要
```
### 4.2 配置 keepalivedVIP 浮动)
A`/etc/keepalived/keepalived.conf`MASTER
```
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100 # A 高优先级
advert_int 1
virtual_ipaddress { 192.168.1.100/24 }
}
```
B 相同,但 `state BACKUP``priority 90`
keepalived 是**兜底**:机器全宕机时 VIP 自动漂移;"机器活、服务挂"的场景由 FOMS 主动 fence/promote 处理。
### 4.3 配置并启动 Agent
环境变量A、B 各自的值不同):
```bash
export FOMS_MANAGEMENT_URL=http://<C的IP>:9080 # 管理中心地址
export FOMS_NODE_ID=<本节点在nodes表的id> # A/B 各自
export FOMS_AGENT_TOKEN=<鉴权token> # 与管理中心约定
export FOMS_HEARTBEAT_INTERVAL=10
# 切换动作专用
export FOMS_MYSQL_SERVICE=mysql # MySQL systemd 服务名
export FOMS_VIP=192.168.1.100/24 # VIPCIDR
export FOMS_VIP_IFACE=eth0 # VIP 所在网卡
export MYSQL_ROOT_PASSWORD=<root密码> # promote/verify 用
# 回滚降从库用(指向"对端",即本机的复制上游)
export FOMS_MASTER_HOST=<对端IP> # B 上填 A 的 IPA 上填 B 的 IP
export FOMS_MASTER_PORT=3306
export FOMS_REPL_USER=repl
export FOMS_REPL_PASSWORD=<复制密码>
```
启动:
```bash
python3 foms-agent.py # 建议用 systemd 托管(见下)
```
**systemd 单元**`/etc/systemd/system/foms-agent.service`
```
[Unit]
Description=FOMS Agent
After=network.target
[Service]
EnvironmentFile=/etc/foms/agent.env # 上面的环境变量写这里
ExecStart=/usr/bin/python3 /opt/foms/foms-agent.py
Restart=always
[Install]
WantedBy=multi-user.target
```
```bash
systemctl enable --now foms-agent
```
### 4.4 Agent 需要的权限
Agent 以能执行 `systemctl`/`ip`/`mysql` 的用户运行(通常 root。**生产环境应收窄**
用 sudoers 白名单只允许这几条命令,而非完整 root。
## 五、日常运维
### 5.1 故障识别(自动)
健康判定器后台循环(默认 5s 扫一次):
- 判定条件master 心跳超时(> `failover_threshold × check_interval` 秒)**或** 复制状态 error/failed。
- **防抖**:连续 `failover_threshold` 次命中才判故障(避免瞬时抖动误判)。
- 判故障 → 写 `switchover_logs(status=detected, trigger=auto_detect)` + 发 `hostwatch_events` 告警。
- **半自动**:只告警不切,等人确认。
### 5.2 故障切换(人工确认)
看到告警后,人工确认 → 触发切换:
- 界面:集群列表 → 切换按钮(`trigger_switchover`
- API`POST /api/trigger_switchover.dspy {cluster_id}`
编排器异步执行六步(`switchover_steps` 表可查每步状态):
```
1. precheck 目标节点在线+心跳新鲜+复制延迟≤5s防丢数据
2. fence 隔离原主:停 MySQL + 摘 VIP ← 防脑裂,失败必中止
3. promote 提升新主STOP SLAVE + RESET SLAVE ALL原子
4. flip_sync 反转文件同步方向
5. verify 新主可写测试 + VIP 归属检查(业务层请人工确认)
6. converge 集群主备互换 + 节点角色互换 + 状态收敛
```
### 5.3 故障维修与切回
1. 修好原主 A硬件/系统/MySQL
2. 把 A 重新挂为 B 的从库(`CHANGE MASTER TO` 指向 B`START SLAVE`),等复制追平。
3. 确认 A 健康后,触发切回:`trigger_switchback`(反向六步)。
### 5.4 失败与回滚语义
- 任何一步失败 → 已完成步骤**逆向回滚**`switchover_logs=failed`
- 回滚也失败 → `switchover_logs=rollback_failed` + 最高级告警,**人工介入**。
- 回滚动作unfence恢复原主、revert_promote降回从库、flip_sync 反转、converge 换回。
## 六、防脑裂三重保险
1. **fence 先于 promote**:先停旧主 MySQL + 摘 VIP再立新主。
2. **fence 失败硬中止**:绝不 promote否则双主
3. **keepalived VRRP 兜底**:主机器全宕机时 VIP 自动漂移。
## 七、已知限制(诚实声明)
- **切换时 Agent 延迟**Agent 轮询命令,切换六步串行走完约需数十秒(运维切换通常可接受)。
- **verify 只到数据层**:验证"MySQL 可写 + VIP 可达",业务层是否正常需人工确认。
- **数据丢失窗口**precheck 要求复制延迟 ≤5sfence 到 promote 之间若有未同步 binlog 会丢失(已尽量收窄)。
- **远程命令安全**命令下发目前无白名单鉴权P1 待办),生产应收窄。
## 八、模块清单
| 模块 | 仓库 | 职责 |
|---|---|---|
| foms核心 | yumoqing/foms | 集群/节点/切换编排/心跳/远程命令 |
| dbbackup | yumoqing/dbbackup | 数据库备份 + 日志同步binlog 复制) |
| filesync | yumoqing/filesync | 运行文件同步rsync |
| hostwatch | yumoqing/hostwatch | 日志监控 + 日志分析 + 主机指标 |