MySQL MHA-Go 部署说明

本文说明 mha_go.yml 的支持边界与基础使用方式。mha_go.yml 部署的是 Go 重写的 MHA 管理器(mha-go),它不再依赖 Perl MHA 工具链,以单个静态二进制 + 一份 YAML 配置的形式运行在 manager 节点上。

1. 原理

mha-go 采用"控制器主循环 + 从库侧观察者"的架构,围绕 GTID 复制做故障检测与切换:

  • 主循环:manager 进程以 controller.monitor.interval(默认 2s)为周期探测所有节点。连续 failure_threshold(默认 3 次)判定异常后,再经过 reconfirm_timeout(默认 5s)二次确认,才进入 failover 流程,避免抖动误判。
  • Secondary observer:除了 manager 自己探测,cluster.yaml 会为每个 replica 注册一个 secondary_checks 条目,由 replica 节点反向确认主库存活,防止 manager 单点网络分区导致误切。
  • Lease 选主controller.lease.backend: local-memory,TTL 15s,确保同一时刻只有一个 manager 实例做出切换决策。
  • GTID-only 恢复:复制模式固定为 gtid,不再依赖 relay log diff / binlog 位置拼接。replication.salvage.policy: salvage-if-possible 决定是否在主库失联时尽力追补丢失事务。
  • 半同步偏好semi_sync.policy: preferred + wait_for_replica_count,在可用时优先走半同步确认,在超时后平滑降级。
  • 候选优先级slave_ips 的顺序直接映射到 candidate_priority(100、90、80……),用于 failover 时选定新主。
  • 写入端点抽象writer_endpoint 是独立一层,默认 none,开启 vip 模式后由 manager 调 /usr/local/bin/mha_ip_failover.sh 完成 VIP 漂移;未来可替换为 proxy / DNS 等其他实现,不影响控制面。
  • 执行形态:manager 以 mysql 用户通过 systemd 常驻,日志走 JSON 格式输出到 journal。所有 CLI 动作收敛到一个 mha 二进制的子命令。

2. 与传统 MHA 的对比

mha.yml 部署的是社区 Perl 版 MHA,mha_go.yml 部署的是 Go 重写版本。二者定位都是"一主多从 + 独立 manager 做故障切换",但在实现与使用边界上差异明显:

维度传统 MHA (mha.yml)MHA-Go (mha_go.yml)
实现语言PerlGo,单个静态二进制
OS 支持CentOS 7.5 / EL7CentOS 7/8RHEL 7/8Rocky 9BigCloud 7/8/21openEuler 20/22/24Anolis OS 8Kylin V10
MySQL 支持5.78.0 / 8.4
外部依赖Perl + MHA RPM + DBD::mysql无额外 RPM,单二进制下发
运行形态masterha_manager 前台 / nohup 后台systemd 常驻服务(mha-manager.service
运行用户通常 rootmysql 用户,最小权限
配置格式INI (app1.cnf)YAML (cluster.yaml)
日志文本日志JSON 结构化日志,直接进 journal
复制模式binlog 位置 / GTID 均可仅 GTID
故障检测manager 单点 pingmanager 主循环 + replica 侧 secondary_checks 二次确认
失败阈值ping_interval × 次数failure_thresholdreconfirm_timeout 解耦
丢失事务抢救SSH 到死主 copy binlog,拼 relay logGTID 原生恢复,salvage.policy 策略化
选主优先级candidate_master / no_master 标签candidate_priority 0–100,按 slave_ips 顺序递减
写入端点VIP 漂移脚本硬编码writer_endpoint 抽象层(none / vip,可扩展)
CLI多个 masterha_* 命令统一 mha <subcommand>check-repl / manager / switch / failover-plan / failover-execute / version
Dry-run--dry-run 一等支持
发布方式分发 RPM,独立版本随 dbbot 发版,与剧本同节奏

简单判断:新建集群优先 mha_go.yml;仅当你仍需要维护 MySQL 5.7 + CentOS 7.5 的历史拓扑时,才继续使用 mha.yml

3. 支持边界

  • 目标架构:一主多从 + 一个独立 MHA-Go Manager
  • 复制模式:必须为 GTID 复制,推荐 semi_sync 半同步
  • 适用版本:MySQL 8.0 / 8.4
  • OS 支持范围与 mysql_ansible 其他剧本一致,当前包含 CentOS 7/8RHEL 7/8Rocky 9BigCloud 7/8/21openEuler 20/22/24Anolis OS 8Kylin V10

4. 拓扑约定

mha_go.yml 复用 [dbbot_mysql] 主机组,通过 master_ip / slave_ips / manager_ip 三个变量区分节点角色:

  • master_ip:主库,接收写入;在 cluster.yaml 中登记为 db1,角色为 primary
  • slave_ips:从库列表,至少一台;在 cluster.yaml 中登记为 db2db3……角色为 replica
  • manager_ip:运行 mha-manager 进程的节点,必须在 slave_ips 之内,不能与 master_ip 相同。默认取 slave_ips 的最后一台。

cluster.yaml 中从库的 candidate_priorityslave_ips 顺序递减:第一个从库 100、第二个 90,依此类推,用于主库故障后选主。

5. inventory 示例

[dbbot_mysql]
192.168.199.131 ansible_user=root ansible_ssh_pass="'<your_ssh_password>'"
192.168.199.132 ansible_user=root ansible_ssh_pass="'<your_ssh_password>'"
192.168.199.133 ansible_user=root ansible_ssh_pass="'<your_ssh_password>'"

[all:vars]
ansible_python_interpreter=auto_silent

6. 关键变量

编辑 mysql_ansible/playbooks/vars/var_mha_go.yml

master_ip: 192.168.199.131
slave_ips:
  - 192.168.199.132
  - 192.168.199.133

# manager 默认放在 slave_ips 的最后一台,如需调整请显式填写
manager_ip: "{{ slave_ips[-1] }}"

# cluster.yaml 中的集群名,同时体现在 mha 日志和 systemd Unit 描述
mha_go_cluster_name: app1

# --- 写入端点(VIP 漂移脚本,可选) ---
# mha_go_writer_endpoint_enabled: true
# vip: 192.168.199.130
# vip_netmask: "32"
# net_work_interface: "ens33"

其他可覆盖的角色变量(一般保持默认):

变量默认值说明
mha_go_binary_dest/usr/local/bin/mhamanager 节点上 mha 二进制落地位置
mha_go_config_dir/etc/mhacluster.yaml 所在目录
mha_go_log_dir/var/log/mhamanager 日志目录
mha_go_service_enabledtrue是否 systemctl enable manager 服务
mha_go_writer_endpoint_enabledfalse是否启用 VIP 漂移(开启后需填 vip / vip_netmask / net_work_interface

7. 前置条件

make_mha_go 在执行前会做以下检查,任何一项不满足都会直接失败:

  • 每个节点的 datadir 下必须存在 master_slave_finish.flag,即 MySQL 已由 dbbot 安装并完成主从搭建。
  • manager 节点 /tmp/ssh_finish.flag 必须存在,即 make_ssh_passwordless 已跑过。
  • 所有节点 SELECT @@gtid_mode 必须为 ON

mha_go.yml 本身的 role 执行顺序已经按这个依赖编排:

pre_check_and_set → mysql_server → make_replication → make_ssh_passwordless → make_mha_go

也就是说你只需要直接跑 mha_go.yml,不需要提前手工跑其他剧本。

8. 执行入口

cd /usr/local/dbbot/mysql_ansible/playbooks
ansible-playbook mha_go.yml

非交互式执行(例如在 CI 或自动化流水线中)可以跳过 confirmation.ymlpause 提示:

ansible-playbook mha_go.yml -e dbbot_confirmation_input=confirm

9. 执行后产物

剧本执行成功后,manager 节点上会出现:

  • /usr/local/bin/mha:Go 版 MHA 静态二进制,mha version 返回 mha-go 0.x.y
  • /etc/mha/cluster.yaml:集群定义,角色 db1=primarydb2..=replica,复制模式 gtid,半同步策略 preferred
  • /etc/systemd/system/mha-manager.service:Type=simple 的 systemd 单元,以 {{ mysql_user }} 身份启动 mha manager --config /etc/mha/cluster.yaml --log-format json
  • /var/log/mha/:日志目录(JSON 格式输出,systemd 通常同时走 journal)。

所有节点(主、从、manager)的 datadir 都会多出 mha_go_finish.flag,供后续剧本识别。

10. 常用命令

全部运行在 manager 节点,mha 二进制即 /usr/local/bin/mha

# 查看版本
mha version

# 只做一次性配置与复制健康检查,不启动 manager
mha check-repl --config /etc/mha/cluster.yaml

# 打印故障转移计划(dry run,不执行)
mha failover-plan --config /etc/mha/cluster.yaml

# 指定候选节点执行 failover
mha failover-execute --config /etc/mha/cluster.yaml --candidate db2

# 主动切换到指定新主(受控切换)
mha switch --config /etc/mha/cluster.yaml --new-primary db2 --dry-run

服务状态与日志:

systemctl status mha-manager
journalctl -u mha-manager -f

11. 启用 VIP 写入端点

默认部署不启用 VIP,cluster.yamlwriter_endpoint.kindnone。如果需要对外暴露固定写入 VIP:

  1. vars/var_mha_go.yml 中打开:

    mha_go_writer_endpoint_enabled: true
    vip: 192.168.199.130
    vip_netmask: "32"
    net_work_interface: ens33
    
  2. 剧本会额外执行 edit_sudoer.ymldeploy_vip_script.yml,在所有 MySQL 节点为 mysql 用户添加 ip addr / arping 相关的 sudo 规则,并在 manager 节点落下 /usr/local/bin/mha_ip_failover.sh

  3. cluster.yaml 中的 writer_endpoint 会切换为:

    writer_endpoint:
      kind: vip
      target: <vip>
      command: /usr/local/bin/mha_ip_failover.sh
    
  4. vip / vip_netmask / net_work_interface 必须与实际网络一致,vip 必须是一个合法 IP,vip_netmask 取值范围 0–32

12. 注意事项

  • manager_ip 必须是 slave_ips 中的一员;validate_mha_go.yml 会在 inventory / vars 不一致、manager 指向主库时主动 fail。
  • slave_ips 中每个 IP 都必须同时在 inventory/hosts.ini 里出现,反过来也一样,不允许"inventory 有但 vars 没有"。
  • mha-manager.servicemysql 用户身份运行,cluster.yaml 权限 0640,属主必须是 mysql:mysql
  • 失败后重跑前,建议先确认:manager 节点 mha-manager 服务状态、cluster.yaml 是否被手工改过、各节点 GTID 与复制状态是否一致。
  • 离线环境需要先把 yum 依赖(python3-libselinuxncurses-compat-libsnumactllibaiotar)准备好,否则 pre_check_and_set 阶段会在 yum 刷元数据时失败。