基于 consul 架构的 MHA 自动切换

介绍

一直以来, 我们并未在线上启用 masterha_manager 自动切换脚本, 主要因为在网络抖动(网线, 所属机柜交换机不稳定)的情况下并不能保证数据库真的不能访问. 比如重启检测脚本所在机器的网卡并不能说明数据库出了问题, 所以从这方面看我们不能仅通过一个点的检测就判断数据库不可访问.

不过我们可以通过 consul(因为 consul 提供 dns 接口, 笔者更倾向于使用 consul, 而不是 etcd)集群的特性, 我们增加多点检测机制, 在 n 个集群的环境中, 有超过半数的检测点检测到数据库有问题, 我们就认为数据库不可访问, 这时则开始调用 masterha_manager 脚本进行切换, 如下图所示:

      <checkmysql>        <checkmysql>           <checkmysql>
            |                   |                     |
       +---------+          +---------+          +---------+
       | consul1 |          | consul2 |          | consul3 |
       +---------+          +---------+          +---------+
                  \             |               /
                   \            |              /
                    \           |             /
                     \          |            /
                     +----------------------+
                     |   http api && acl    |
                     +----------------------+
                                |
                                |
                     +----------------------+
                     | consul-template      | ----<mysqlxxx.tpl>  --->  <mysqlxxx.conf>
                     +----------------------+
                                                                                  |
                                                                      +--------------------------+  
                                                                      | masterha_manager_consul  |
                                                                      +--------------------------+

checkmysql 需要部署到每台 consul server 中, 这样我们就实现了多点检测 MySQL 是否正常, 如果正常, checkmysql 会设置一个值为 1 的键: mysql/mysqlxxxx/node-consul, 反之则值为 0, 其中 node-consul 的默认值为当前主机的 hostname.

checkmysql 检测完后, 我们使用 consul-template 工具根据模板文件 mysqlxxx.tpl 来监听所有 key 的变更, 如果有变化则生成配置 mysqlxxxx.conf, 进而调用 masterha_manager_consul 脚本开始进行切换.

我们在 masterha_manager_consul 脚本中重写了方法 MHA::HealthCheck::wait_until_unreachable, 避免了无限循环检测, 如果少于一半的检测点认为数据库异常, 则退出该轮的调用, 否则启用子进程开始执行切换操作.

备注:

masterha_manager_consul 是基于 MHA v0.5.6 修改的, 并且默认只在当天的21点到第二天的 9 点之间做自动切换, 可以通过 night 选项控制此功能. 另外多台 consul server 建议部署到不同的交换机或机柜中.

consul 更新机制见 raft

使用说明

代码见 mha_manager_consul

整体结构如下:

mha_manager_consul
├── bin
│   ├── checkmysql
│   └── masterha_manager_consul
├── conf
│   ├── db.cnf
│   └── template-config
├── consul
│   ├── acl
│   │   ├── policy.ano
│   │   └── policy.key
│   ├── conf
│   │   └── consul.conf
│   └── conf.d
│       └── server.json
├── README.md
└── template
    └── mysql3308.tpl

测试环境

继续使用以往的测试环境:

ip os hostname version
10.0.21.5 centos 6.5 cz-test1 consul 0.8v
10.0.21.7 centos 6.5 cz-test2 consul 0.8v
10.0.21.17 centos 6.5 cz-test3 consul 0.8v

下面所有的操作都假设已经安装好了 consul cluster.

备注

在运行 checkmysql 之前, 我们需要设置好 acl 策略, 以免 consul 的敏感信息被旁人访问. 下面命令中的 token 参数即是 consul 主配置文件中的 acl_master_token 选项, 文件 policy.ano 则是限制匿名用户访问 mysql/* 相关键的策略, policy.key 则是设置允许访问 mysql.* 相关键的权限, 这里生成的 token 则为 dcb5b583-cd36-d39d-2b31-558bebf86502, 大家可以访问 consul acl 了解更多访问控制的内容.

#curl -X PUT --data @policy.ano http://localhost:8500/v1/acl/create?token=e95597e0-4045-11e7-a9ef-b6ba84687927
{"ID":"anonymous"}

#curl -X PUT --data @policy.key http://localhost:8500/v1/acl/create?token=e95597e0-4045-11e7-a9ef-b6ba84687927
{"ID":"dcb5b583-cd36-d39d-2b31-558bebf86502"}

checkmysql

在每个 consul server 的节点上运行该脚本, 这里的 token 参数即为上述 acl 的结果, tag 则是 db.conf 配置里的实例, 通过以下命令启动:

perl checkmysql --conf db.cnf --verbose --tag mysql3308 --token dcb5b583-cd36-d39d-2b31-558bebf86502
[2017-06-08T10:09:14] mysql/mysql3308/cz-test2 with value 1 no change
[2017-06-08T10:09:15] mysql/mysql3308/cz-test2 with value 1 no change

cz-test2 表示当前的主机名是 cz-test2, 对应上述介绍的 node-consul.

备注: 如果你的 MySQL master 是通过 vip 提供服务, db.conf 配置里的 host 选项最好设置成 vip 的地址.

consul-template

在 checkmysql 更新 consul 的相关 key 之后, 如果有任意一个 checkmysql 变更了key 值, 则 consul-template 根据模板文件重新生成 mysqlxxx.conf 文件, 随后开始调用 masterha_manager_consul 脚本, consul-template 的配置详见 template-config; 通过以下命令启动:

# consul-template -config config 
2017/05/25 10:11:13 [DEBUG] (logging) enabling syslog on LOCAL5

mysqlxxxx.tpl 模板文件的内容如下:

# node3308

cz-test1:1
cz-test2:1
cz-test3:1

如果少于半数的监测点发现 MySQL 异常, consul-template 打印下面的消息:

[2017-06-08T10:24:15] status ok, skip switch..

反之则打印 error 信息, 并开始调用 masterha_manager_consul 脚本:

[2017-05-25T10:24:48] status error, need switch..
Wed May 24 10:24:48 2017 - [info] Reading default configuration from /etc/masterha/app_default.cnf..
...
...

conf.d/server.json

详见 template-config 配置中的 address = "consul.service.consul:8500" 选项; 在网络波动的情况下, address 选项如果只配置一个 consul server 的 ip 的话, consul-template 则不能连接到 consul server 中监控相应的 key 值, 尽管 consul-template 有重试功能, 但是在单 ip 的情况下, 难以确保可以正常获取相关的 key 值信息.

conf.d/server.json 配置则将各个 consul server 的 ip 作为一个 dns 条目, 如下所示:

# dig @10.0.21.5 consul.service.consul
......
......
;; QUESTION SECTION:
;consul.service.consul.     IN  A

;; ANSWER SECTION:
consul.service.consul.  0   IN  A   10.0.21.7
consul.service.consul.  0   IN  A   10.0.21.5
consul.service.consul.  0   IN  A   10.0.21.17

单个 consul server 异常, 会自动跳到正常的 consul-server 中.

主从切换测试

我们简单关闭 master 的实例, 看看各工具间的输出状态.

关闭 master

关闭 master 后, checkmysql 脚本开始更新状态, 在超过半数的情况下调用 masterha_manager_consul 脚本进行主从切换: checkmysql 脚本输出, 开始将 key 的值更为 0

[2017-06-08T18:16:43] mysql/mysql3308/cz-test2 with value 1 no change
DBI connect('mysql_read_default_file=./db.cnf;mysql_read_default_group=mysql3308','',...) failed: Can't connect to MySQL server on '10.0.21.7' (111) at checkmysql line 56
[2017-06-08T18:16:44] set 0 with key mysql/mysql3308/cz-test2 ok
DBI connect('mysql_read_default_file=./db.cnf;mysql_read_default_group=mysql3308','',...) failed: Can't connect to MySQL server on '10.0.21.7' (111) at checkmysql line 56
[2017-06-08T18:16:45] mysql/mysql3308/cz-test2 with value 0 no change

mysql3308.conf 配置文件变更为如下:

# node3308

cz-test1:0
cz-test2:0
cz-test3:0

consul-template 则显示如下:

# consul-template -config config 
2017/06/08 18:11:13 [DEBUG] (logging) enabling syslog on LOCAL5

[2017-05-24T18:16:48] status error, need switch.. # 脚本判定超过半数认为数据库不可访问
Wed Jun 08 18:16:48 2017 - [info] Reading default configuration from /etc/masterha/app_default.cnf..
Wed Jun 08 18:16:48 2017 - [info] Reading application default configuration from /etc/masterha/app_56.conf..
Wed Jun 08 18:16:48 2017 - [info] Updating application default configuration from /usr/bin/init_conf_loads..
Wed Jun 08 18:16:49 2017 - [info] Reading server configuration from /etc/masterha/app_56.conf..
Wed Jun 08 18:17:01 2017 - [info] Reading default configuration from /etc/masterha/app_default.cnf..
Wed Jun 08 18:17:01 2017 - [info] Reading application default configuration from /etc/masterha/app_56.conf..
Wed Jun 08 18:17:01 2017 - [info] Updating application default configuration from /usr/bin/init_conf_loads..
Wed Jun 08 18:17:01 2017 - [info] Reading server configuration from /etc/masterha/app_56.conf..

如果没有超过半数, consul-template 则显示以下:

[2017-06-08T18:24:15] status ok, skip switch..

MHA 切换日志

mha 切换的日志则包含以下信息, 日志文件则根据 mha 的具体配置而定:

Wed Jun 08 18:45:37 2017 - [info] Starting master failover..
Wed Jun 08 18:45:37 2017 - [info] 
From:
10.0.21.7(10.0.21.7:3308) (current master)
 +--10.0.21.17(10.0.21.17:3308)

To:
10.0.21.17(10.0.21.17:3308) (new master)
...
...
Master failover to 10.0.21.17(10.0.21.17:3308) completed successfully.
Wed Jun 08 18:45:41 2017 - [info] Sending mail..

总结

整体上而言, 使用 consul 的架构相对繁琐, 没有单节点那么简易方便, 不过对于比较核心的数据库来说, 一致性应该放到首位, 多点检测则很大程度上健壮了切换机制. 而且原工具自带的 masterha_manager 脚本本身只是循环检测, 超过三次错误(每次间隔时间递增)才会开始切换, 在网络波动, 交换机故障或数据库主机较繁忙的时候, 会引起一些意料之外的操作, 所以相对来说, 多点检测避免了这类不稳定的问题, 另外 consul cluster 部署完成后也可以用于其他需要一致性判断的业务, 不用太纠结于繁琐方面的考虑.