环境
| 服务 | 版本 |
|---|---|
| centos | 7.9 |
| mysql | 5.7 |
| docker | 26.1.4 |
| docker-compose | 2.27.1 |
使用docker-compose结合脚本进行自动化部署mysql一主一从
主节点即master节点容器
从节点即slave节点容器
'双主双从'等待未来博客
思路
首先理清一般非容器化mysql主从部署思路:
- 在多主机上安装mysql(统一版本)
- 修改配置文件(区分master与slave)
- 主节点需要创建复制用户并授权,进行测试
- 从节点需要指定主节点并开启复制线程
但是容器化就要有N个问题,主要集中在3,4点上
- 如何保证主节点先于从节点开启?
- 如何从主节点容器获取binlog的文件与断点?
- 如何非交互的使用mysql?
- 如何进行mysql初始化?
解答:
1.如何保证主节点先于从节点开启?
这个问题如果单独使用dockerfile或直接docker启动其实没什么问题,只要先启动master容器就行了,但是如果使用docker-compose呢?我的第一个想法就是使用depends_on,salve容器依赖master容器不就行了吗?那就太easy,不会放这里了,确实可以使用depends_on设置优先级,但是docker-compose启动了容器不代表服务已经启动了!
举个简单的例子,如果docker运行比较多就会发现有的容器刚启动还没ps就挂了,有的启动后需要等一会才挂掉,为什么不直接挂断?这是因为容器虽然启动了,但是服务还正在启动,然后发现启动不了,容器就挂了
docker-compose这里也是一样,depends_on只能设置master优先启动,但此时服务可能正在启动,此时slave如果直接设置复制change master..会直接报错(经测试,即使设置了depends_on,master服务启动也比slave服务慢!)
实际上需要的不是master容器先于slave容器启动,而是master的mysql服务先于slave的mysql服务启动,我想到的最简单的方法就是在slave的初始化脚本中使用sleep,这样就可以强制salve的mysql服务启动后于master的mysql服务启动
补:仅使用depends不能保证服务完全启动,只能保证启动顺序,但是depends_on是可以设置需要被依赖容器完全启动才启动依赖容器
2.如何从主节点获取binlog的文件名与断点并传入从节点?
通过mysql的-h可以通过容器名实现跨节点的登录mysql,然后使用show master status结合三剑客过滤出FILE与POSTION字段,这样就可以直接在从节点直接获取binlog的文件名与断点
3.如何非交互的使用mysql?
通过mysql的-e参数可以实现非交互式的操作mysql,需要注意的是,如果需要连续输入多个命令可以使用双引号将SLQ扩起来,这样就不用使用多个mysql命令重复登录操作就可以一次实现操作了
4.如何进行mysql初始化?
使用mysql的初始化脚本,初始化脚本docker-entrypoint.sh会执行docker-entrypoint-initdb.d中的脚本,只要将脚本传入其中即可作为初始化脚本
最终使用文件
- master的配置文件my1.cnf
- slave的配置文件my2.cnf
- docker-compose.yml
- 初始化脚本init.sh
- 启动命令脚本START
实现
1.master的配置文件my1.cnf
[mysqld]
datadir=/var/lib/mysql
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
server_id=1
#开启binlog日志
log_bin=mysql-bin.log
binlog_format=ROW
max_binlog_size=500M
expire_logs_days=7
slave_skip_errors=1062
log-slave-updates=1
auto-increment-increment=2
auto-increment-offset=1
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
2.slave的配置文件my2.cnf
[mysqld]
datadir=/var/lib/mysql
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
#server_id必须与master不同
server_id=2
#开启只读
read_only = 13.docker-compose.yml
version: "3.8"
services:
master:
image: mysql:5.7
container_name: mysql_master
#build:
restart: always
ports:
- 3306
volumes:
- data1:/var/lib/mysql
- /app/docker/mysql_cluster/my1.cnf:/etc/mysql/conf.d/my.cnf
- /app/docker/mysql_cluster/init.sh:/docker-entrypoint-initdb.d/init.sh
networks:
- mysql_cluster
env_file:
- env
environment:
- ROLE=master
#command:
slave:
image: mysql:5.7
container_name: mysql_slave
#build:
restart: always
ports:
- 3306
#注意,depends_on是可以实现完全服务启动再往后执行的,本次使用sleep(脚本中)实现
depends_on:
- master
volumes:
- data2:/var/lib/mysql
- /app/docker/mysql_cluster/my2.cnf:/etc/mysql/conf.d/my.cnf
- /app/docker/mysql_cluster/init.sh:/docker-entrypoint-initdb.d/init.sh
networks:
- mysql_cluster
env_file:
- env
#传入ROLE与MASTER
environment:
- ROLE=slave
- MASTER=mysql_master
#command:
networks:
mysql_cluster:
driver: bridge
volumes:
data1: {}
data2: {}4.初始化脚本init.sh(适用于主+从节点)
#!/bin/bash
#需要传入变量ROLE(本机身份,用于判断),MASTER(从节点需要传入用于指定主节点容器名)
#错误判断
function test_order {
if [ $? -ne 0 ]; then
echo "命令执行错误"
exit 1
fi
}
function MASTER {
#创建用户(在没有传入MYSQL_USER和MYSQL_PASSWORD时启用)
#CREATE_SQL="create user "slave"@'%' identified by '123';"
#授权复制用户
GRANT_SQL="grant replication slave on *.* to "${MYSQL_USER}"@'%';"
#刷新权限
FLUSH_SQL="flush privileges;"
mysql -u 'root' -p"${MYSQL_ROOT_PASSWORD}" -e "${GRANT_SQL} ${FLUSH_SQL}"
test_order
}
function SLAVE {
sleep 10
SHOW_STATUS_SQL1="SHOW MASTER STATUS;"
FILE="$(mysql -u'root' -p"${MYSQL_ROOT_PASSWORD}" -h ${MASTER} -e"${SHOW_STATUS_SQL1}"|grep mysql|awk '{print $1}')" &>>/dev/stdout
POSITION="$(mysql -u'root' -p"${MYSQL_ROOT_PASSWORD}" -h ${MASTER} -e"${SHOW_STATUS_SQL1}"|grep mysql|awk '{print $2}')" &>>/dev/stdout
#从节点开启复制
CHANGE_MASTER_SQL="change master to master_host='${MASTER}',master_user='${MYSQL_USER}',master_password='${MYSQL_PASSWORD}',master_log_file='${FILE}',master_log_pos=${POSITION};"
START_SLAVE="start slave;"
#指定主节点
mysql -u 'root' -p"${MYSQL_ROOT_PASSWORD}" -e "${CHANGE_MASTER_SQL} ${START_SLAVE}"
test_order
}
#角色为master
if [ "${ROLE}" = "master" ]; then
MASTER
#角色为slave
elif [ "${ROLE}" = "slave" ]; then
SLAVE
else
exit 1
fi5.环境变量文件env(通用变量,主+从挂载)
#env.txt
MYSQL_ROOT_PASSWORD=MyStr0ngP@ssw0rd!
MYSQL_USER=slave
MYSQL_PASSWORD=123
6.启动命令脚本START
#!/bin/bash
docker compose down &>/dev/null
docker volume rm mysql_cluster_data1
docker volume rm mysql_cluster_data2
docker compose up -d
docker ps7.一点注意事项
- master节点需要传入ROLE用于判断角色
- slave节点需要传入ROLE用于判断角色,需要传入MASTER用于确定master节点的容器名
- 如果执行出问题,可以尝试进入容器手动执行init.sh初始化脚本
启动START脚本即可
最终总结
搭了两天才搭起来,一方面是mysql好久没搭过主从,另一方面是之前没有接触过mysql容器的自动化,几乎都是花时间在脚本的调错上
配置过程中的错误参考mysql主从自动容器化(2) 错误
已推送至gitee仓库.可直接通过gitee仓库下载配置文件 my-sqlauto-replica仓库
