一、前言
最近在研究完ProxySQL之后发现,当后端主从服务器的Master宕机之后,这个复制架构就需要人工介入重新部署Master。此时想着有没有什么版本能让后端的从服务器自动成为Master来接替宕机的Master的功能。后来百度了一下,发现MHA能实现这种需求。MHA,即Master High Availability Manager and Tools for MySQL,是日本的一位MySQL专家采用Perl语言编写的一个脚本管理工具,该工具仅适用于MySQL Replication(二层)环境,目的在于维持Master主库的高可用性。从架构上来说,MHA分为如下两大部分:
Node:我们知道,MHA是基于MySQL Replication环境的,在该环境中,不管是Master角色,还是Slave角色,都称为Node,是被监控管理的对象节点。 Node服务器上需要安装MHA Node包。
Manager:为MHA架构中的管理者,建议部署在一台独立的服务器上,当然也可部署在某个Slave上,但该Slave永远不要被选择成为新的Master,否则故障切换后的MHA架构就失去了高可用性。 Manager服务器需要安装MHA Manager包,并完善一个主配置文件。 一个Manager可管理多套MySQL Replication环境。
MHA的manager工具包主要包括以下几个工具:
masterha_check_ssh 检查MHA的SSH配置状况
masterha_check_repl 检查MySQL复制状况
masterha_manger 启动MHA
masterha_check_status 检测当前MHA运行状态
masterha_master_monitor 检测master是否宕机
masterha_master_switch 控制故障转移(自动或者手动)
masterha_conf_host 添加或删除配置的server信息
本次实验我们采取一主两从的数据库复制架构,然后通过proxysql来代理主从服务,接着配置MHA来实现Master的高可用,其配置拓扑图如下:
二、配置数据库
此处我们配置数据库为半同步复制的主从架构。
1、构建master
首先配置master 的mariadb-server服务的配置文件:
[root@master ~]# vim /etc/my.cnf
skip-name-resolve=ON ##取消解析登录主机名
log-bin=master.log #启动master服务器的log-bin功能
innodb-file-per-table=ON
sync_binlog = 1 #默认,sync_binlog=0,表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失。
server_id=1 #mysql的同步的数据中是包含server-id的,用于标识该语句最初是从哪个server写入的,所以server-id一定要有的
然后启动mariadb服务并授权主从同步账号:
[root@master ~]# systemctl start mariadb
[root@master ~]# mysql
MariaDB [(none)]> GRANT REPLICATION SLAVE ON *.* TO 'slave'@'192.168.0.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
查看master服务器上对应的二进制日志名称及日志pos位置:
MariaDB [(none)]> show master status;
+---------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| master.000003 | 473 | | |
+---------------+----------+--------------+------------------+
1 row in set (0.00 sec)
接着安装半同步复制的插件(非必要,可不配置,此处我纯当练手):
MariaDB [(none)]> install plugin rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)
然后修改/etc/my.cnf文件,添加如下配置并重启mariadb服务启动半同步复制功能:
[root@master ~]# vim /etc/my.cnf
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=30000
[root@master ~]# systemctl restart mariadb
2、构建slave1和slave2
首先配置slave主机的配置文件:
skip-name-resolve=ON #取消解析登录主机名
innodb-file-per-table=ON
relay-log=relay.log #用于保存master服务器的binlog日志
read-only=ON #设置从库为只读模式,但仅对非SUPER权限的用户有效
server_id=2 #slave1和slave2的server_id需修改为不一样,如2,3;
接着在slave主机上启动mariadb服务,并执行change master语句:
[root@slave2 ~]# systemctl start mariadb
MariaDB [(none)]> CHANGE MASTER TO MASTER_HOST='192.168.0.82',MASTER_USER='slave',MASTER_PASSWORD='123456',MASTER_LOG_FILE='master.000003',MASTER_LOG_POS=473;
Query OK, 0 rows affected (0.06 sec)
MariaDB [(none)]> start slave;
然后安装slave的半同步插件(非必要,可不配置,此处我纯当练手):
MariaDB [(none)]> install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.00 sec)
最后修改/etc/my.cnf配置文件,添加下述配置并重启mariadb服务:
[root@slave2 ~]# vim /etc/my.cnf
rpl_semi_sync_slave_enabled=1
[root@slave2 ~]# systemctl restart mariadb
slave1 和slave2的配置雷同,可参考上述模板进行配置。
至此数据库相关的配置就配置完了。
三、配置ProxySQL
在proxysql主机上安装proxysql服务:
[root@proxysql ~]# yum install -y https://www.percona.com/downloads/proxysql/proxysql-1.4.8/binary/redhat/7/x86_64/proxysql-1.4.8-1.1.el7.x86_64.rpm
[root@proxysql ~]# systemctl start proxysql
在配置proxySQL服务之前,首先在后端数据库中添加相应的proxysql授权账号:
MariaDB [(none)]> grant all on *.* to 'proxysql'@'192.168.0.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
接着在proxysql主机的本地连接proxysql的管理接口,然后配置proxysql服务:
[root@proxysql ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032
#添加proxysql用于访问后端数据库
MySQL [(none)]> insert into mysql_users(username,password,default_hostgroup,transaction_persistent) values('proxysql','123456',0,1);
Query OK, 1 row affected (0.00 sec)
#添加做健康检测的账号
MySQL [(none)]> set mysql-monitor_username='proxysql';
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> set mysql-monitor_password='123456';
Query OK, 1 row affected (0.00 sec)
#添加后端数据库主机,hostgroup_id 0表示写组,1表示读组
MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(0,'192.168.0.82',3306,1,'Write Group');
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(1,'192.168.0.87',3306,1,'Read Group');
Query OK, 1 row affected (0.00 sec)
#将insert这样的修改语句路由到0组(写组)
MySQL [(none)]> insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(1,'192.168.0.89',3306,1,'Read Group');
Query OK, 1 row affected (0.00 sec)
#将select语句全部路由至hostgroup_id=1的组(也就是读组)
MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(1,1,'^INSERT.*',0,0);
Query OK, 1 row affected (0.00 sec)
#将select语句全部路由至hostgroup_id=1的组(也就是读组)
MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(2,1,'^SELECT',1,0);
Query OK, 1 row affected (0.00 sec)
#将show语句全部路由至hostgroup_id=1的组(也就是读组)
MySQL [(none)]> insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(3,1,'^SHOW',1,0);
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> select rule_id,active,match_digest,destination_hostgroup,apply from mysql_query_rules;
+---------+--------+--------------+-----------------------+-------+
| rule_id | active | match_digest | destination_hostgroup | apply |
+---------+--------+--------------+-----------------------+-------+
| 1 | 1 | ^INSERT.* | 0 | 0 |
| 2 | 1 | ^SELECT | 1 | 0 |
| 3 | 1 | ^SHOW | 1 | 0 |
+---------+--------+--------------+-----------------------+-------+
3 rows in set (0.00 sec)
#添加mysql_replication_groups,此配置用于与MHA结合实现高可用
MySQL [(none)]> insert into mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment)values(0,1,'test with proxysql and MHA');
Query OK, 1 row affected (0.00 sec)
MySQL [(none)]> select * from runtime_mysql_replication_hostgroups;
+------------------+------------------+----------------------------+
| writer_hostgroup | reader_hostgroup | comment |
+------------------+------------------+----------------------------+
| 0 | 1 | test with proxysql and MHA |
+------------------+------------------+----------------------------+
1 row in set (0.01 sec)
#加载刚才添加的配置
MySQL [(none)]> load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)
MySQL [(none)]> load mysql servers to runtime;
Query OK, 0 rows affected (0.01 sec)
MySQL [(none)]> load mysql variables to runtime;
Query OK, 0 rows affected (0.00 sec)
MySQL [(none)]> load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)
MySQL [(none)]> load mysql query rules to runtime;
Query OK, 0 rows affected (0.00 sec)
#保存配置至db文件中
MySQL [(none)]> save mysql users to disk;
Query OK, 0 rows affected (0.01 sec)
MySQL [(none)]> save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)
MySQL [(none)]> save mysql query rules to disk;
Query OK, 0 rows affected (0.02 sec)
MySQL [(none)]> save mysql variables to disk;
Query OK, 94 rows affected (0.01 sec)
MySQL [(none)]> save admin variables to disk;
Query OK, 31 rows affected (0.00 sec)
配置完成后,应能够通过192.168.0.81的6033端口访问后端的数据库服务,如:
[root@proxysql ~]# mysql -uproxysql -p123456 -h192.168.0.81 -P6033
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| hellodb |
| mysql |
| performance_schema |
| test |
+--------------------+
5 rows in set (0.00 sec)
MySQL [(none)]>
MySQL [(none)]> select * from hellodb.students;
+-------+---------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+---------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 22 | M | 1 | 7 |
| 3 | Xie Yanke | 53 | M | 2 | 16 |
| 4 | Ding Dian | 32 | M | 4 | 4 |
| 5 | Yu Yutong | 26 | M | 3 | 1 |
| 6 | Shi Qing | 46 | M | 5 | NULL |
| 7 | Xi Ren | 19 | F | 3 | NULL |
| 8 | Lin Daiyu | 17 | F | 7 | NULL |
| 9 | Ren Yingying | 20 | F | 6 | NULL |
| 10 | Yue Lingshan | 19 | F | 3 | NULL |
| 11 | Yuan Chengzhi | 23 | M | 6 | NULL |
| 12 | Wen Qingqing | 19 | F | 1 | NULL |
| 13 | Tian Boguang | 33 | M | 2 | NULL |
| 14 | Lu Wushuang | 17 | F | 3 | NULL |
| 15 | Duan Yu | 19 | M | 4 | NULL |
| 16 | Xu Zhu | 21 | M | 1 | NULL |
| 17 | Lin Chong | 25 | M | 4 | NULL |
| 18 | Hua Rong | 23 | M | 7 | NULL |
| 19 | Xue Baochai | 18 | F | 6 | NULL |
| 20 | Diao Chan | 19 | F | 7 | NULL |
| 21 | Huang Yueying | 22 | F | 6 | NULL |
| 22 | Xiao Qiao | 20 | F | 1 | NULL |
| 23 | Ma Chao | 23 | M | 4 | NULL |
| 24 | Xu Xian | 27 | M | NULL | NULL |
| 25 | Sun Dasheng | 100 | M | NULL | NULL |
+-------+---------------+-----+--------+---------+-----------+
25 rows in set (0.01 sec)
MySQL [(none)]> insert into hellodb.students values (26,'Jin Yong',75,'M',2,3);
Query OK, 1 row affected (0.01 sec)
此时在proxysql的管理接口中,通过下述命令可以查看proxysql的工作情况:
[root@proxysql ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032
MySQL [(none)]> select * from stats_mysql_query_digest;
+-----------+--------------------+----------+--------------------+---------------------------------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname | username | digest | digest_text | count_star | first_seen | last_seen | sum_time | min_time | max_time |
+-----------+--------------------+----------+--------------------+---------------------------------------------------+------------+------------+------------+----------+----------+----------+
| 0 | information_schema | proxysql | 0xF705DF6D769FEE0D | insert into hellodb.students values (?,?,?,?,?,?) | 1 | 1530285228 | 1530285228 | 6063 | 6063 | 6063 |
| 0 | information_schema | proxysql | 0x65E26382DB42BC29 | insert into hellodb.students values (?,?,?,M,?,?) | 1 | 1530285224 | 1530285224 | 4176 | 4176 | 4176 |
| 1 | information_schema | proxysql | 0x234E67CE83C9D8AD | select * from hellodb.students | 1 | 1530285184 | 1530285184 | 5992 | 5992 | 5992 |
| 1 | information_schema | proxysql | 0x02033E45904D3DF0 | show databases | 1 | 1530285175 | 1530285175 | 3938 | 3938 | 3938 |
+-----------+--------------------+----------+--------------------+---------------------------------------------------+------------+------------+------------+----------+----------+----------+
5 rows in set (0.00 sec)
相关的语句均都proxysql代理到指定的hostgroup中,说明proxysql工作正常。
四、配置MHA
在配置完成proxysql+mysql半同步主从后,最后我们来配置MHA提供Mysql的高可用。因此MHA架构分为Manger和node,因此我需要在各监控的节点上安装mha4mysql-node,然后在MHA节点上安装mha4mysql-node和mha4mysql-manager。
1、安装mha4mysql-node
在各数据库节点及MHA主机上安装mha4mysql-node的rpm包:
[root@slave2 ~]# yum install -y mha4mysql-node-0.56-0.el6.noarch.rpm
2、安装mha4mysql-manager
另外还需要在MHA管理节点上安装mha4mysql-manager:
#部分依赖包需要安装epel-release来提供
[root@MHA ~]# yum install -y mha4mysql-manager-0.56-0.el6.noarch.rpm
3、修改各数据库节点的配置文件并授权MHA管理账号
在两个slave节点上编辑/etc/my.cnf,添加下述配置后重启mariadb服务:
[root@slave2 ~]# vim /etc/my.cnf
relay_log_purge=0 #关闭中继日志自动修剪功能,因为中继日志默认会自动修剪,此时为了以后各从服务器之间的执行过的操作对比
log-bin=master.log #因为slave有可能会变成master ,因此需要开启binlog日志
[root@slave2 ~]# systemctl restart mariadb
在Master节点的/etc/my.cnf文件上,添加下述配置并重启mariadb服务:
[root@master ~]# vim /etc/my.cnf
relay-log=relay-bin #因为master有可能会变成slave,所以需要开启中继日志
[root@master ~]# systemctl restart mariadb
授权MHA管理账号:
MariaDB [(none)]> GRANT ALL ON *.* TO 'mhauser'@'192.168.0.%' IDENTIFIED BY '123456';
Query OK, 0 rows affected (0.01 sec)
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
4、配置MHA各节点之间的SSH免密钥登录
在MHA管理节点上输入下述命令:
[root@MHA ~]# ssh-keygen -t rsa -P ""
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:NwX89jz5FtOyaeeQe9Ik1nfwWDTBFE7Uf45DjdCPxgM root@MHA
The key's randomart image is:
+---[RSA 2048]----+
| .. .+*+|
| ..E +oo|
| ..+ Bo|
| .o O *|
| S o. = @o|
| . . %+O|
| .o@=|
| =o*|
| ..*.|
+----[SHA256]-----+
[root@MHA ~]# cat .ssh/id_rsa.pub >> .ssh/authorized_keys
[root@MHA ~]# chmod go= .ssh/authorized_keys
[root@MHA ~]# scp -p .ssh/id_rsa .ssh/authorized_keys root@192.168.0.82:/root/.ssh/
[root@MHA ~]# scp -p .ssh/id_rsa .ssh/authorized_keys root@192.168.0.87:/root/.ssh/
[root@MHA ~]# scp -p .ssh/id_rsa .ssh/authorized_keys root@192.168.0.89:/root/.ssh/
#注意:请事先确保各节点上/root/.ssh目录存在
5、配置MHA
首先创建MHA的配置文件:
[root@MHA ~]# vim /etc/masterha/app.cnf
[server default]
user=mhauser # MySQL 管理员,用来管理各个节点
password=123456
manager_workdir=/data/masterha/app1 #这个目录/data/masterha/app1不需要创建,不存在时会自动创建
manager_log=/data/masterha/app1/manager.log
remote_workdir=/data/masterha/app1 #指各个node节点的工作目录,自动创建
ssh_user=root
ssh_port=22
repl_user=slave #此处使用此前做mysql 主从时的repl账号,注意需确保此账号在master和slave中都存在
repl_password=123456
ping_interval=1 #多长时间检测一次
[server1]
hostname=192.168.0.82
candidate_master=1
[server2]
hostname=192.168.0.87
candidate_master=1 #将来是否可以成为主节点,如果不定义,就不能成为候选的主节点
[server3]
hostname=192.168.0.89
candidate_master=1
#no_master=1 #如果定义no_master就不能成为主节点
创建配置完配置文件后,可以使用命令masterha_check_ssh来检查各节点的ssh连通性:
[root@MHA ~]# masterha_check_ssh --conf=/etc/masterha/app.cnf
.....
MySQL Replication Health is OK.
#若最后一行出现上述信息,说明检查成功;如有报错,请根据报错进行排查。
最后启动MHA,需注意的是,masterha_manager没有启动程序,需手动启动服务:
[root@MHA ~]# nohup masterha_manager --conf=/etc/masterha/app.cnf > /data/masterha/app1/manager.log 2>& 1 &
[1] 11941
[root@MHA ~]# ps aux | grep masterha_manager | grep -v grep
root 11941 3.1 2.1 297132 21592 pts/1 S 13:13 0:00 perl /usr/bin/masterha_manager --conf=/etc/masterha/app.cnf
启动成功后,可通过如下命令来查看 master 节点的状态。
[root@MHA ~]# masterha_check_status --conf=/etc/masterha/app.cnf
app (pid:11429) is running(0:PING_OK), master:192.168.0.82
上述信息表示MHA服务运行正常,否则会显示为类似于“app is stopped”类似的字眼。
若需要停止MHA,需要使用masterha_stop命令:
[root@MHA ~]# masterha_stop --conf=/etc/masterha/app.cnf
Stopped app successfully.
[1]+ Exit 1 nohup masterha_manager --conf=/etc/masterha/app.cnf > /data/masterha/app1/mnager.log 2>&1
6、测试故障转移
在master 节点关闭mariadb服务:
[root@master ~]# systemctl stop mariadb
在MHA管理节点上如果能在/data/masterha/app1/manager.log日志文件中查看到如下信息,说明manager检查到192.168.0.82节点故障,并自动执行故障转移了,将192.168.0.87提升为主节点:
----- Failover Report -----
app: MySQL Master failover 192.168.0.82(192.168.0.82:3306) to 192.168.0.87(192.168.0.87:3306) succeeded
Master 192.168.0.82(192.168.0.82:3306) is down!
Check MHA Manager logs at MHA:/data/masterha/app1/manager.log for details.
Started automated(non-interactive) failover.
The latest slave 192.168.0.87(192.168.0.87:3306) has all relay logs for recovery.
Selected 192.168.0.87(192.168.0.87:3306) as a new master.
192.168.0.87(192.168.0.87:3306): OK: Applying all logs succeeded.
192.168.0.89(192.168.0.89:3306): This host has the latest relay log events.
Generating relay diff files from the latest slave succeeded.
192.168.0.89(192.168.0.89:3306): OK: Applying all logs succeeded. Slave started, replicating from 192.168.0.87(192.168.0.87:3306)
192.168.0.87(192.168.0.87:3306): Resetting slave info succeeded.
Master failover to 192.168.0.87(192.168.0.87:3306) completed successfully.
此时登录Proxysql的管理接口查看相应的servers状态:
MySQL [(none)]> select * from mysql_servers;
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| 0 | 192.168.0.87 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Read Group |
| 1 | 192.168.0.82 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Write Group |
| 1 | 192.168.0.89 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Read Group |
| 1 | 192.168.0.87 | 3306 | ONLINE | 1 | 0 | 1000 | 0 | 0 | 0 | Read Group |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
4 rows in set (0.00 sec)
从上述信息发现,192.168.0.87被提升为hostgroup 0(写组),而192.168.0.82则变成hostgroup 1(读组)。这是因为proxysql的mysql_replication_hostgroups表配置的原因,mysql_replication_hostgroups 表的主要作用是监视指定主机组中所有服务器的read_only值,并且根据read_only的值将服务器分配给写入器或读取器主机组,从而定义 hostgroup 的主从关系。ProxySQL monitor 模块会监控后端所有servers 的 read_only 变量,如果发现从库的 read_only 变为0、主库变为1,则认为角色互换了,自动改写 mysql_servers 表里面 hostgroup 的数值,达到自动 Failover 效果。因此当MHA把后端的Master更改为192.168.0.87的时候,ProxySQL也能够将相应的192.168.0.87调整为写组。
结尾
最后配置完做冗余切换的时候发现,mysql主从的半同步在MHA高可用切换后就失效了(因为master变更了),新的master上也没有启动相关的master插件,那估计还得重启新master的mariadb服务才能重做,这时候相当于数据库要停机了,后面我再研究下看看是否有其他解决办法。