数据的重要性
数据已成为当今数字时代最重要的资产之一,对于企业的成功至关重要。它可以帮助企业了解客户、市场和自身运营,提高运营效率,做出明智决策,推动创新,并获得竞争优势。
数据的采集,存储,分析离不开企业的各种软件系统,这些数据最终都会存储在数据库中。确保数据安全,数据完整性,不丢失数据可以说是整个IT团队的责任,是保障系统正常运行和业务连续性的重要保证。
MySQL高可用方案
通常来讲,单台数据库服务器很容易就会发生单点故障,就很有可能造成业务数据的丢失,为了避免单点故障,生产环境的数据库都会采用高可用集群的方式进行部署。
MySQL有几中常见的高可用方案
- MySQL复制(MySQL Replication)
- MHA(Master High Availability)
- MGR(MySQL Group Replication)
今天我们要讲的就是Mysql官方提供的MGR解决方案。更详细的内容,大家可以翻阅Mysql官方关于组复制的参考文档。
MGR简单说明
MySQL Group Replication是MySQL数据库的一种高可用性解决方案,它是基于组复制(Group Replication)技术实现的。它允许多个MySQL服务器之间形成一个同步复制组,实现数据的自动复制和故障转移,从而提高了系统的可用性和容错性。
通过Docker容器模拟搭建一个MGR集群
我们进入今天的主题,带大家一步一步通过Docker容器来快速搭建一个MGR的集群。不过要声明一点,我们这个MGR集群是在本地电脑上模拟出的三个节点,实际上还是属于单点,只是为了让大家熟悉了理解MGR集群创建的整个过程,生产环境请不要按这种方式操作。
容器环境的准备
如果我们的操作系统上还没有安装Docker运行时环境的,可以参考我写的另两篇文章
- 【Ubuntu 22.04下Docker安装(最全指引)】
- 【Mac OS下Docker的安装与配置】
准备工作
- 创建容器网络
- 创建工作目录
创建容器网络
#创建容器网络,并指定subnet
docker network create --subnet=172.20.0.0/16 mysql-mgr#检查容器网络
vian@txzq1899-ubuntu:~$ docker network inspect mysql-mgr
[{"Name": "mysql-mgr","Id": "6d33e30330ed998c7ceb26971455479d5642d04e015bcfbab720733f9655bd80","Created": "2024-05-07T17:38:32.975808257+08:00","Scope": "local","Driver": "bridge","EnableIPv6": false,"IPAM": {"Driver": "default","Options": {},"Config": [{"Subnet": "172.20.0.0/16"}]},"Internal": false,"Attachable": false,"Ingress": false,"ConfigFrom": {"Network": ""}
]
创建工作目录
工作目录主要是用于存放脚本,初始配置文件,容器数据卷,用户名&密码文件
vian@txzq1899-ubuntu:~/ws/mysql$ ll
总计 24
drwxrwxr-x 6 vian vian 4096 5月 8 01:34 ./
drwxrwxr-x 10 vian vian 4096 5月 8 09:40 ../
drwxrwxr-x 5 vian vian 4096 5月 8 01:42 conf/
drwxrwxr-x 5 999 vian 4096 5月 8 01:27 data/
drwxrwxr-x 2 vian vian 4096 5月 8 01:45 script/
drwxrwxr-x 2 vian vian 4096 5月 8 09:50 secrets/vian@txzq1899-ubuntu:~/ws/mysql/secrets$ ll
总计 16
drwxrwxr-x 2 vian vian 4096 5月 8 09:50 ./
drwxrwxr-x 6 vian vian 4096 5月 8 01:34 ../
-rw-rw-r-- 1 vian vian 11 5月 7 10:38 mysql-root
-rw-rw-r-- 1 vian vian 11 5月 7 10:38 repl-user
脚本源码
本文中所有的操作步骤我都已经进行了脚本化,大家可以上我的github repo上自行下载相关的脚本。
地址如下:MySQL Containerize
https://github.com/TXZQ1899/containerize.git
STEP1、初始化Mysql实例
- 创建三个MySQL容器
- 映射数据卷
- 分配IP地址
以下是脚本源码
- 容器IP与准备工作中的子网是对应上的。
- 映射了数据卷
- 数据库文件
- 密码文件
我们看到,第一次创建和启动容器是没有使用自定义的my.cnf配置文件的,采用默认方式启动3个MySQL容器节点。
#!/bin/bash# MySQL的docker容器名
container_names=("mysql-1" "mysql-2" "mysql-3")# MySQL宿主机端口
host_ports=(3306 3307 3308)
host_ports_x=(13306 13307 13308)
container_ips=("172.20.0.11" "172.20.0.12" "172.20.0.13")# 批量创建MySQL容器
for i in ${!container_names[@]}
dodocker run --name ${container_names[$i]} \--net mysql-mgr --ip ${container_ips[$i]} \-d -p ${host_ports[$i]}:3306 -p ${host_ports_x[$i]}:33061 \-v ~/ws/mysql/data/${container_names[$i]}:/var/lib/mysql \-v ~/ws/mysql/secrets:/run/secrets \-e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root \mysql:latestecho "Mysql container : ${container_names[$i]} has been created!"
doneecho "Step 1: MySQL Cluster Initialization Completed!"
STEP2、创建MGR配置文件
这里只是准备三个节点的配置文件,用于第四步中更新配置文件。同样也是使用脚本来批量创建。
vian@txzq1899-ubuntu:~/ws/mysql/conf$ tree
.
├── mysql-1
│ └── my.cnf
├── mysql-2
│ └── my.cnf
└── mysql-3└── my.cnf3 directories, 3 files
请注意源码中,这个组名是一个UUID,大家自行生成即可。
group_replication_group_name="bc946766-0c46-11ef-a8e2-0242ac110002"
脚本源码如下:
#!/bin/bash# mysql安装路径
mysql_base_path=~/ws/mysql/conf/rm -rf ~/ws/mysql/conf/*# 节点数量和起始IP地址
node_count=3
base_ip="172.20.0.11"# 将起始IP地址分割成数组
IFS='.' read -ra ADDR <<< "$base_ip"# 计算最后一个IP片段的起始数字
last_octet=${ADDR[3]}# 创建配置文件
for ((i=1; i<=node_count; i++))
do# 计算每个节点的IP地址ip="172.20.0.$((last_octet + i - 1))"# MySQL配置路径conf_path=${mysql_base_path}/mysql-${i}# 创建目录mkdir -p ${conf_path}# 将节点配置写入对应的my.cnf文件cat > ${conf_path}/my.cnf <<EOF
[mysqld]
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"server_id=${i}
gtid_mode=ON
enforce_gtid_consistency=ONplugin_load_add='group_replication.so'group_replication_group_name="bc946766-0c46-11ef-a8e2-0242ac110002"
group_replication_start_on_boot=off
group_replication_local_address= "${ip}:33061"
group_replication_group_seeds= "172.20.0.$((last_octet)):33061,172.20.0.$((last_octet + 1)):33061,172.20.0.$((last_octet + 2)):33061"
group_replication_bootstrap_group=off
EOFdone
tree ~/ws/mysql/conf
echo "MGR配置文件创建完毕!"
STEP3、安装Group Replication插件
这一步是要在三个MySQL实例上安装group replication插件,需要在每一个MySQL实例的命令行执行安装指令。
mysql > install PLUGIN group_replication SONAME 'group_replication.so';
因为我们的MySQL实例部署在docker 容器中,所以需要通过docker exec 这个命令在容器内执行相应的SQL语句。
#mysql-1 是容器名称
#mysql,是容器中的mysql客户端,
#-u,-p mysql用户名,密码
#-e "sql command;",要执行的SQL
docker exec -it mysql-1 \
mysql -uroot -phelloworld -e "sql command;"
第三步的脚本源码如下:
#!/bin/bash# 用户信息设置
CONTAINER_NAMES=("mysql-1" "mysql-2" "mysql-3")# 从环境变量读取root密码
MYSQL_ROOT_PASSWORD=$(<~/ws/mysql/secrets/mysql-root)# 在每个MySQL实例上执行创建用户和设置权限的步骤
for CONTAINER in "${CONTAINER_NAMES[@]}"
dodocker exec -it $CONTAINER mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "install PLUGIN group_replication SONAME 'group_replication.so';"
doneecho "Done"
STEP4、更新MySQL配置文件
这一步比较简单,就是需要将第二步创建的配置文件,复制到容器中mysql配置文件存储的地方。
主要通过docker cp命令实现
docker cp /path/to/file/filename container_name:/path/in/container
批量更新脚本源码如下 :
#!/bin/bash# MySQL的docker容器名
container_names=("mysql-1" "mysql-2" "mysql-3")# 批量更新MGR配置文件
for i in ${!container_names[@]}
dodocker cp ~/ws/mysql/conf/${container_names[$i]}/my.cnf ${container_names[$i]}:/etc/mysql/conf.d
doneecho "Configuration update complete!"
STEP5、重启MySQL实例
更新完配置文件后需要批量重启MySQL实例,这个也比较简单,通过docker restart container_name来实现
脚本源码如下:
#!/bin/bash# MySQL的docker容器名
container_names=("mysql-1" "mysql-2" "mysql-3")# 批量重启MySQL容器
for i in ${!container_names[@]}
dodocker restart ${container_names[$i]}
doneecho "Container restart is complete!"
STEP6、创建Replication用户
这一步需要创建用于Replication的用户,授权等操作。和第三步类似,也是通过docker exec在容器内执行相应SQL语句。
具体的SQL语句参考官方文档:
- 【User Credentials For Distributed Recovery】
脚本源码如下 :
#!/bin/bash# 用户信息设置
USER='rpl_user'
PASSWORD=$(<~/ws/mysql/secrets/repl-user)
CONTAINER_NAMES=("mysql-1" "mysql-2" "mysql-3")# 从环境变量读取root密码
MYSQL_ROOT_PASSWORD=$(<~/ws/mysql/secrets/mysql-root)# 在每个MySQL实例上执行创建用户和设置权限的步骤
for CONTAINER in "${CONTAINER_NAMES[@]}"
dodocker exec -it $CONTAINER mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SET SQL_LOG_BIN=0;CREATE USER IF NOT EXISTS '$USER'@'%' IDENTIFIED BY '$PASSWORD';GRANT REPLICATION SLAVE, CONNECTION_ADMIN, BACKUP_ADMIN ON *.* TO '$USER'@'%';GRANT GROUP_REPLICATION_STREAM ON *.* TO '$USER'@'%';FLUSH PRIVILEGES;SET SQL_LOG_BIN=1;CHANGE REPLICATION SOURCE TO SOURCE_USER='$USER',SOURCE_PASSWORD='$PASSWORD' FOR CHANNEL 'group_replication_recovery';"
doneecho "Done"
STEP7、设置和开启组复制
这一步就是设置和开启组复制功能,也是需要在数据库中执行相应的SQL指令。
开启组复制的SQL指令参考官方文档:
- 【Bootstrapping the Group】
MySQL官方称之为引导,指引中有明确讲到:引导应该只由单个服务器完成,即启动群组的服务器,并且只能执行一次。
The process of starting a group for the first time is called bootstrapping. You use the group_replication_bootstrap_group system variable to bootstrap a group. The bootstrap should only be done by a single server, the one that starts the group and only once.
所以最终的脚本如下:
#!/bin/bash# 用户信息
USER='rpl_user'
PASSWORD=$(<~/ws/mysql/secrets/repl-user)
MYSQL_ROOT_PASSWORD=$(<~/ws/mysql/secrets/mysql-root)container_names=("mysql-1" "mysql-2" "mysql-3") # 所有容器的名称for CONTAINER in "${container_names[@]}"
doif [ "$CONTAINER" == "mysql-1" ]; then# 如果是master容器,执行bootstrapping命令echo "Initializing the master ($CONTAINER)..."docker exec -it $CONTAINER mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SET GLOBAL group_replication_bootstrap_group=ON;START GROUP_REPLICATION USER='$USER', PASSWORD='$PASSWORD';SET GLOBAL group_replication_bootstrap_group=OFF;"else# 如果是其它容器,则加入已有集群echo "Adding the slave ($CONTAINER) to the group..."docker exec -it $CONTAINER mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "START GROUP_REPLICATION USER='$USER', PASSWORD='$PASSWORD';"fi
doneecho "Group replication started for all nodes."
STEP8、检查MGR集群状态
这一步就是检查一下MGR集群的状态,也是通过执行MySQL指令的形式
脚本源码如下:
#!/bin/bash
MYSQL_ROOT_PASSWORD=$(<~/ws/mysql/secrets/mysql-root)
docker exec -it mysql-1 mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "SELECT * FROM performance_schema.replication_group_members;"
MySQL-MGR-Cluster搭建完成
大家可以在主库上进行一些数据库操作,看看从库是否能实现数据同步。
也可以通过docker stop关闭mysql-1节点(Master),看看MGR机制是否能完成主从的切换。
vian@txzq1899-ubuntu:~/ws/mysql/script$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84531a0b4f0d mysql:latest "docker-entrypoint.s…" 12 hours ago Up 12 hours 33060/tcp, 0.0.0.0:3308->3306/tcp, :::3308->3306/tcp, 0.0.0.0:13308->33061/tcp, :::13308->33061/tcp mysql-3
f91d0848fac2 mysql:latest "docker-entrypoint.s…" 12 hours ago Up 12 hours 33060/tcp, 0.0.0.0:3307->3306/tcp, :::3307->3306/tcp, 0.0.0.0:13307->33061/tcp, :::13307->33061/tcp mysql-2
e7f3589bdeac mysql:latest "docker-entrypoint.s…" 12 hours ago Up 12 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp, 0.0.0.0:13306->33061/tcp, :::13306->33061/tcp mysql-1
vian@txzq1899-ubuntu:~/ws/mysql/script$ vian@txzq1899-ubuntu:~/ws/mysql/script$ ./8.check-mgr-cluster-member-status.sh
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | MEMBER_COMMUNICATION_STACK |
+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+
| group_replication_applier | 122dd904-0c97-11ef-a155-0242ac14000b | e7f3589bdeac | 3306 | ONLINE | PRIMARY | 8.4.0 | XCom |
| group_replication_applier | 125ddf4e-0c97-11ef-a1fb-0242ac14000c | f91d0848fac2 | 3306 | ONLINE | SECONDARY | 8.4.0 | XCom |
| group_replication_applier | 12a8bbac-0c97-11ef-a14f-0242ac14000d | 84531a0b4f0d | 3306 | ONLINE | SECONDARY | 8.4.0 | XCom |
+---------------------------+--------------------------------------+--------------+-------------+--------------+-------------+----------------+----------------------------+#所有的脚本如下,只需要按次序依次执行即可。
vian@txzq1899-ubuntu:~/ws/mysql/script$ ll
总计 56
drwxrwxr-x 2 vian vian 4096 5月 8 01:45 ./
drwxrwxr-x 6 vian vian 4096 5月 8 01:34 ../
-rwxr-xr-x 1 vian vian 766 5月 8 01:26 1.init-cluster.sh*
-rwxr-xr-x 1 vian vian 1210 5月 8 01:45 2.init-conf.sh*
-rwxrwxr-x 1 vian vian 450 5月 7 22:52 3.install-gr-plugin.sh*
-rwxr-xr-x 1 vian vian 300 5月 8 01:11 4.update-mgr-conf.sh*
-rwxr-xr-x 1 vian vian 260 5月 8 01:13 5.restart-cluster.sh*
-rwxrwxr-x 1 vian vian 861 5月 7 22:52 6.create-repl-user.sh*
-rwxr-xr-x 1 vian vian 989 5月 8 00:58 7.setup-group-replication.sh*
-rwxr-xr-x 1 vian vian 207 5月 8 01:41 8.check-mgr-cluster-member-status.sh*
-rwxr-xr-x 1 vian vian 118 5月 7 17:25 cleanup.sh*
-rwxr-xr-x 1 vian vian 88 5月 7 17:07 remove-cluster.sh*
-rwxr-xr-x 1 vian vian 228 5月 8 01:37 start-cluster.sh*
-rwxr-xr-x 1 vian vian 228 5月 8 01:38 stop-cluster.sh*
留一个思考题,MGR只是保证了MySQL集群的高可用,MGR能自动进行主从的切换,但我们的应用是感知不到这个动作的发生的。一般来讲应用通过域名连接到这个MySQL-MGR-集群,域名又是指向的哪里?如何保证应用能准确的连接到新的主库上呢?
关注我的公众号
欢迎大家关注、点赞、转发,一起交流软件开发、架构设计、云原生技术。