一、MMM简介
1. 概述
MMM(Master-Master replication manager for MySQL)是一套支持双主故障切换和双主日常管理的脚本程序。MMM使用Perl语言开发,主要用来监控和管理MySQL Master-Master(双主)复制,可以说是mysql主主复制管理器。虽然叫做双主复制,但是业务上同一时刻只允许对一个主进行写入,另一台备选主上提供部分读服务,以加速在主主切换时刻备选主的预热,可以说MMM这套脚本程序一方面实现了故障切换的功能,另一方面其内部附加的工具脚本也可以实现多个slave的read负载均衡。MMMM是关于MySQL主主复制配置的监控、故障转移和管理的一套可伸缩的脚本套件(在任何时候只有一个节点可以被写入)。这个套件也能对居于标准的主从配置的任意数量的从服务器进行读负载均衡,所以可以用它在一组居于复制的服务器启动虚拟IP,除此之外,它还有实现数据备份、节点之间重新同步功能的脚本。
MMM提供了自动和手动两种方式移除一组服务器中复制延迟较高的服务器的虚拟IP,同时它还可以备份数据,实现两节点之间的数据同步等。由于MMM无法完全的保证数据一致性,所以MMM适用于对数据的一致性要求不是很高,但是又想最大程度的保证业务可用性的场景。MySQL本身没有提供replication failover的解决方案,通过MMM方案能实现服务器的故障转移,从而实现MySQL的高可用。对于那些对数据的一致性要求很高的业务,不建议采用MMM这种高可用架构。
2. 优缺点
- 优点:高可用性,扩展性好,出现故障自动切换,对于主主同步,在同一时间只提供一台数据库写操作,保证的数据的一致性。
- 缺点:Monitor节点是单点,可以结合Keepalived实现高可用。
3. 工作原理
MMM是一套灵活的脚本程序,基于perl实现,用来对mysql replication进行监控和故障迁移,并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的)。MMM的主要功能通过以下三个脚本提供:
- mmm_mond:监视守护进程,它执行所有监视工作并做出有关角色切换的所有决定等等。此脚本需要在监管机上运行。
- mmm_agentd:运行在每个mysql服务器上(Master和Slave)的代理进程,完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。
- mmm_control:一个简单的脚本,提供管理mmm_mond进程的命令。
mysql-mmm的监管端会提供多个虚拟IP(VIP),包括一个可写VIP,多个可读VIP。通过监管的管理,这些IP会绑定在可用MySQL之上,当某一台MySQL宕机时,监管会将VIP迁移至其它MySQL。在整个监管过程中,需要在MySQL中添加相关授权用户,以便让MySQL可以支持监理机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户,如果想使用mmm的备份工具则还要添加一个mmm_tools用户。
4. 典型用例
(1)双节点设置
双节点架构如图1所示。
图1
在双节点主-主设置中,MMM使用五个IP:每个节点的单个永久IP,两个读取VIP(只读)和1个写入VIP(更新)。最后三个IP在节点之间迁移,具体取决于节点可用性。通常在没有复制延迟时,活动主库有2个VIP(读写),备用主库有1个读VIP(只读)。如果发生故障,读写操作都会迁移到工作节点。
(2)双主+一个/多个从
这种架构如图2所示。
图2
5. 系统需求
对于使用n个MySQL服务器的MMM设置,有以下需求:
- n + 1 个主机:每个MySQL服务器一个主机; MMM监视器的一个主机。
- 2 * n + 1 个IP地址:每个主机一个固定IP + 读角色一个VIP,一个写入VIP。
- monitor user:具有用于MMM监视器的REPLICATION CLIENT特权的MySQL用户。
- agent user:具有SUPER,REPLICATION CLIENT,MMM代理进程权限的MySQL用户。
- replication user:具有REPLICATION SLAVE权限的MySQL用户,用于复制。
- tools user:具有SUPER,REPLICATION CLIENT,RELOAD for MMM工具权限的MySQL用户。
监控主机需要安装以下支持包:
(1)perl
(2)fping(如果你想以非root用户身份运行mmm_mond)
(3)Perl模块:
- Algorithm::Diff
- Class:Singleton
- DBI and DBD::mysql
- File::Basename
- File::stat
- File::Temp
- Log::Dispatch
- Log::Log4perl
- Mail::Send
- Net::Ping
- Proc::Daemon
- Thread::Queue
- Time::HiRes
对于节点主机,初始应该在所有MySQL服务器的配置中设置read_only=1,MMM将使用active_master_role在活动主机上将其更改为read_only=0。主机需要安装以下支持包:
(1)perl
(2)iproute
(3)send_arp (solaris)
(4)Perl模块:
- Algorithm::Diff
- DBI and DBD::mysql
- File::Basename
- File::stat
- Log::Dispatch
- Log::Log4perl
- Mail::Send
- Net::ARP (linux)
- Proc::Daemon
- Time::HiRes
如果要使用MMM工具(mmm_backup,mmm_restore,mmm_clone),则必须将LVM用于MySQL数据库和日志所在的分区。注意,需要对回滚段空间进行自由物理扩展,参见“Estimating Undo Space needed for LVM Snapshot”。MMM工具还需要以下perl模块:
- Path::Class
- Data::Dumper
二、实验设计
1. 基本环境
- 操作系统版本:CentOS Linux release 7.2.1511 (Core)
- MySQL版本:5.6.14
- 主机信息:见表1
角色 | IP | 主机名 | 网卡 | server_id |
DB1 | 172.16.1.125 | hdp2 | ens32 | 125 |
DB2 | 172.16.1.126 | hdp3 | ens32 | 126 |
Monitor | 172.16.1.127 | hdp4 | - | - |
表1
- VIP信息:见表2
VIP | 角色 | 描述 |
172.16.1.100 | write | 应用程序连接该VIP对主库进行写请求 |
172.16.1.210 | read | 应用程序连接该VIP进行读请求 |
172.16.1.211 | read | 应用程序连接该VIP进行读请求 |
表2
2. 架构设计
实验架构如图3所示。
图3
三、MMM安装配置
1. 配置双主复制
双主复制的详细配置步骤可以参考这篇文章:,这里从略。
2. 安装MMM
在三台主机执行下面的yum命令安装MMM软件包。
yum -y install mysql-mmm-*
3. 建立数据库用户
在DB1、DB2中建立mmm_agent和mmm_monitor用户。
grant super,replication client,process on *.* to 'mmm_agent'@'%' identified by '123456';
grant replication client on *.* to 'mmm_monitor'@'%' identified by '123456';
4. 配置MMM
(1)通用配置
编辑DB1上的/etc/mysql-mmm/mmm_common.conf文件,内容如下:
active_master_role writer
<host default>
cluster_interface ens32
pid_path /var/run/mmm_agentd.pid
bin_path /usr/libexec/mysql-mmm/
replication_user repl
replication_password 123456
agent_user mmm_agent
agent_password 123456
</host>
<host db1>
ip 172.16.1.125
mode master
peer db2
</host>
<host db2>
ip 172.16.1.126
mode master
peer db1
</host>
<role writer>
hosts db1, db2
ips 172.16.1.100
mode exclusive
</role>
<role reader>
hosts db1, db2
ips 172.16.1.210, 172.16.1.211
mode balanced
</role>
主要配置项说明:
- active_master_role:活动主机角色名称,agent与monitor使用。
- replication_user:用于复制的用户。
- agent_user:mmm-agent用户。
- host段中的mode:标明是否为主或者备选主,或者从库。
- role段中的mode:exclusive为独占模式,同一时刻只能有一个主。balanced可能有多个ips,ips将在主机之间平衡。
- <role write>中hosts:表示目前的主库和备选主的真实主机IP或者主机名,ips为对外提供的虚拟机ip地址。
- <role readr>中hosts:代表从库真实的ip和主机名,ips代表从库的虚拟ip地址。
将该文件复制到其它所有节点(DB2、Monitor)。
scp /etc/mysql-mmm/mmm_common.conf 172.16.1.126:/etc/mysql-mmm/
scp /etc/mysql-mmm/mmm_common.conf 172.16.1.127:/etc/mysql-mmm/
(2)agent配置
DB1的/etc/mysql-mmm/mmm_agent.conf文件内容为:
include mmm_common.conf
this db1
DB2的/etc/mysql-mmm/mmm_agent.conf文件内容为:
include mmm_common.conf
this db2
(3)monitor配置
Monitor上的/etc/mysql-mmm/mmm_mon.conf文件内容为:
include mmm_common.conf
<monitor>
ip 172.16.1.127
pid_path /var/run/mmm_mond.pid
bin_path /usr/libexec/mysql-mmm
status_path /var/lib/mysql-mmm/mmm_mond.status
ping_ips 172.16.1.125,172.16.1.126
auto_set_online 60
</monitor>
<host default>
monitor_user mmm_monitor
monitor_password 123456
</host>
debug 0
auto_set_online表示将节点状态从AWAITING_RECOVERY切换到ONLINE之前等待的秒数,0表示已禁用。
四、功能测试
1. 启动MMM
(1)在DB1、DB2上启动agent
/etc/init.d/mysql-mmm-agent start
/etc/init.d/mysql-mmm-agent start
/etc/init.d/mysql-mmm-agent文件内容分如下:
#!/bin/sh
#
# mysql-mmm-agent This shell script takes care of starting and stopping
# the mmm agent daemon.
#
# chkconfig: - 64 36
# description: MMM Agent.
# processname: mmm_agentd
# config: /etc/mmm_agent.conf
# pidfile: /var/run/mmm_agentd.pid
# Cluster name (it can be empty for default cases)
CLUSTER=''
#-----------------------------------------------------------------------
# Paths
if [ "$CLUSTER" != "" ]; then
MMM_AGENTD_BIN="/usr/sbin/mmm_agentd @$CLUSTER"
MMM_AGENTD_PIDFILE="/var/run/mmm_agentd-$CLUSTER.pid"
else
MMM_AGENTD_BIN="/usr/sbin/mmm_agentd"
MMM_AGENTD_PIDFILE="/var/run/mmm_agentd.pid"
fi
echo "Daemon bin: '$MMM_AGENTD_BIN'"
echo "Daemon pid: '$MMM_AGENTD_PIDFILE'"
#-----------------------------------------------------------------------
# See how we were called.
case "$1" in
start)
# Start daemon.
echo -n "Starting MMM Agent daemon... "
if [ -s $MMM_AGENTD_PIDFILE ] && kill -0 `cat $MMM_AGENTD_PIDFILE` 2> /dev/null; then
echo " already running."
exit 0
fi
$MMM_AGENTD_BIN
if [ "$?" -ne 0 ]; then
echo "failed"
exit 1
fi
echo "Ok"
exit 0
;;
stop)
# Stop daemon.
echo -n "Shutting down MMM Agent daemon"
if [ -s $MMM_AGENTD_PIDFILE ]; then
pid="$(cat $MMM_AGENTD_PIDFILE)"
cnt=0
kill "$pid"
while kill -0 "$pid" 2>/dev/null; do
cnt=`expr "$cnt" + 1`
if [ "$cnt" -gt 15 ]; then
kill -9 "$pid"
break
fi
sleep 2
echo -n "."
done
echo " Ok"
exit 0
fi
echo " not running."
exit 0
;;
status)
echo -n "Checking MMM Agent process:"
if [ ! -s $MMM_AGENTD_PIDFILE ]; then
echo " not running."
exit 3
fi
pid="$(cat $MMM_AGENTD_PIDFILE)"
if ! kill -0 "$pid" 2> /dev/null; then
echo " not running."
exit 1
fi
echo " running."
exit 0
;;
restart|reload)
$0 stop
$0 start
exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
;;
esac
exit 1
(2)在Monitor上启动监控
/etc/init.d/mysql-mmm-monitor start
/etc/init.d/mysql-mmm-monitor文件内容分如下:
#!/bin/sh
#
# mysql-mmm-monitor This shell script takes care of starting and stopping
# the mmm monitoring daemon.
#
# chkconfig: - 64 36
# description: MMM Monitor.
# processname: mmm_mond
# config: /etc/mmm_mon.conf
# pidfile: /var/run/mmm_mond.pid
# Cluster name (it can be empty for default cases)
CLUSTER=''
#-----------------------------------------------------------------------
# Paths
if [ "$CLUSTER" != "" ]; then
MMM_MOND_BIN="/usr/sbin/mmm_mond @$CLUSTER"
MMM_MOND_PIDFILE="/var/run/mmm_mond-$CLUSTER.pid"
else
MMM_MOND_BIN="/usr/sbin/mmm_mond"
MMM_MOND_PIDFILE="/var/run/mmm_mond.pid"
fi
echo "Daemon bin: '$MMM_MOND_BIN'"
echo "Daemon pid: '$MMM_MOND_PIDFILE'"
#-----------------------------------------------------------------------
# See how we were called.
case "$1" in
start)
# Start daemon.
echo -n "Starting MMM Monitor daemon: "
if [ -s $MMM_MOND_PIDFILE ] && kill -0 `cat $MMM_MOND_PIDFILE` 2> /dev/null; then
echo " already running."
exit 0
fi
$MMM_MOND_BIN
if [ "$?" -ne 0 ]; then
echo "failed"
exit 1
fi
echo "Ok"
exit 0
;;
stop)
# Stop daemon.
echo -n "Shutting down MMM Monitor daemon: "
if [ -s $MMM_MOND_PIDFILE ]; then
pid="$(cat $MMM_MOND_PIDFILE)"
cnt=0
kill "$pid"
while kill -0 "$pid" 2>/dev/null; do
cnt=`expr "$cnt" + 1`
if [ "$cnt" -gt 15 ]; then
kill -9 "$pid"
break
fi
sleep 2
echo -n "."
done
echo " Ok"
exit 0
fi
echo " not running."
exit 0
;;
status)
echo -n "Checking MMM Monitor process:"
if [ ! -s $MMM_MOND_PIDFILE ]; then
echo " not running."
exit 3
fi
pid="$(cat $MMM_MOND_PIDFILE)"
if ! kill -0 "$pid" 2> /dev/null; then
echo " not running."
exit 1
fi
echo " running."
exit 0
;;
restart|reload)
$0 stop
$0 start
exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
;;
esac
exit 1
(3)检查MMM启动后的状态
mmm启动成功后,在Monitor上执行mmm_control show和mmm_control checks命令结果如下。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
[root@hdp4~]#mmm_control checks
db2 ping [last change: 2018/08/02 08:57:38] OK
db2 mysql [last change: 2018/08/02 08:57:38] OK
db2 rep_threads [last change: 2018/08/02 08:57:38] OK
db2 rep_backlog [last change: 2018/08/02 08:57:38] OK: Backlog is null
db1 ping [last change: 2018/08/02 08:57:38] OK
db1 mysql [last change: 2018/08/02 08:57:38] OK
db1 rep_threads [last change: 2018/08/02 08:57:38] OK
db1 rep_backlog [last change: 2018/08/02 08:57:38] OK: Backlog is null
2. 测试切换
(1)停止DB1上MySQL服务
service mysql stop
查看状态,DB1上的VIP reader(172.16.1.210) 自动迁移到DB2上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/HARD_OFFLINE. Roles:
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
(2)启动DB1上MySQL服务
service mysql start
一分钟之后,状态恢复:
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
(3)停止DB2上MySQL服务
service mysql stop
DB2上负责读的VIP(172.16.1.211)以及负责写的VIP(172.16.1.100)会自动迁移到DB1上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/HARD_OFFLINE. Roles:
[root@hdp4~]#
(4)启动DB2上的MySQL服务
service mysql start
一分钟之后,DB1上负责读的VIP(172.16.1.210)自动迁移到DB2上,但是负责写的VIP(172.16.1.100),仍在DB1上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(5)只读节点上stop slave
在DB2上停止复制:
mysql> stop slave;
查看状态,DB2上的VIP(172.16.1.210)会自动迁移到DB1上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/REPLICATION_FAIL. Roles:
[root@hdp4~]#
(6)只读节点上start slave
在DB2上启动复制:
mysql> start slave;
状态恢复:
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(7)读写节点上stop slave
在DB1上停止复制:
mysql> stop slave;
查看状态无任何变化。理论上也应该是对现有的环境无任何影响。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(8)停止MMM监控主机上的monitor服务
/etc/init.d/mysql-mmm-monitor stop
VIP都还在之前的节点上:
[root@hdp2~]#ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 172.16.1.125/24 brd 172.16.1.255 scope global ens32
inet 172.16.1.100/32 scope global ens32
inet 172.16.1.211/32 scope global ens32
[mysql@hdp3~]$ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
inet 172.16.1.210/32 scope global ens32
(9)启动MMM监控服务
/etc/init.d/mysql-mmm-monitor start
对DB1和DB2上的相关服务无影响。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(10)查看监控日志
以上的角色切换的过程都在监控日志中记录:
[root@hdp4~]#tail -f /var/log/mysql-mmm/mmm_mond.log
...
2018/08/02 09:07:46 FATAL State of host 'db1' changed from ONLINE to HARD_OFFLINE (ping: OK, mysql: not OK)
2018/08/02 09:10:53 FATAL State of host 'db1' changed from HARD_OFFLINE to AWAITING_RECOVERY
2018/08/02 09:11:54 FATAL State of host 'db1' changed from AWAITING_RECOVERY to ONLINE because of auto_set_online(60 seconds). It was in state AWAITING_RECOVERY for 61 seconds
2018/08/02 09:14:06 FATAL State of host 'db2' changed from ONLINE to HARD_OFFLINE (ping: OK, mysql: not OK)
2018/08/02 09:16:22 FATAL State of host 'db2' changed from HARD_OFFLINE to AWAITING_RECOVERY
2018/08/02 09:17:24 FATAL State of host 'db2' changed from AWAITING_RECOVERY to ONLINE because of auto_set_online(60 seconds). It was in state AWAITING_RECOVERY for 62 seconds
2018/08/02 09:20:02 FATAL State of host 'db2' changed from ONLINE to REPLICATION_FAIL
2018/08/02 09:22:14 FATAL State of host 'db2' changed from REPLICATION_FAIL to ONLINE