MHA(Master High Availability),也称主库高可用(或高可用),即在 MySQL Replication (主从复制) 的基础上,对其进行优化。
目前在 MySQL 高可用方面是一个相对成熟的解决方案,它由日本 DeNA 公司 youshimaton(现就职于Facebook公司)开发,是一套优秀的作为 MySQL 高可用性环境下故障切换和主从提升的高可用软件。在 MySQL 故障切换过程中,MHA 能做到在 0 ~ 30 秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA 能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
MHA Manager 可以单独部署在一台独立的机器上,管理多个 master-slave 集群,也可以部署在一台 slave 节点上。MHA Node 运行在每台 MySQL 服务器上,MHA Manager 会定时探测集群中的 master 节点,当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master,然后将所有其他的 slave 重新指向新的master。整个故障转移过程对应用程序完全透明。
在 MHA 自动故障切换过程中,MHA 试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过 ssh 访问,MHA 没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用 MySQL 5.5 的半同步复制,可以大大降低数据丢失的风险。MHA 可以与半同步复制结合起来。如果只有一个 slave 已经收到了最新的二进制日志,MHA 可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。
目前 MHA 主要支持一主多从的架构,搭建一个 MHA 集群最少要三台数据库服务器,一主二从,即一台充当master,一台充当备用 master,另外一台充当从库。
MHA 的复制机制:
(1) 从宕机崩溃的 master 保存二进制日志事件(binlog events);
(2) 识别含有最新更新的 slave;
(3) 应用差异的中继日志(relay log)到其它的 slave;
(4) 应用从 master 保存的二进制日志事件(binlog events);
(5) 提升一个 slave 为新的 master;
(6) 使其他的 slave 连接新的 master 进行复制;
MHA Manager 工具包主要包括以下几个工具:
(1) masterha_check_ssh:检查 MHA 的 SSH 配置加测工具;
(2) masterha_check_repl:检查 MySQL复制环境检测工具;
(3) masterha_manger:启动MHA服务主程序;
(4) masterha_check_status :检测当前 MHA 运行状态探测工具;
(5) masterha_master_monitor:检测 master 是否宕机;
(6) masterha_master_switch:控制故障转移(自动或者手动);
(7) masterha_conf_host:添加或删除配置的 server 信息;
Node 工具包主要包括以下几个工具:
(1) save_binary_logs:保存和复制 master 的二进制日志;
(2) apply_diff_relay_logs:识别差异的中继日志事件并将其差异的事件应用于其他的 slave;
(3) filter_mysqlbinlog:去除不必要的 ROLLBACK 事件(MHA 已不再使用这个工具);
(4) purge_relay_logs:清除中继日志(不会阻塞 SQL 线程);
注:这些工具通常由 MHA Manager 的脚本触发,无需人为操作。
自定义扩展:
(1) secondary_check_script: 通过多条网络路由检测 master 可用性;
(2) master_ip_failover_script: 更新 master 节点 ip 地址;
(3) shutdown_script: 关闭主机;
(4) report_script: 发送报告;
(5) init_conf_load_script:加载初始配置参数;
MHA Manager:https://github.com/yoshinorim/mha4mysql-manager
MHA Node:https://github.com/yoshinorim/mha4mysql-node
MHA Google: https://code.google.com/p/mysql-master-ha
本文部署 MHA + MariaDB 集群来演示如何实现高可用。
1. 部署环境
IP 地址(本地测试环境):192.168.0.10
操作系统:Linux CentOS 7.9
Docker 版本:20.10.7
Docker Compose 版本:2.6.1
MyCat 版本:1.6.7.5
MariaDB 集群目录:/home/docker/mysql_cluster
MHA 目录:/home/docker/mysql_cluster/mha
MHA build 目录:/home/docker/mysql_cluster/mha/build
2. 构建 MHA-Manager 镜像
1) 下载 MHA
在 https://github.com/yoshinorim 上 mha4mysql 只有一个版本 v0.58,v0.58 有一个 super_read_only 参数在 MariaDB 10.4 上不能用,所以本文使用 v0.56 版本:mha4mysql-manager-0.56.tar.gz 和 mha4mysql-node-0.56.tar.gz。
创建 /home/docker/mysql_cluster/mha/build/manager 目录,把 mha4mysql-manager-0.56.tar.gz 和 mha4mysql-node-0.56.tar.gz 复制到该目录下。
下载地址:https://gitee.com/slksm/public-codes/tree/master/mha (包含 v0.58、v0.56,声明:v0.56 来源于网络,无法知道其代码与官方发布版本是否一致。)
2) 创建 Dockerfile
$ cd /home/docker/mysql_cluster/mha/build/manager
$ vim Dockerfile
FROM debian:jessie COPY ./mha4mysql-manager-0.56.tar.gz /tmp/ COPY ./mha4mysql-node-0.56.tar.gz /tmp/ RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list RUN sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list RUN build_deps='ssh sshpass perl libdbi-perl libmodule-install-perl libdbd-mysql-perl libconfig-tiny-perl liblog-dispatch-perl libparallel-forkmanager-perl make' \ && apt-get update \ && apt-get -y --force-yes install $build_deps \ && tar -zxf /tmp/mha4mysql-node-0.56.tar.gz -C /opt \ && cd /opt/mha4mysql-node-0.56 \ && perl Makefile.PL \ && make \ && make install \ && tar -zxf /tmp/mha4mysql-manager-0.56.tar.gz -C /opt \ && cd /opt/mha4mysql-manager-0.56 \ && perl Makefile.PL \ && make \ && make install \ && cd /opt \ && rm -rf /opt/mha4mysql-* \ && apt-get clean
注:这里使用 debian:jessie 基础镜像来演示,可以访问 https://hub.docker.com/_/debian/tags 查询其它 debian 版本。如果官方源访问慢,可以设置阿里源。
在新创建的镜像里,安装 perl 并自动配置 mha4mysql-manager 和 mha4mysql-node 的脚本。
3) 构建
$ cd /home/docker/mysql_cluster/mha/build/manager
$ docker build -t mha-manager:2022-10-08 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mha-manager 2022-10-08 b40ed37fa041 1 hours ago 215MB
...
3. 构建 MariaDB + MHA-Node 镜像
1) 创建 Dockerfile
$ cd /home/docker/mysql_cluster/mha/build
$ mkdir node
$ cd node
$ cp ../manager/mha4mysql-node-0.56.tar.gz ./
$ vim Dockerfile
FROM mariadb:10.4 COPY ./mha4mysql-node-0.56.tar.gz /tmp/ RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list RUN build_deps='ssh sshpass perl libdbi-perl libmodule-install-perl libdbd-mysql-perl make' \ && apt-get update \ && apt-get -y --force-yes install $build_deps \ && tar -zxf /tmp/mha4mysql-node-0.56.tar.gz -C /opt \ && cd /opt/mha4mysql-node-0.56 \ && perl Makefile.PL \ && make \ && make install \ && cd /opt \ && rm -rf /opt/mha4mysql-* \ && apt-get clean
注:MariaDB 默认的基础镜像是 Ubuntu,如果官方源访问慢,可以设置阿里源。
Ubuntu 运行 apt-get update 时,可能存在时区冲突问题,需要把宿主的时间同步为网络当前时间。本文使用的宿主是 CentOS 7,CentOS 同步网络时间的命令如下:
$ sudo yum -y install ntp ntpdate # 安装 ntpdate 工具
$ sudo ntpdate 0.asia.pool.ntp.org # 设置系统时间与网络时间同步
其它时间服务器:
time.nist.gov
time.nuri.net
0.asia.pool.ntp.org
1.asia.pool.ntp.org
2.asia.pool.ntp.org
3.asia.pool.ntp.org
可以使用 crontab 计划任务定时更新网络时间,修改 crontab 文件,在末尾增加 * */1 * * * ntpdate 0.asia.pool.ntp.org,每隔 1 小时同步一次时间。
$ sudo vi /etc/crontab#crontab -e / crontab -l # 编辑 crontab 定时任务
还可以系统时间同步到硬件,防止系统重启后时间还原:
$ hwclock --systohc
2) 构建
$ cd /home/docker/mysql_cluster/mha/build/node
$ docker build -t mariadb-mha-node:10.4 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mariadb-mha-node 10.4 53833ca515ac 4 minutes ago 563MB
mha-manager 2022-10-08 b40ed37fa041 9 hours ago 215MB
...
4. MariaDB 集群目录
在 /home/docker/mysql_cluster 目录下,目录结构如下:
mysql_cluster |- master | |- conf | | |- my_mariadb.cnf | |- data | |- log | |- node_1 | |- conf | | |- my_mariadb.cnf | |- data | |- log | |- node_2 | |- conf | | |- my_mariadb.cnf | |- data | |- log | |- mha |- conf | |- mha_manager.conf | |- share | |-scripts | | |- ssh_key.sh | | |- ssh_execute.sh | | |- mariadb_grant_slave.sh | | |- mariadb_start_slave.sh | | |- mariadb_execute.sh | | |- mha_execute.sh | | | |- ssh-keys | |- common.env | |- work |- docker-compose.yml
注:common.env 里包含账号、密码等环境参数,ssh-keys 包含各容器的 SSH public key。
5. MariaDB 的配置文件
1) 创建 MariaDB 主库 master 的 my_mariadb.cnf 文件
在 /home/docker/mysql_cluster/master/conf 目录下,创建 my_mariadb.cnf 文件,内容如下:
[mysqld] server-id=1 port=3306 #basedir=/usr/local/mysql #tmpdir=/tmp datadir=/var/lib/mysql # 查询日志,默认在 /var/lib/mysql 目录下 #general_log=1 #general_log_file=mysql_general-1.log # 慢查询日志,默认在 /var/log/mysql 目录下 #slow_query_log=1 #long_query_time=1 #slow_query_log_file=mysql_slow_query-1.log # 错误日志,指定到 /var/log/mysql 目录 log_error=/var/log/mysql/mysql_err-1.log # 二进制日志,默认在 /var/lib/mysql 目录下 log_bin=mysql_binlog-1
注:MariaDB (MySQL) 的默认配置文件是 /etc/mysql/my.cnf 文件。如果想要自定义配置,在 /etc/mysql/conf.d 目录中创建 *.cnf 文件。新建的文件可以任意起名,只要保证后缀名是 cnf 即可。新建的文件中的配置项可以覆盖 /etc/mysql/my.cnf 中的配置项。
2) 创建 MariaDB 备库 node_1 的 my_mariadb.cnf 文件
在 /home/docker/mysql_cluster/node_1/conf 目录下,创建 my_mariadb.cnf 文件,内容如下:
[mysqld] server-id=2 port=3307 #basedir=/usr/local/mysql #tmpdir=/tmp datadir=/var/lib/mysql # 查询日志,默认在 /var/lib/mysql 目录下 #general_log=1 #general_log_file=mysql_general-2.log # 慢查询日志,默认在 /var/log/mysql 目录下 #slow_query_log=1 #long_query_time=1 #slow_query_log_file=mysql_slow_query-2.log # 错误日志,指定到 /var/log/mysql 目录 log_error=/var/log/mysql/mysql_err-2.log # relay-log #relay-log=mysql-relaylog-2 # 二进制日志,默认在 /var/lib/mysql 目录下 log_bin=mysql_binlog-2 #read_only=ON
3) 创建 MariaDB 从库 node_2 的 my_mariadb.cnf 文件
在 /home/docker/mysql_cluster/node_2/conf 目录下,创建 my_mariadb.cnf 文件,内容如下:
[mysqld] server-id=3 port=3308 #basedir=/usr/local/mysql #tmpdir=/tmp datadir=/var/lib/mysql # 查询日志,默认在 /var/lib/mysql 目录下 #general_log=1 #general_log_file=mysql_general-3.log # 慢查询日志,默认在 /var/log/mysql 目录下 #slow_query_log=1 #long_query_time=1 #slow_query_log_file=mysql_slow_query-3.log # 错误日志,指定到 /var/log/mysql 目录 log_error=/var/log/mysql/mysql_err-3.log # relay-log #relay-log=mysql-relaylog-3 # 二进制日志,默认在 /var/lib/mysql 目录下 #log_bin=mysql_binlog-3 #read_only=ON
6. MHA 配置文件
1) 创建 mha_manager.conf 文件
在 /home/docker/mysql_cluster/mha/conf 目录下,创建 mha_manager.conf 文件,内容如下:
[server default] user=root password=123456 ssh_user=root manager_workdir=/usr/local/mha remote_workdir=/usr/local/mha repl_user=mhatest repl_password=mhatest [server0] hostname=172.25.0.6 candidate_master=1 port=3306 [server1] hostname=172.25.0.7 candidate_master=1 port=3307 [server2] hostname=172.25.0.8 #candidate_master=1 no_master=1 port=3308
2) 创建 common.env 文件
在 /home/docker/mysql_cluster/mha/share 目录下,创建 common.env 文件,内容如下:
ROOT_PASSWORD=123456 MYSQL_ROOT_PASSWORD=123456 MYSQL_DATABASE=testdb MYSQL_USER=tuser MYSQL_PASSWORD=tuser MHA_HOST_GROUP=172.25.0.% MHA_HOST_MASTER=172.25.0.6 MHA_REPL_USER=mhatest MHA_REPL_PASSWORD=mhatest MHA_SHARE_SSHKEYS_PATH=/root/mha_share/ssh-keys MHA_SHARE_SCRIPTS_PATH=/root/mha_share/scripts
注:/root/mha_share 是容器内的路径,创建容器时会把 /home/docker/mysql_cluster/mha/share 挂载到容器内 /root/mha_share 。
3) SSH 脚本
(1) 创建 ssh_key.sh
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 ssh_key.sh 文件,内容如下:
#!/bin/bash if [[ $1 = 'generate' ]]; then ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa cp /root/.ssh/id_rsa.pub "$MHA_SHARE_SSHKEYS_PATH/id_rsa_$CONTAINER_NAME.pub" elif [[ $1 = 'share' ]]; then cat $MHA_SHARE_SSHKEYS_PATH/*.pub > /root/.ssh/authorized_keys else echo "Usage: $0 [generate|share]" echo " generate - Generate SSH key" echo " share - Apppend SSH public key to ~/.ssh/authorized_keys" echo "" fi
(2) 创建 ssh_execute.sh
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 ssh_execute.sh 文件,内容如下:
#!/bin/bash SCRIPTS_PATH=/root/mha_share/scripts container_array=('mariadb_master-10.4' 'mariadb_node_1-10.4' 'mariadb_node_2-10.4' 'mha_manager') if [[ $1 = 'start' ]]; then for var in ${container_array[*]}; do docker exec -it $var /bin/bash service ssh start done elif [[ $1 = 'share' ]]; then for var in ${container_array[*]}; do docker exec -it $var /bin/bash $SCRIPTS_PATH/ssh_key.sh generate done for var in ${container_array[*]}; do docker exec -it $var /bin/bash $SCRIPTS_PATH/ssh_key.sh share done else echo "Usage: $0 [start|share]" echo " start - Start SSH service" echo " share - Execute ssh_key.sh" echo "" fi
注:/root/mha_share/scripts 是容器内的路径。
4) MariaDB 脚本
(1) 创建 mariadb_grant_slave.sh
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 mariadb_grant_slave.sh 文件,内容如下:
mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOSQL grant all on *.* to $MYSQL_USER@'$MHA_HOST_GROUP' identified by '$MYSQL_PASSWORD'; grant replication slave on *.* to $MHA_REPL_USER@'$MHA_HOST_GROUP' identified by '$MHA_REPL_PASSWORD'; reset master; EOSQL
注:' 是单引号,不是反单引号。
(2) 创建 mariadb_start_slave.sh
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 mariadb_start_slave.sh 文件,内容如下:
mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOSQL stop slave; grant all on *.* to $MYSQL_USER@'$MHA_HOST_GROUP' identified by '$MYSQL_PASSWORD'; change master to master_host='$MHA_HOST_MASTER', master_user='$MHA_REPL_USER', master_password='$MHA_REPL_PASSWORD', master_connect_retry=60; start slave; EOSQL
(3) 创建 mariadb_execute.sh
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 mariadb_execute.sh 文件,内容如下:
SCRIPTS_PATH=/root/mha_share/scripts container_array1=('mariadb_master-10.4' 'mariadb_node_1-10.4') container_array2=('mariadb_node_1-10.4' 'mariadb_node_2-10.4') for var in ${container_array1[*]}; do docker exec -it $var /bin/bash $SCRIPTS_PATH/mariadb_grant_slave.sh done for var in ${container_array2[*]}; do docker exec -it $var /bin/bash $SCRIPTS_PATH/mariadb_start_slave.sh done
5) MHA 脚本
在 /home/docker/mysql_cluster/mha/share/scripts 目录下,创建 mha_execute.sh 文件,内容如下:
#!/bin/bash CONTAINER_NAME=mha_manager if [[ $1 = 'start_manager' ]]; then docker exec -it $CONTAINER_NAME masterha_manager --conf=/etc/mha/mha_manager.conf elif [[ $1 = 'check_ssh' ]]; then docker exec -it $CONTAINER_NAME masterha_check_ssh --conf=/etc/mha/mha_manager.conf elif [[ $1 = 'check_repl' ]]; then docker exec -it $CONTAINER_NAME masterha_check_repl --conf=/etc/mha/mha_manager.conf else echo "Usage: $0 [start_manager|check_ssh|check_repl]" echo " start_manager - Start MHA manager" echo " check_ssh - Check SSH connection" echo " check_repl - Check replication" echo "" fi
7. 创建 docker-compose.yml
在 /home/docker/mysql_cluster/mha 目录下,创建 docker-compose.yml 文件,内容如下:
version: "3" services: mariadb_master: image: mariadb-mha-node:10.4 container_name: mariadb_master-10.4 ports: - "3306:3306" restart: always networks: my_net: ipv4_address: 172.25.0.6 volumes: - /home/docker/mysql_cluster/master/conf:/etc/mysql/conf.d - /home/docker/mysql_cluster/master/data:/var/lib/mysql - /home/docker/mysql_cluster/master/log:/var/log/mysql - /home/docker/mysql_cluster/mha/share:/root/mha_share env_file: - /home/docker/mysql_cluster/mha/share/common.env environment: - CONTAINER_NAME=mariadb_master-10.4 mariadb_node_1: image: mariadb-mha-node:10.4 container_name: mariadb_node_1-10.4 ports: - "3307:3307" restart: always networks: my_net: ipv4_address: 172.25.0.7 volumes: - /home/docker/mysql_cluster/node_1/conf:/etc/mysql/conf.d - /home/docker/mysql_cluster/node_1/data:/var/lib/mysql - /home/docker/mysql_cluster/node_1/log:/var/log/mysql - /home/docker/mysql_cluster/mha/share:/root/mha_share env_file: - /home/docker/mysql_cluster/mha/share/common.env environment: - CONTAINER_NAME=mariadb_node_1-10.4 mariadb_node_2: image: mariadb-mha-node:10.4 container_name: mariadb_node_2-10.4 ports: - "3308:3308" restart: always networks: my_net: ipv4_address: 172.25.0.8 volumes: - /home/docker/mysql_cluster/node_2/conf:/etc/mysql/conf.d - /home/docker/mysql_cluster/node_2/data:/var/lib/mysql - /home/docker/mysql_cluster/node_2/log:/var/log/mysql - /home/docker/mysql_cluster/mha/share:/root/mha_share env_file: - /home/docker/mysql_cluster/mha/share/common.env environment: - CONTAINER_NAME=mariadb_node_2-10.4 mha_manager: image: mha-manager:2022-10-08 container_name: mha_manager depends_on: - mariadb_master - mariadb_node_1 - mariadb_node_2 restart: always networks: my_net: ipv4_address: 172.25.0.5 volumes: - /home/docker/mysql_cluster/mha/conf:/etc/mha - /home/docker/mysql_cluster/mha/work:/usr/local/mha - /home/docker/mysql_cluster/mha/share:/root/mha_share entrypoint: "sleep infinity" env_file: - /home/docker/mysql_cluster/mha/share/common.env environment: - CONTAINER_NAME=mha_manager networks: my_net: driver: bridge ipam: config: - subnet: 172.25.0.0/16
注:当容器启动后没有 service 运行,但又想继续保持 running 状态,可以添加 entrypoint: "tail -f /dev/null" (即查看空设备的日志,一直会处于阻塞状态),也可以使用 entrypoint: "sleep infinity" (睡眠阻塞)。
8. 运行和测试
1) 启动容器
$ cd /home/docker/mysql_cluster/mha # 进入 docker-compose.yml 所在目录
$ docker-compose up -d # 在后台运行
$ docker ps
CONTAINER ID IMAGE ... PORTS NAMES b16aa6d0b58c mha-manager:2022-10-08 ... mha_manager a545dedf2f7c mariadb-mha-node:10.4 0.0.0.0:3306->3306/tcp, mariadb_master-10.4 a2cd59548f57 mariadb-mha-node:10.4 0.0.0.0:3308->3308/tcp, mariadb_node_2-10.4 ffa0c88edf68 mariadb-mha-node:10.4 0.0.0.0:3307->3307/tcp, mariadb_node_1-10.4
2) SSH 配置
MHA 要求各个主机能够相互 SSH 登录,执行如下命令:
$ cd /home/docker/mysql_cluster/mha
$ sh ./share/scripts/ssh_execute.sh start
$ sh ./share/scripts/ssh_execute.sh share # 只需在首次启动容器时执行一次
注:ssh_execute.sh start 开启各个容器上的 SSH 服务,docker 容器启动时,SSH 服务不会自动启动,所以首次启动容器、重启容器,都需要执行该脚本。比如:执行了 docker-compose stop、docker pause 等命令后,需要重启容器的情况。
在 mha_manager 容器上检测 SSH 是否配置成功:
$ sh ./share/scripts/mha_execute.sh check_ssh
...
Sat Oct 8 04:54:20 2022 - [debug] ok.
Sat Oct 8 04:54:20 2022 - [info] All SSH connection tests passed successfully.
注:以上脚本的功能,也可执行如下命令实现:
$ docker exec -it mha_manager /bin/bash
root@bd0a13f7e084:/# masterha_check_ssh --conf=/etc/mha/mha_manager.conf
3) 开启主从复制
首先对主库、备考创建和授权复制账号,对备库、从库设置主库信息和开始复制,以下命令会完成这些操作:
$ cd /home/docker/mysql_cluster/mha
$ sh ./share/scripts/mariadb_execute.sh # 只需在首次启动容器时执行一次
注:执行脚本成功后,在主库上创建 testdb 数据库,并在 testdb 下创建一张表,写入一些数据,查看备库、从库会不会同步。具体操作可以参考 “Docker基础知识 (13) - 部署 MariaDB 集群 (一) | 主从复制”。
在 mha_manager 容器上检测 REPL 是否配置成功:
$ sh ./share/scripts/mha_execute.sh check_repl
...
Mon Oct 10 11:51:51 2022 - [info] Got exit code 0 (Not master dead).
MySQL Replication Health is OK.
注:上面显示的内容表示检测成功。
如果使用 mha4mysql v0.58,MHA 检查复制状态时出现如下报错:
Checking if super_read_only is defined and turned on..DBD::mysql::st execute failed: Unknown system variable 'super_read_only' at /usr/local/share/perl/5.30.0/MHA/SlaveUtil.pm line 245.
因为 v0.58 有一个 super_read_only 参数在 MariaDB 10.4 上不能用。
4) 开启 MHA 监控
SSH 和 REPL 检测正常后,开启 MHA 监控:
$ cd /home/docker/mysql_cluster/mha $ sh ./share/scripts/mha_execute.sh start_manager ... Mon Oct 10 12:02:31 2022 - [warning] master_ip_failover_script is not defined. Mon Oct 10 12:02:31 2022 - [warning] shutdown_script is not defined. Mon Oct 10 12:02:31 2022 - [info] Set master ping interval 3 seconds. Mon Oct 10 12:02:31 2022 - [warning] secondary_check_script is not defined. It is highly recommended setting it to check master reachability from two or more routes. Mon Oct 10 12:02:31 2022 - [info] Starting ping health check on 172.25.0.6(172.25.0.6:3306).. Mon Oct 10 12:02:31 2022 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn't respond..
masterha_manager 进程会一直监视主库状态是否可用,控制台(Console 1)处于阻塞状态。如果主库宕机,masterha_manager 会将备库与从库的 Relay Log 进行比较,把最新的数据整合到备库,然后把备库提升为新主库,从库跟随复制新主库,最后masterha_manager 进程会退出,不再监控。
5) 模拟 master 节点故障
可以暂停主库(mariadb_master-10.4)的容器,打开一个新的控制台(Console 2):
$ docker pause mariadb_master-10.4
以上命令运行后,控制台(Console 1)阻塞状态结束阻塞,新显示内容如下:
... ----- Failover Report ----- mha_manager: MySQL Master failover 172.25.0.6(172.25.0.6:3306) to 172.25.0.7(172.25.0.7:3307) succeeded Master 172.25.0.6(172.25.0.6:3306) is down! Check MHA Manager logs at fdc4662754c2 for details. Started automated(non-interactive) failover. The latest slave 172.25.0.7(172.25.0.7:3307) has all relay logs for recovery. Selected 172.25.0.7(172.25.0.7:3307) as a new master. 172.25.0.7(172.25.0.7:3307): OK: Applying all logs succeeded. 172.25.0.8(172.25.0.8:3308): This host has the latest relay log events. Generating relay diff files from the latest slave succeeded. 172.25.0.8(172.25.0.8:3308): OK: Applying all logs succeeded. Slave started, replicating from 172.25.0.7(172.25.0.7:3307) 172.25.0.7(172.25.0.7:3307): Resetting slave info succeeded. Master failover to 172.25.0.7(172.25.0.7:3307) completed successfully.
我们查看从库(mariadb_node_2-10.4)容器的状态:
$ docker exec -it mariadb_node_2-10.4 /bin/bash root@76111eff114c:/# mysql -uroot -p Enter password: ... MariaDB [(none)]> show slave status \G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172.25.0.7 Master_User: mhatest Master_Port: 3307 Connect_Retry: 60 Master_Log_File: mysql_binlog-2.000001 Read_Master_Log_Pos: 514 Relay_Log_File: mysqld-relay-bin.000002 Relay_Log_Pos: 560 Relay_Master_Log_File: mysql_binlog-2.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes ...
很显然,之前的备库 172.25.0.7 已经变成了主库,Failover 成功。
6) 恢复 MHA 状态
把 mariadb_master-10.4 变为 mariadb_node_1-10.4 的备库即可恢复 MHA 的状态。
修改 mariadb_master-10.4 的 my_mariadb.cnf 文件,为了防止无效数据写入,需要添加如下内容:
read_only=ON
重启 mariadb_master-10.4 容器,重新设置主从复制:
$ docker restart mariadb_master-10.4 $ docker exec -it mariadb_master-10.4 /bin/bash root@b84979617acf:/# mysql -uroot -p Enter password: ... MariaDB [(none)]> show variables like 'read_only'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | read_only | ON | +---------------+-------+ 1 row in set (0.001 sec) MariaDB [(none)]> change master to master_host='172.25.0.7', master_port=3307, master_user='mhatest', master_password='mhatest', master_connect_retry=60; MariaDB [(none)]> start slave;
重启整个数据库集群:
$ cd /home/docker/mysql_cluster/mha
$ docker-compose restart
重启各容器的 SSH 服务:
$ sh ./share/scripts/ssh_execute.sh start
顺序执行如下命令,进入下一轮 MHA 状态:
$ sh ./share/scripts/mha_execute.sh check_ssh # 在 mha_manager 容器上检测 SSH 服务
$ sh ./share/scripts/mha_execute.sh check_repl # 在 mha_manager 容器上检测 REPL 服务
$ sh ./share/scripts/mha_execute.sh start_manager # 开启 MHA 监控