MHA高可用架构解决方案
欢迎来到 来到大浪涛天的博客 !
一、MHA高可用架构解决方案
1. MHA的介绍
- MHA(Master High Availability)目前在 MySQL 高可用方面是一个相对成熟的解决方案,它由日本 DeNA 公司的 youshimaton(现就职于 Facebook 公司)开发,是一套优秀的作为 MySQL 高可用性环境下故障切换和主从提升的高可用软件。
- 在 MySQL 故障切换过程中,MHA 能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA 能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
- 该软件由两部分组成:MHA Manager(管理节点)和 MHA Node(数据节点)。MHA Manager 可以单独部署在一台独立的机器上管理多个 master-slave 集群,也可以部署在一台 slave 节点上。MHA Node 运行在每台 MySQL 服务器上,MHA Manager 会定时探测集群中的 master 节点,当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master,然后将所有其他的 slave 重新指向新的 master。整个故障转移过程对应用程序完全透明。
- 在 MHA 自动故障切换过程中,MHA 试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA 没法保存二进制日志,只进行故障转移而丢失了最新的数据,可以使用数据补偿模式,增加一个slave不用于切换只是用于拉取二进制日志。如果只有一个 slave 已经收到了最新的二进制日志,MHA 可以将最新的二进制日志应用于其他所有的 slave 服务器上,因此可以保证所有节点的数据一致性。
- 目前 MHA 主要支持一主多从的架构,要搭建 MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当 master,一台充当备用 master,另外一台充当从库,因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。
2. MHA集群结构说明
MHA一般至少是需要3台数据库服务器,而且额外还需要一台服务器来做为Manager端。如下图所示,一般的生产应用中,Manager是用来管理多个应用数据库集群的,它只是用来监测每个应用的master端是否故障,如果故障自动进行切换,因此资源占用并不高。
2-1. 服务角色
2-1-1. MHA Manager:
通常单独部署在一台独立机器上管理多个 master/slave 集群(组),每个 master/slave 集群称作一个 application,用来管理统筹整个集群。
2-1-2. MHA node:
主要是接收管理节点所发出指令的代理,代理需要运行在每一个 mysql 节点上。简单讲 node 就是用来收集从节点服务器上所生成的 bin-log 。对比打算提升为新的主节点之上的从节点的是否拥有并完成操作,如果没有发给新主节点在本地应用后提升为主节点。每个复制组内部和 Manager 之间都需要ssh实现无密码互连,只有这样,在 Master 出故障时, Manager 才能顺利的连接进去,实现主从切换功能。
2-2. MHA提供的工具脚本
MHA会提供诸多工具程序, 其常见的如下所示:
Manager节点:mha4mysql-manager-0.56-0.el6.noarch.rpm
masterha_check_ssh:MHA 依赖的 ssh 环境监测工具;
masterha_check_repl:MYSQL 复制环境检测工具;
masterga_manager:MHA 服务主程序;
masterha_check_status:MHA 运行状态探测工具;
masterha_master_monitor:MYSQL master 节点可用性监测工具;
masterha_master_swith:master:节点切换工具;
masterha_conf_host:添加或删除配置的节点;
masterha_stop:关闭 MHA 服务的工具。
Node节点:(这些工具通常由MHA Manager的脚本触发,无需人为操作)
mha4mysql-node-0.56-0.el6.noarch.rpm
save_binary_logs:保存和复制 master 的二进制日志;
apply_diff_relay_logs:识别差异的中继日志事件并应用于其他 slave;
purge_relay_logs:清除中继日志(不会阻塞 SQL 线程);
自定义扩展:
secondary_check_script:通过多条网络路由检测master的可用性;
master_ip_failover_script:更新application使用的masterip;
report_script:发送报告;
init_conf_load_script:加载初始配置参数;
master_ip_online_change_script;更新master节点ip地址。
2-3. MHA工作原理
2-3-1. MHA Failover实现方式
(1) MHA通过masterha_manger脚本启动MHA的功能.
(2) 在manager启动之前,会自动检查ssh互信(masterha_check_ssh)和主从状态(masterha_check_repl)
(3) MHA-manager 通过 masterha_master_monitor脚本(每隔ping_interval秒)
(4) masterha_master_monitor探测主库3次无心跳之后,就认为主库宕机了.
(5) 进行选主过程
算法一:
读取配置文件中是否有强制选主的参数?
candidate_master=1
check_repl_delay=0
算法二:
自动判断所有从库的日志量.将最接近主库数据的从库作为新主.
算法三:
按照配置文件先后顺序的进行选新主.
2-4. candidate_master=1 应用场景
(1) MHA+KeepAlive VIP(早期MHA架构)
(2) 多地多中心
2-5. 数据补偿
判断主库SSH的连通性
情况一: SSH能连
调用 save_binary_logs脚本,立即保存缺失部分的binlog到各个从节点,恢复
情况二: SSH无法连接
调用 apply_diff_relay_logs 脚本,计算从库的relaylog的差异,恢复到2号从库
2-6. MHA 应用透明(vip)
1. MHA会根据配置文件调用master_ip_failover.txt脚本,实现VIP的浮动。
2. 在 /etc/mha/app01.cnf中添加该脚本
master_ip_failover_script=/usr/local/bin/master_ip_failover
3. 第一次启动需要自己手动添加vip
[root@db01 ~]# ifconfig eth0:1 10.211.55.85/24
4. 业务采用VIP,当主库down后,VIP会自己切换到选的下一个master上,这样对业务来说是透明的,因为ip都一样,业务不会知道已经切换到了另外一个库上。
2-7. MHA 故障提醒
1. MHA会根据配置文件调用/usr/local/bin/send脚本,实现有故障会主动发邮件通知。
2. 在 /etc/mha/app01.cnf中添加该脚本
report_script=/usr/local/bin/send
2-8. 设置binlog server
1. 为了最大减少数据丢失,我们可以选取一台服务器专门负责拉取主库二进制日志,而且该binlog server不进行选主,只是为了拉去binlog
2. 在配置文件中添加binlog设置
/etc/mha/app1.cnf
[binlog1]
no_master=1
hostname=10.0.0.53
master_binlog_dir=/data/mysql/binlog
3. 创建该目录,并赋予对应的权限
mkdir -p /data/mysql/binlog
chown -R mysql.mysql /data/*
4. 拉取二进制日志
cd /data/mysql/binlog -----》必须进入到自己创建好的目录
mysqlbinlog -R --host=10.0.0.51 --user=mha --password=mha --raw --stop-never mysql-bin.000001 &
注意:
拉取日志的起点,需要按照目前主库正在使用的binlog为起点.
5. 修改完配置文件都需要进行重启mha服务
[root@db03 bin]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 bin]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
3. MHA搭建配置
3-1. MHA搭建环境
为了节省虚拟机,因此规划3台虚拟机来搭建,分别为DB01和DB02及DB03,其中DB01为master,因为master压力较大,因此选DB03来安装mha manger,防止mha轻易挂掉,当然正常生产环境MHA管理节点应该是独立的一个主机,如图:
3-1-1. MHA搭建具体实施步骤
3-1-1-1. 配置关键程序软连接
ln -s /application/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /application/mysql/bin/mysql /usr/bin/mysql
3-1-1-2. 配置互信
#创建密钥对 如果没有该命令 yum -y install openssh-clients 安装
[root@mysql-db03 ~]# ssh-keygen -t dsa -P "" -f ~/.ssh/id_dsa >/dev/null 2>&1
#发送mysql-db03公钥,包括自己
[root@mysql-db03 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.81
[root@mysql-db03 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.82
[root@mysql-db03 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.83
#发送mysql-db02公钥,包括自己
[root@mysql-db02 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.81
[root@mysql-db02 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.82
[root@mysql-db02 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.83
#发送mysql-db01公钥,包括自己
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.81
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.82
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@10.211.55.83
3-1-1-3. 检查互信是否成功
db01:
ssh 10.211.55.81 hostname
ssh 10.211.55.82 hostname
ssh 10.211.55.83 hostname
db02:
ssh 10.211.55.81 hostname
ssh 10.211.55.82 hostname
ssh 10.211.55.83 hostname
db03:
ssh 10.211.55.81 hostname
ssh 10.211.55.82 hostname
ssh 10.211.55.83 hostname
3-1-1-4. 安装软件包(所有节点)
yum install perl-DBD-MySQL -y
rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
3-1-1-5. 基于GTID配置主从同步:
- 准备环境
pkill mysqld
m -rf /data/mysql/data/*
m -rf /data/binlog/*
- 准备配置文件
主库db01:
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql/
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=81
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db01 [\d]>
EOF
slave1(db02):
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=82
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db02 [\d]>
EOF
slave2(db03):
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=83
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=db03 [\d]>
EOF
- 创建用户
useradd -s /sbin/nologin mysql
- 创建必须的目录
mkdir -p /data/binlog/
mkdir -p /data/mysql/data
chown -R mysql.mysql /data
- 初始化数据
mysqld --initialize-insecure --user=mysql --basedir=/application/mysql --datadir=/data/mysql/data
- 启动路径systemctl启动
cat >/etc/systemd/system/mysqld.service <<EOF
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE = 5000
EOF
- 复制启动脚本 /etc/init.d/mysqld start启动
cp /application/mysql/support-files/mysql.server /etc/init.d/mysqld
修改一下
basedir=/application/mysql
datadir=/data/mysql/data
- 启动数据库
/etc/init.d/mysqld start
- 构建主从:
master:81
slave:82,83
81:
grant replication slave on *.* to repl@'10.211.55.%' identified by 'admin123';
8283:
change master to
master_host='10.211.55.81',
master_user='repl',
master_password='admin123' ,
MASTER_AUTO_POSITION=1;
start slave;
3-1-1-6. 在db01主库中创建mha需要的用户
grant all privileges on *.* to mha@'10.211.55.%' identified by 'mha';
3-1-1-7. Manager软件安装(db03)
yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm
3-1-1-7-1. 配置文件准备(db03)
创建配置文件目录
mkdir -p /etc/mha
创建日志目录
mkdir -p /var/log/mha/app1
编辑mha配置文件
cat > /etc/mha/app1.cnf <<EOF
[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/binlog
user=mha
password=mha
ping_interval=2
repl_password=admin123
repl_user=repl
ssh_user=root
[server1]
hostname=10.211.55.81
port=3306
[server2]
hostname=10.211.55.82
port=3306
[server3]
hostname=10.211.55.83
port=3306
EOF
3-1-1-8. MHA 应用透明(vip)-db03
cp /root/master_ip_failover.txt /usr/local/bin/master_ip_failover
vim /usr/local/bin/master_ip_failover
my $vip = '10.211.55.85/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down";
[root@db03 bin]# chmod +x /usr/local/bin/master_ip_failover
[root@db03 bin]# yum install -y dos2unix
[root@db03 bin]# dos2unix /usr/local/bin/master_ip_failover
[root@db03 bin]# vim /etc/mha/app1.cnf
master_ip_failover_script=/usr/local/bin/master_ip_failover
db01:手工添加vip
[root@db01 ~]# ifconfig eth0:1 10.211.55.85/24
3-1-1-9. MHA 故障提醒
[root@db03 ~]# cp -a email/* /usr/local/bin/
[root@db03 ~]# cd /usr/local/bin/
[root@db03 ]# chmod +x *
[root@db03 bin]# vim /etc/mha/app1.cnf
report_script=/usr/local/bin/send
3-1-1-10. 额外的数据补偿(binlog_server)
找一台额外的机器,必须要有5.6以上的版本,支持gtid并开启,我们直接用的第二个slave(db03)
vim /etc/mha/app1.cnf
[binlog1]
no_master=1
hostname=10.211.55.83
master_binlog_dir=/data/mysql/binlog
(2) 创建必要目录
mkdir -p /data/mysql/binlog
chown -R mysql.mysql /data/*
(3) 拉取主库binlog日志
cd /data/mysql/binlog -----》必须进入到自己创建好的目录
mysqlbinlog -R --host=10.211.55.81 --user=mha --password=mha --raw --stop-never mysql-bin.000002 &
注意:
拉取日志的起点,需要按照目前主库正在使用的binlog为起点.
3-1-1-11. 状态检查及开启MHA(db03)
masterha_check_ssh --conf=/etc/mha/app1.cnf
masterha_check_repl --conf=/etc/mha/app1.cnf
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
3-1-1-12. 查看MHA状态
[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:10074) is running(0:PING_OK), master:10.211.55.81
4. MHA故障模拟及恢复处理
4-1. 宕掉 db01 数据库
[root@db01 ~]# /etc/init.d/mysqld stop
过了5秒后查看db02数据库看看vip是否已经飘过来了
[root@db02 ~]# ifconfig -a |grep -A3 eth0:1
eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.211.55.85 netmask 255.255.255.0 broadcast 10.211.55.255
ether 00:1c:42:06:3d:8f txqueuelen 1000 (Ethernet)
4-2. 恢复故障
4-2-1. 启动故障节点
[root@db01 ~]# /etc/init.d/mysqld start
4-2-2. 恢复1主2从(db01)
在mha的管理日志中可以看到change master to的指令,直接把MASTER_PASSWORD密码改掉就可以直接用了
[root@db03 bin]# grep "CHANGE MASTER TO" /var/log/mha/app1/manager
[info] All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='10.0.0.52', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='xxx';
db01执行完change master to后启动slave
[root@db03 bin]#
db01 [(none)]>CHANGE MASTER TO MASTER_HOST='10.0.0.52', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';
db01 [(none)]>start slave;
4-2-3. 恢复配置文件(db03)
db03中恢复db01和binlog的配置文件
[server1]
hostname=10.0.0.51
port=3306
[server2]
hostname=10.0.0.52
port=3306
[server3]
hostname=10.0.0.53
port=3306
[binlog1]
no_master=1
hostname=10.211.55.83
master_binlog_dir=/data/mysql/binlog
恢复binlogserver
cd /data/mysql/binlog
rm -rf /data/mysql/binlog/*
mysqlbinlog -R --host=10.0.0.52 --user=mha --password=mha --raw --stop-never mysql-bin.000001 &
4-2-4. 启动MHA
[root@db03 bin]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
[1] 16543
[root@db03 bin]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:16543) is running(0:PING_OK), master:10.0.0.52
5. 总结
- MHA 是一次性的,当MHA自动恢复主库成功后,MHA的监控自动会断开,需要我们把环境重新整理好,比如修复之前down掉的master,修复binlogserver,修复好配置文件,再重新启动MHA服务 ,虽然麻烦,但是它能让业务透明的情况下0-30秒内切换自动切换主库,让全年可靠性达到99.9%,还是非常理想的。
- MHA是单活的一个集群系统,意味着只能让可靠性增强,而不能实现proxy这种均分压力的能力,因此一般配置MHA的情况下,如果压力繁忙,还会让MHA的环境搭建中间件(atlas,mycat等),完成读写分离或者分库,分表,来分担主库的压力。