原本想记录整理记录一下具体的如何配置,但是写了写,都感觉不是很顺手,想了想,可能议论文不太适合我,还是写一篇记(流)叙(水)文(帐)吧,想到什么写什么可能写起来顺手一些。。。
前情提要
客户的公司有两个厂区,电信专线构成内网。希望系统和数据在两个地方都保存,其中一个地方停电了,不至于影响到全公司都无法生产。
网上找了几个MySQL 高可用的方案,选择几个简单实用的先来尝试一下。
- MySQL + Keepalived
- MHA
选型
客户是传统制造行业,并不想上云,而只有两地两台主机。
一开始尝试MySQL + Keepalived 方案,应该是可行的,但是配置方面还是得多看看博客和文档,脚本、配置文件什么的都得自己来,相对于MHA 来说没有那么智能,加上水平不够,了解到一些keepalived 的问题(比如裂脑),而MHA 相对来说配置就比较轻松,反正配置完成之后,只要交给manager 去管理就可以了。
在网上看了一圈,总结一下,最终要实现的效果应该如下:
两台VPS,分布两地,可以通过内网联通,并且两台主机分别运行两个MySQL 实例,平时,只有一台MySQL 作为master(参与Web 程序的读写交互),另一台主机的MySQL 则作为master 的备份来使用(不参与读写)。当主MySQL 挂了的时候,VIP 切换到从MySQL ,从机提升为主机提供数据的读写。等到原主机恢复后,原主机作为从机,从现在的主机获取binlog 日志,并开始备份。
由于客户只有两台主机,且主要目的是为了解决两地厂区其中一方停机(停电、维护等),不影响另一方生产的问题。因此不能使用MHA,毕竟MHA 的manager 也是要占用一台主机的,并且还需要保证MHA 的maneger 主机不死,那就不适合了。
因此,最终就选择keepalived。
MySQL 双主配置
MySQL 主从配置可以参考官方文档。
文档中介绍了如何使用二进制日志文件(Binary Log File)实现MySQL 实例主从复制的方法。
具体操作如下:
-
修改MySQL 配置文件
my.cnf
。[mysqld] # 开启MySQL 的bin-log 功能,由于是主主复制,因此两个实例都需要开启,如果只是做主备复制,则最少只需要主库开启即可 log_bin=mysql-bin-1 # 每一台MySQL 实例的server id 必须不相同 server-id=1 # 如果是innodb 引擎,则还需要增加一下配置 innodb_flush_log_at_trx_commit=1 sync_binlog=1
-
配置一个用户,用来允许两个MySQL 实例间的互相访问。安全性要求不高,这里忽略,之后就用root。
-
如果原本的数据库中有数据,则先使用mysqldump 工具,复制数据到从库。否则第4步。
-
主库使用
show master status\G;
命令查询file和position -
从库使用
change master to
和start slave
命令开启复制 -
由于是双主,所以,反过来还要执行一遍。
至此,MySQL 双主应该是配置完成的,可以在其中一个库执行修改数据,看看另一个库是否会同步数据。若其中一个库死了,第二个库还在写入数据,在第一个库恢复之后,数据应该还是会重新同步。
附:
change master to
master_host = 'centos1',
master_port = 3308 ,
master_user ='root',
master_password ='root',
master_log_file ='mysql-bin-1.000006', # show master status 得到的file
master_log_pos =1761; # show master status 得到的position
btw,网上有说,两个库如果配置主主复制的话,应该配置两个库的自增主键岔开。但是我没有进行配置,主要是不太清楚,配置了之后Web 程序在与数据库交互的时候会不会有奇奇怪怪的bug 出现。加之不会出现Web 程序同时读写两个库的情况,那么有可能出现冲突的情况,应该是其中一台服务器死机了,但是还有数据没有同步到从库,这时候从库提升为主库,就会造成原本主库的部分数据丢失,也有可能会出现自增主键冲突的情况,不过这种情况发生几率应该不大,故忽略。
Keepalived 配置
1. 下载keepalived 源码包
$ cd /usr/local/src
$ wget https://www.keepalived.org/software/keepalived-2.2.1.tar.gz
$ tar -zxvf keepalived-2.2.1.tar.gz
$ cd keepalived-2.2.1
2. 编译安装
$ yum install -y gcc
$ yum install -y openssl-devel
$ yum install -y libnl3-devel
$ ./configure prefix=/usr/local/keepalived
$ make && make install
3. 开机启动
$ systemctl enable keepalived
4. 修改配置文件
$ vi /etc/keepalived/keepalived.conf
$ cat /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
vrrp_strict
}
vrrp_script check_run {
script "/etc/keepalived/mysql/mysql_check.sh"
interval 30
weight -5
fall 2
rise 1
}
vrrp_sync_group VG1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface ens32
mcast_src_ip 192.168.121.136
virtual_router_id 51
priority 90
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
check_run
}
notify_master /etc/keepalived/mysql/master.sh
notify_stop /etc/keepalived/mysql/stop.sh
virtual_ipaddress {
192.168.121.100
}
}
$cat mysql_check.sh
#!/bin/bash
# 这个脚本的作用是实时检测mysql 实例有没有死
# 检测MySQL 的方法:
# 1. 通过mysql 命令连接MySQL 实例
# 2. 通过docker ps 命令检测容器状态
# 如果连续5次 检测的结果都是MySQL 已死,则判断为MySQL 已死,那么此时应该停止本机的keepalived
count=1
while true
do
/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show status;" > /dev/null 2>&1
i=$?
docker ps | grep mysql > /dev/null 2>&1
j=$?
if [ $i = 0 ] && [ $j = 0 ]
then
exit 0
else
if [ $i = 1 ] && [ $j = 0 ]
then
exit 0
else
if [ $count -gt 5 ]
then
break
fi
let count++
continue
fi
fi
done
/etc/init.d/keepalived stop
$cat master.sh
#!/bin/bash
# 这个脚本是当本机成为了master 之后执行的脚本
# 获取IO 线程当前正在读取的二进制文件
Master_Log_File=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show slave status\G;" | grep -w Master_Log_File | awk -F": " '{print $2}')
# 包含当前SQL 线程最近执行的语句(doc 中是event,不知道翻译的对不对)的二进制文件
Relay_Master_Log_File=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show slave status\G;" | grep -w Relay_Master_Log_File | awk -F": " '{print $2}')
# 当前IO 线程已经读取的二进制文件的位置
Read_Master_Log_Pos=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show slave status\G;" | grep -w Read_Master_Log_Pos | awk -F": " '{print $2}')
# SQL 线程当前读取和执行到的二进制文件的位置
Exec_Master_Log_Pos=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show slave status\G;" | grep -w Exec_Master_Log_Pos | awk -F": " '{print $2}')
i=1
while true
do
# 如果文件不同或者读取和执行到的文件不同,则可能当前的数据库的数据还没有完全完成同步,就等待1min,等数据完成同步
if [ $Master_Log_File = $Relay_Master_Log_File ] && [ $Read_Master_Log_Pos -eq $Exec_Master_Log_Pos ]
then
echo "ok"
break
else
sleep 1
if [ $i -gt 60 ]
then
break
fi
continue
let i++
fi
done
#/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "stop slave;"
#/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "reset slave all;"
/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show master status;" > /tmp/master_status_$(date "+%y%m%d-%H%M").txt
$cat stop.sh
#!/bin/bash
# 这个脚本是本机keepalived 停止时执行的脚本
# 主机的二进制文件名
M_File1=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show master status\G;" | awk -F': ' '/File/{print $2}')
# 主机的二进制文件位置
M_Position1=$(/usr/bin/mysql -uroot -h172.17.0.2 -P3306 -proot -e "show master status\G;" | awk -F': ' '/Position/{print $2}')
# 等待一秒后重新读取以上两个信息
sleep 1
M_File2=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show master status\G;" | awk -F': ' '/File/{print $2}')
M_Position2=$(/usr/bin/mysql -uroot -proot -h172.17.0.2 -P3306 -e "show master status\G;" | awk -F': ' '/Position/{print $2}')
i=1
while true
do
# 如果前后读取的文件和位置相同的话,就认为当前准备关闭的MySQL 实例没有读写操作了,否则等待1min
if [ $M_File1 = $M_File2 ] && [ $M_Position1 -eq $M_Position2 ]
then
echo "ok"
break
else
sleep 1
if [ $i -gt 60 ]
then
break
fi
continue
let i++
fi
done
配置完成keepalived 之后,两台机器启动。
需要先启动MySQL,后启动keepalived,否则,检测到MySQL 没有启动的话,刚启动的keepalived 就又自动关闭了。
通过ip addr
命令查询,应可看到在其中一台机器的网卡上有绑定虚拟ip,而另外一台没有。
PS. keepalived 的配置根据网上拿来的,有些地方可能用不到,但是影响不大。
PPS. 放上官方的参考文献地址,以后说不定就能用到了