背景
近期在进行sharding-jdbc的试验,上周学习了分库分表的配置,近期在学习读写分离的配置。验证读写分离需要搭建一个docker环境下的mysql主从配置。本来以为是个挺简单的事情,拷贝一段配置文件就能跑起来,测试过程中发现还是有很多坑。网上的示例只负责把主从实例拉起来,主从复制都要人工登录从库执行命令。想通过docker的机制自动执行start slave/start replica
命令,遇到各种困难,没有得到实际有效的解决,最后用一个脚本,实现了docker拉起来自动启动同步复制,还是有些粗糙,后续有更好的解决方案后再来更新。
试验过程
用docker-compose来实现主从实例一起启动,主要配置文件如下:
# compose.yaml
version: "3"
services:
mysql-master: &mysql-master
image: mysql
container_name: mysql-master
security_opt:
- seccomp:unconfined
restart: unless-stopped
env_file:
- .env
environment:
- MYSQL_ROOT_PASSWORD=${MASTER_MYSQL_ROOT_PASSWORD}
- TZ=Asia/Shanghai
ports:
- "13306:3306"
expose:
- "3306"
volumes:
- ./mysql-master-conf:/etc/mysql/conf.d/
- ./mysql-master-data:/var/lib/mysql
- ./init-master/:/docker-entrypoint-initdb.d/
healthcheck:
disable:true
healthcheck:
test: ["CMD-SHELL", '/docker-entrypoint-initdb.d/init.sh']
interval: 10s
timeout: 5s
retries: 50
mysql-slave: &mysql-slave
<<: *mysql-master
container_name: mysql-slave
environment:
- MYSQL_ROOT_PASSWORD=${NODE_MYSQL_ROOT_PASSWORD}
- MASTER_MYSQL_ROOT_PASSWORD=${MASTER_MYSQL_ROOT_PASSWORD}
- TZ=Asia/Shanghai
ports:
- "23306:3306"
depends_on:
- mysql-master
# 试验环境
# command:
# - sh
# - -c
# - |
# echo "13456"
# sleep 3
# echo "23456"
# sleep 2
# echo "34567"
volumes:
- ./mysql-slave-conf:/etc/mysql/conf.d/
- ./mysql-slave-data:/var/lib/mysql
- ./init-slave/:/docker-entrypoint-initdb.d/
启动两个实例,分别为mysql-master,mysql-slave。分别用master.cnf和slave.cnf作为配置文件,两个配置文件的内容如下:
# master.cnf
[mysqld]
max_connections = 2000
default-time_zone='+8:00'
collation-server=utf8mb4_unicode_ci
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
#gtid:
server_id = 1 #服务器id
gtid_mode = on #开启gtid模式
enforce_gtid_consistency = on #强制gtid一致性,开启后对于特定create table不被支持
#binlog
log_bin = mysql-binlog
log_replica_updates = on
binlog_format = row #强烈建议,其他格式可能造成数据不一致
#relay log
#master-info-repository = TABLE
#skip_replica_start = 1
推荐使用gtid_mode,关于gtid_mode详情,参见mysql_官方文档。
# slave.cnf
[mysqld]
max_connections = 2000
default-time_zone = '+8:00'
collation-server=utf8mb4_unicode_ci
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
#GTID:
server_id = 2 #服务器id
gtid_mode = on #开启gtid模式
enforce_gtid_consistency = on #强制gtid一致性,开启后对于特定create table不被支持
#binlog
log_bin =mysql-binlog
log_replica_updates = on
binlog_format = row #强烈建议,其他格式可能造成数据不一致
#relay log
#skip_replica_start = 1
#relay_log_info_repository = TABLE
#master_info_repository = TABLE
#relay_log_recovery = on
read_only = on #设置只读
主库和从库分别需要执行一些初始化命令,创建基本数据库和用户,对应的命令如下:
# init.sql master
-- 添加用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl';
-- 添加同步权限
grant replication slave on *.* to 'repl'@'%' with grant option;
-- 刷新权限
flush privileges;
create database repl;
# init.sql slave
RESET REPLICA;
#设置复制参数
CHANGE REPLICATION SOURCE TO SOURCE_HOST='mysql-master',
SOURCE_PORT=3306,
GET_SOURCE_PUBLIC_KEY=1,
SOURCE_SSL=1,
SOURCE_AUTO_POSITION=1;
START REPLICA user='repl' password='repl';
SHOW REPLICA STATUS\G;
注:mysql8之后,原有的一些命令被废弃,换成了以上的名字,分别对应 RESET SLAVE;CHANGE MASTER;START SLAVE;SHOW SLAVE STATUS
等,同时,mysql8启用了新的安全认证方法,需要加上GET_SOURCE_PUBLIC_KEY,SOURCE_SSL
等启动SSL通讯。
一些坑
其中的一些坑已经在上面的配置中加上了,如使用start slave
命令会有报警,不加SOURCE_SSL
会报错等,不再赘述。现在的配置还有一个比较严重的问题,在docker启动后(启动命令docker-compose up -d
),从库的配置,永远都是没有启动的状态,查看slave的状态如下:
Slave_IO_State:
Master_Host: mysql-master
Master_User:
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-binlog.000010
Read_Master_Log_Pos: 196
Relay_Log_File: dd16c078a7fb-relay-bin.000003
Relay_Log_Pos: 377
Relay_Master_Log_File: mysql-binlog.000010
Slave_IO_Running: No
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 196
Relay_Log_Space: 0
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: Yes
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 0
Master_UUID: 9bf86368-0c7c-11ec-a1af-0242c0a87002
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set: 9bf86368-0c7c-11ec-a1af-0242c0a87002:1-11,
9d482826-0c7c-11ec-9ca8-0242c0a87003:1-5
Auto_Position: 1
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
Master_public_key_path:
Get_master_public_key: 1
Network_Namespace:
看slave的日志,有各种问题,包括连不上主库,从库的relay_log不对等等,尝试了各种方案,如healthy_check,denpends_on,command等,始终没有得到有效的解决,只有连上mysql_slave,手工执行reset replica;start replica user='repl' password='repl'
后从库正常启动复制。
分析原因,docker-compose的depends_on无法判断所依赖容易的精确状态(具体判断了什么待研究),网上的解决方案,如wait-for-it
等又需要容器里有nc
等命令,比较折腾,最终用了一个比较粗糙的办法来启动mysql主从集群。
解决方案
需要新增两个脚本,check.sh 和 start.sh,check.sh用mysqladmin ping
命令来检查主从mysql是否均已正确启动,start.sh负责在启动好的从库上执行启动复制的命令,需要把密码等敏感信息写在脚本中,有一些缺憾,脚本内容如下:
# check.sh
while ! mysqladmin ping -h127.0.0.1 -P13306 --user=repl --password=repl
do
echo "master is not active wait..."
sleep 3
done
echo "master is active,now start slave"
while ! mysqladmin ping -h127.0.0.1 -P23306 --user=root --password=123456
do
echo "slave is not active wait..."
sleep 3
done
echo "master and slave ar all active,now start REPLICA"
# start.sh
#!/bin/sh
#启动docker
docker-compose up -d
#检查启动状态
./check.sh
#查看主从复制状态
mysql -h127.0.0.1 -uroot -P23306 --password=123456 --silent --execute="reset replica;start replica user='repl' password='repl'"
mysql -h127.0.0.1 -uroot -P23306 --password=123456 --silent --execute="show replica status\G"
经过几次验证,均能够正常启动。
注:两个脚本无需放到容器中,在宿主机上执行就可以了,如果是k8s环境,估计还搞不了。🤣。
总结
没有经过试验,大原理总是正确的,问题总是未知的。本方案的自动化程度很低,脚本也没有明确判断状态,强制执行,需要不断完善,如果有更好的方案,欢迎交流。




