MySQL MHA-Go 部署说明
本文说明 mha_go.yml 的支持边界与基础使用方式。mha_go.yml 部署的是 Go 重写的 MHA 管理器(mha-go),它不依赖 Perl MHA 工具链,以一个静态二进制和一份 YAML 配置运行在 manager 节点上。
当前 mha_go.yml 的定位是:在已经由 dbbot master_slave.yml 部署完成的一主多从 GTID 拓扑上部署 mha-go manager。如果还没有 MySQL 主从拓扑,请先运行 master_slave.yml。
1. 原理
mha-go 围绕 GTID 单主复制做故障检测、在线切换与故障转移:
- GTID-only:只支持 GTID 复制,不支持 binlog file/position 模式。
- 拓扑发现:通过 SQL 探测主从角色、GTID、只读状态、复制线程、复制延迟与半同步状态。
- 候选主选择:
slave_ips的顺序映射为candidate_priority,第一个从库优先级最高。 - 故障恢复策略:dbbot 默认使用
availability-first,旧主整机不可达时允许提升最先进的存活从库;旧主上尚未复制出去的事务可能丢失。 - 写入口抽象:默认
writer_endpoint.kind: none;开启 VIP 后由/usr/local/bin/mha_ip_failover.sh切换写入口。 - 执行形态:manager 以
mysql用户通过systemd常驻,日志使用 JSON 输出到 journal。
2. 与传统 MHA 的对比
| 维度 | 传统 MHA (mha.yml) | MHA-Go (mha_go.yml) |
|---|---|---|
| 实现语言 | Perl | Go,单个静态二进制 |
| MySQL 支持 | 主要面向 5.7 历史拓扑 | 8.4.x、9.7.0 ER/EA |
| 复制模式 | binlog 位置 / GTID 均可 | 仅 GTID |
| 配置格式 | INI (app1.cnf) | YAML (cluster.yaml) |
| 运行形态 | masterha_manager 前台或 nohup | mha-manager.service |
| 运行用户 | 通常 root | mysql 用户 |
| 写入口 | 传统 VIP hook 脚本 | writer_endpoint 抽象,默认不启用 |
| Dry-run | 弱 | switch / failover-execute 原生支持 --dry-run |
新建 MySQL 8.4/9.7 GTID 主从集群优先使用 mha_go.yml;只有维护 MySQL 5.7 历史拓扑时才继续使用传统 mha.yml。
3. 支持边界
- 目标架构:一主多从 + 一个 MHA-Go manager。
- MySQL 版本:
8.4.x或9.7.0ER/EA。 - 复制模式:必须开启
gtid_mode=ON和enforce_gtid_consistency=ON。 - 默认部署:纯异步复制,
semi_sync.policy: disabled,salvage.policy: availability-first。 - 半同步:只有已经安装并启用 MySQL semi-sync 插件时,才建议把策略改为
preferred或required。 - 不支持:MySQL
5.7、8.0、9.6、非 GTID 复制。
MySQL 9.7.x 使用 glibc2.28 包,不支持 CentOS/RHEL 7 系列;这类系统请使用 MySQL 8.4.x 作为 MHA-Go 前置主从集群。
4. 拓扑约定
mha_go.yml 复用 [dbbot_mysql] 主机组,通过 master_ip / slave_ips / manager_ip 区分节点:
master_ip:当前主库,在cluster.yaml中登记为db1。slave_ips:从库列表,在cluster.yaml中登记为db2、db3等。manager_ip:运行mha-manager的节点,必须是slave_ips中的从库,不能是master_ip。默认使用最后一个从库,使主库宿主机故障时不会同时带走故障转移控制器;第一个从库仍作为最高优先级候选主。
dbbot 默认测试环境:
192.168.161.11 primary
192.168.161.12 replica / preferred failover candidate
192.168.161.13 replica / manager
5. 关键变量
编辑 mysql_ansible/playbooks/vars/var_mha_go.yml:
master_ip: 192.168.161.11
slave_ips:
- 192.168.161.12
- 192.168.161.13
sub_nets: "192.168.161.%"
manager_ip: "{{ slave_ips | last }}"
mha_go_cluster_name: app1
mha_go_semi_sync_policy: disabled
mha_go_semi_sync_wait_for_replica_count: 0
mha_go_semi_sync_timeout: 5s
mha_go_salvage_policy: availability-first
mha_go_salvage_timeout: 30s
常用变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
manager_ip | `{{ slave_ips | last }}` |
mha_go_binary_dest | /usr/local/bin/mha | manager 节点上的二进制路径 |
mha_go_config_dir | /etc/mha | cluster.yaml 目录 |
mha_go_log_dir | /var/log/mha | 日志目录 |
mha_go_service_enabled | true | 是否 enable systemd 服务 |
mha_go_semi_sync_policy | disabled | disabled、preferred 或 required |
mha_go_salvage_policy | availability-first | 主库不可达时的一致性/可用性取舍 |
mha_go_writer_endpoint_enabled | false | 是否启用 VIP 写入口切换 |
6. 前置条件
make_mha_go 执行前会检查:
- 每个节点的
datadir下存在master_slave_finish.flag,即已经完成master_slave.yml。 - 每个节点都开启
gtid_mode=ON和enforce_gtid_consistency=ON。 master_ip、slave_ips、manager_ip都能在 inventory 中找到,且manager_ip必须属于slave_ips。
mha_go.yml 当前 role 顺序:
pre_check_and_set -> make_mha_go
它不会重装 MySQL,也不会重建复制关系。
7. 执行入口
cd /usr/local/dbbot/mysql_ansible/playbooks
python3 /usr/local/dbbot/portable-ansible/ansible-playbook \
-i ../inventory/hosts.ini \
mha_go.yml \
-e dbbot_confirmation_input=confirm
使用 dbbot 公开默认密码测试时,还需要显式允许:
-e '{"fcs_allow_dbbot_default_passwd": true}'
8. 执行后产物
manager 节点上会生成:
/usr/local/bin/mha:当前 dbbot 内置的 mha-go 静态二进制。/etc/mha/cluster.yaml:集群配置,topology.kind: mysql-replication-single-primary,replication.mode: gtid。/etc/systemd/system/mha-manager.service:以mysql:mysql运行mha manager --log-format json。/var/log/mha/:日志目录。
manager 节点的 datadir 下会生成 mha_go_finish.flag。
启用 VIP 时还会生成:
/usr/local/bin/mha_ip_failover.sh:root:mysql 0750的 VIP 切换脚本。/home/mysql/.ssh/id_rsa_dbbot_mha_go:仅供 manager 执行 VIP hook 的 RSA-3072 专用私钥。/home/mysql/.ssh/known_hosts_dbbot_mha_go:由部署期采集并固定的节点 host key。/usr/local/libexec/dbbot-mha-go-vip-ssh:root 管理的 forced-command 白名单,专用公钥不能获得普通mysqlshell。/etc/sudoers.d/dbbot-mha-go-vip:只允许当前 VIP、掩码、网卡对应的ip addr add/del和arping命令。/var/lib/dbbot/mha-go-vip.yml:用于在关闭 VIP 功能时准确清理旧地址和配套权限的受管状态。
9. 常用命令
在 manager 节点执行:
mha version
mha check-repl --config /etc/mha/cluster.yaml
mha switch --config /etc/mha/cluster.yaml --new-primary db2 --dry-run
mha failover-plan --config /etc/mha/cluster.yaml
systemctl status mha-manager
journalctl -u mha-manager -f
failover-plan 和 failover-execute 会在主库仍存活时阻断,这是正常保护。
9.1 从库重启后的只读状态
MHA-Go 复用 master_slave.yml 的异步复制基础配置,my.cnf 保持 super_read_only=OFF。master_slave.yml 首次建立复制时会在从库运行时设置 super_read_only=ON,但该状态不会通过基础模板持久化。当前 MHA-Go manager 会探测并报告只读状态异常,不会在 mysqld 重启后自动修改数据库变量。
每次数据库实例或主机重启后,在恢复业务流量前必须重新发现实际拓扑。只在已经确认的从库上执行:
SHOW REPLICA STATUS\G
SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;
SET GLOBAL super_read_only = ON;
SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;
验收时应保证当前主库的 read_only 和 super_read_only 均为 0,每个从库均为 1,复制线程正常,然后重新执行:
mha check-repl --config /etc/mha/cluster.yaml
不要根据旧的 master_ip 或 cluster.yaml 角色判断当前真实主库,也不要在真实主库上执行 SET GLOBAL super_read_only=ON。异步拓扑没有把该值写死为 ON,是为了避免当前主库 crash 后在尚未发生切换时自行恢复为只读;重启后的从库只读状态需要由运维流程持续保证。
9.2 整机故障、Salvage 与 manager 生命周期
dbbot 生成的 cluster.yaml 不包含节点级 ssh 配置。MHA-Go 的 SQL 探测、候选主选择、提升和复制重指向不依赖 OS SSH,也不会默认执行 SSH binlog salvage。只有启用 VIP 后,外部 VIP hook 才需要 manager 上的 mysql 用户通过 SSH 登录各节点;剧本会建立并验证这条专用链路。因此,未启用 VIP 的部署测试通过,不能证明 VIP SSH 路径可用。
默认 availability-first 会在旧主整机不可达、无法证明或抢救旧主独有事务时继续提升最先进的存活从库。这符合传统 MHA 的可用性优先语义,但纯异步复制下可能丢失只提交在旧主且尚未传到任何从库的事务。若业务必须优先避免这种风险,可改为 salvage-if-possible 或 strict;旧主完全不可达且没有节点级 SSH/agent salvage 时,这两种策略会阻断自动提升并需要人工处理。
主库直接探测达到阈值后,manager 还会等待从库复制 IO 线程不再确认旧主可达,以降低 manager 单点网络隔离造成的误切换。总故障确认时间因此还受 MySQL replica_net_timeout、复制 heartbeat 和操作系统 TCP 超时影响,并不只由 monitor.interval、failure_threshold、reconfirm_timeout 三项相加决定。
一次故障转移成功后 manager 按设计正常退出,因为原 cluster.yaml 的角色已经过期。先恢复或重建旧主、按真实拓扑更新 master_ip / slave_ips,再重新运行 mha_go.yml。若执行被阻断或中途失败,当前内置二进制会返回非零,Restart=on-failure 会拉起服务重试;仍应先查看 journal 中的第一处 blocking/failed step,不能把持续重启当作已恢复。
10. 卸载
mha_go_unsafe_uninstall.yml 会先停止并禁用 mha-manager.service,删除 mha-go manager 文件和配置的 VIP,再按当前 mysql_port 清理 MySQL 实例数据、日志和运行目录:
cd /usr/local/dbbot/mysql_ansible/playbooks
python3 /usr/local/dbbot/portable-ansible/ansible-playbook \
-i ../inventory/hosts.ini \
mha_go_unsafe_uninstall.yml \
-e dbbot_confirmation_input=confirm
如果你只想保留 MySQL 主从拓扑,不要执行这个入口;它会一并清理当前 mysql_port 对应的 MySQL 实例。
11. 启用 VIP 写入口
默认不启用 VIP。如果需要固定写入口:
mha_go_writer_endpoint_enabled: true
vip: 192.168.161.10
vip_netmask: "32"
net_work_interface: enp1s0
开启后剧本会:
- 检查 VIP 未错误驻留在非主库节点,并首次绑定到
master_ip。 - 在 manager 生成独立 SSH key,把带 forced-command 限制的公钥写入所有 MySQL 节点,并以
BatchMode=yes、严格 host-key 校验逐节点验证;白名单外命令会被拒绝。 - 安装参数级 sudo 白名单;不会授予无参数限制的
/usr/sbin/ip,也不会授予scp。 - 部署 VIP hook,并在切换时从旧主移除 VIP、添加到新主、发送 gratuitous ARP。
vip、vip_netmask、net_work_interface 必须与真实网络一致。dbbot 192.168.161.* 默认测试 inventory 中的样例环境通常使用 enp1s0;其他环境请先用 ip route 或 ip addr 确认真实网卡名。修改 VIP、掩码或网卡时,修改 var_mha_go.yml 后重新运行剧本,让 VIP 脚本、sudo 白名单和当前主库绑定一起更新;不要只手改其中一个文件。若旧 VIP 仍在非主库,剧本会停止并要求先清理,避免重复地址。
将 mha_go_writer_endpoint_enabled 改回 false 并重跑剧本时,dbbot 会依据 /var/lib/dbbot/mha-go-vip.yml 删除旧 VIP、sudo policy、专用 SSH 授权和脚本。不要在关闭前手工删除该状态文件。修改 manager_ip 并重跑时,非 manager 节点上旧的 mha-manager.service 会先被停止和删除,旧 binary/config/finish flag 也会清理,避免本地内存 lease 下出现双 manager;旧 journal 日志不会被这一步删除。
12. 注意事项
mha_go.yml是增量部署入口,不是 MySQL 主从初始化入口。manager_ip必须是slave_ips中的从库。不要把 manager 与当前主库放在同一节点,否则主机级故障会同时中断数据库和故障转移控制器。- 默认
availability-first优先恢复写可用性,不代表零数据丢失;纯异步复制的 RPO 取决于故障发生时事务是否已经到达存活从库。 - MySQL 9.7.0 的
cluster.yaml会自动写入version_series: "9.7";MySQL 8.4.x 会写入"8.4"。 - dbbot 默认半同步策略是
disabled,避免在未安装 semi-sync 插件的纯异步主从上误报。 cluster.yaml权限为0640,属主为mysql:mysql,因为 systemd 服务以mysql用户读取它。