暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

在docker环境下mysql主从配置

读读书写写代码 2021-09-26
772

背景

近期在进行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环境,估计还搞不了。🤣。

总结

没有经过试验,大原理总是正确的,问题总是未知的。本方案的自动化程度很低,脚本也没有明确判断状态,强制执行,需要不断完善,如果有更好的方案,欢迎交流。


文章转载自读读书写写代码,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论