本文是Docker 下 MySQL 主从三部曲系列的第二篇, 上一篇Docker 下 MySQL 主从三部曲之一: 极速体验我们轻而易举的搭建了 MySQL 主从同步环境, 凭借的是一个 docker-compose.yml 脚本, 今天我们一起来看看这个脚本相关的技术细节, 学习如何制作支持 MySQL 主从同步镜像;
镜像要解决的问题
在开始制作镜像前, 我们先列出镜像要解决的问题:
主库镜像:
1. 开启 bin log,ID 是多少, 这些问题最好做成可以接受外部传入, 交给镜像的使用者去设置, 因为多个从库的时候是难以在镜像中分配 ID 的;
2. 要创建同步账户, 并授权;
3. 刷新状态;
从库镜像:
1. 从库的 ID 设置, 像主库一样支持外部传入配置文件, 由镜像的使用者来设置;
2. 执行设置同步参数的 SQL;
3. 执行启动同步的 SQL;
综上所述, 一共有两个问题需要我们重点解决:
1. 如何让镜像接受外部传入的配置文件;
2. 如何让容器自动执行指定的 SQL;
第一个问题, 可以修改 mysql 的配置文件 my.cnf, 增加一个存放配置文件目录, 镜像的使用者将自己的配置文件放在这个目录, MySQL 启动的时候就能取到里面的配置文件了;
第二个问题, MySQL 官方镜像中有个 docker-entrypoint.sh 文件, 这里面加入的 SQL 可以在容器启动后自动执行, 我们就把 SQL 写在这里面;
梳理制作过程
先将步骤梳理出来, 这样后面思路更清晰, 主库和从库的镜像制作步骤是一样的, 不同之处在替换官方的 my.cnf,docker-entrypoint.sh 文件内容不同:
1. 创建 Dockerfile, 父镜像是 MySQL 官方版本: mysql:5.7.21;
2. 用修改过的 my.cnf 文件替换原有的同名文件;
3. 用修改过的 docker-entrypoint.sh 文件替换原有的同名文件;
所有脚本的源码
您可以在 GitHub 下载本章涉及的脚本和配置文件, 地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在 GitHub 上的主页 |
git 仓库地址 (https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https 协议 |
git 仓库地址 (ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh 协议 |
这个 git 项目中有多个目录, 本次所需的资源放在 mysql-master-slave 目录下, 如下图红框所示:
打开 mysql-master-slave 文件夹里面有两个文件夹, docker-compose 中是上一章用到的 docker-compose.yml 文件以及各容器的配置文件, docker-build 中有两个文件夹, 分别存放了制作主库和从库所需的全部材料;
Dockerfile
主库镜像的 Dockerfile 内容:
- # Docker file from bolingcavalry # VERSION 0.0.1
- # Author: bolingcavalry
- # 基础镜像
- FROM mysql:5.7.21
- # 作者
- MAINTAINER BolingCavalry <zq2599@gmail.com>
- # 定义配置文件存放目录
- ENV BASE_CONF_PATH /etc/mysql
- # 定义存放外部配置文件的文件夹名称
- ENV EXTEND_CONF_FILE_FOLDER_NAME extend.conf.d
- # 定义 conf 文件名
- ENV CONF_FILE_NAME my.cnf
- # 定义 entrypoint 文件所在路径
- ENV ENTRY_FILE_PATH /usr/local/bin
- # 定义 entrypoint 文件名
- ENV ENTRY_FILE_NAME docker-entrypoint.sh
- # 定义 entrypoint 的软链接文件名
- ENV ENTRY_FILE_SOFT_LINK_NAME entrypoint.sh
- # 删除原有的配置文件
- RUN rm $BASE_CONF_PATH/$CONF_FILE_NAME
- # 复制新的配置文件
- COPY ./$CONF_FILE_NAME $BASE_CONF_PATH/
- # 给 shell 文件赋读权限
- RUN chmod a+r $BASE_CONF_PATH/$CONF_FILE_NAME
- # 创建存放外部配置文件的目录
- RUN mkdir $BASE_CONF_PATH/$EXTEND_CONF_FILE_FOLDER_NAME
- # 删除原有的软链接
- RUN rm -rf /$ENTRY_FILE_SOFT_LINK_NAME
- # 删除原有的文件
- RUN rm -rf $ENTRY_FILE_PATH/$ENTRY_FILE_NAME
- # 将 entrypoint 文件复制到原有位置
- COPY ./$ENTRY_FILE_NAME $ENTRY_FILE_PATH/
- # 给 entrypoint 文件赋读权限
- RUN chmod a+x $ENTRY_FILE_PATH/$ENTRY_FILE_NAME
- # 建立软链接
- RUN ln -s $ENTRY_FILE_PATH/$ENTRY_FILE_NAME /$ENTRY_FILE_SOFT_LINK_NAME
- ENTRYPOINT ["docker-entrypoint.sh"]
- EXPOSE 3306
- CMD ["mysqld"]
上述脚本内有详细注释, 就不多赘述了, 以下三点需要注意:
1. /etc/mysql/extend.conf.d 是在 my.cnf 中定义的, 用来存放自定义配置文件的目录, 需要创建好, 否则 mysql 启动会报错;
2. 新的 docker-entrypoint.sh 文件, 记得增加可执行权限, 如果不加的话, 在 vmware 的 ubuntu 上可以创建容器成功, 在真正的 ubuntu 电脑上创建容器会失败, 提示找不到可执行文件;
3. entrypoint.sh 只是个软链接, 真正的脚本是 docker-entrypoint.sh, 官方镜像是这么设置的, 在此脚本中和官方保持一致;
接下来, 我们看看用来覆盖官方镜像中的 my.cnf 和 docker-entrypoint.sh 的文件的具体内容;
配置文件 my.cnf
主库和从库的 my.cnf 是一模一样的, 内容如下所示:
- # Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; version 2 of the License.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
!includedir /etc/mysql/extend.conf.d/
与 MySQL 官方镜像中的同名文件相比, 只在尾部增加了一行! includedir /etc/mysql/extend.conf.d/, 也就是说 / etc/mysql/extend.conf.d / 这个文件夹下的配置文件也是 MySQL 的配置文件;
自动执行脚本 docker-entrypoint.sh
容器启动后脚本 docker-entrypoint.sh 会被执行, 我们将需要自动执行的 SQL 放进来, 添加 SQL 的位置在下图红框和绿框之间的位置:
对于主库, 我们要加入的内容如下:
- echo "CREATE USER'$MYSQL_REPLICATION_USER'@'%'IDENTIFIED BY'$MYSQL_REPLICATION_PASSWORD';" | "${mysql[@]}"
- echo "GRANT REPLICATION SLAVE ON *.* TO'$MYSQL_REPLICATION_USER'@'%'IDENTIFIED BY'$MYSQL_REPLICATION_PASSWORD';" | "${mysql[@]}"
- echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}"
上面三行 SQL 的功能分别是创建用户, 授权, 刷新, 用到了两个环境变量: MYSQL_REPLICATION_USER,MYSQL_REPLICATION_PASSWORD, 在 docker-compose.yml 中传入了这两个变量的值;
对于从库, 我们要加入的内容如下:
- if [ "$MYSQL_MASTER_SERVICE_HOST" -a "$MYSQL_REPLICATION_USER" -a "$MYSQL_REPLICATION_PASSWORD" ]; then
- echo "STOP SLAVE;" | "${mysql[@]}"
- if [ "$MASTER_LOG_FILE" -a "$MASTER_LOG_POS" ]; then
- echo "CHANGE MASTER TO master_host='$MYSQL_MASTER_SERVICE_HOST', master_user='$MYSQL_REPLICATION_USER', master_password='$MYSQL_REPLICATION_PASSWORD', master_log_file='$MASTER_LOG_FILE', master_log_pos=$MASTER_LOG_POS ;" | "${mysql[@]}"
- else
- echo "CHANGE MASTER TO master_host='$MYSQL_MASTER_SERVICE_HOST', master_user='$MYSQL_REPLICATION_USER', master_password='$MYSQL_REPLICATION_PASSWORD';" | "${mysql[@]}"
- fi
- echo "START SLAVE;" | "${mysql[@]}"
- fi
上面的脚本中, 先检查 MYSQL_MASTER_SERVICE_HOST,MYSQL_REPLICATION_USER,MYSQL_REPLICATION_PASSWORD 这三个变量是否同时存在, 如果存在, 就执行设置从库同步的 SQL, 执行前再检查是否存在 MASTER_LOG_FILE 和 MASTER_LOG_POS 参数, 用于设置 bin log 文件名和同步的开始位置, 如果有这两个参数就在执行 SQL 的时候带上, 否则就是默认同步所有可以同步的写操作;
设置完毕后, 再执行 START SLAVE; 开启同步;
至此, 镜像脚本的开发工作就完成了, 执行命令 docker build -t bolingcavalry/mysql-master:0.0.1 . 完成镜像制作;
让我们再看看 docker-compose.yml 吧, 通过这个脚本我们创建了容器, 并且给它们做了一些设置;
分析 docker-compose.yml
docker-compose.yml 内容如下:
- version: '2'
- services:
- master:
- image: bolingcavalry/mysql-master:0.0.1
- environment:
- MYSQL_ROOT_PASSWORD: 888888
- MYSQL_REPLICATION_USER: rep
- MYSQL_REPLICATION_PASSWORD: 888888
- volumes:
- - ./master:/etc/mysql/extend.conf.d
- restart: always
- slave0:
- image: bolingcavalry/mysql-slave:0.0.1
- depends_on:
- - master
- links:
- - master:masterhost
- environment:
- MYSQL_ROOT_PASSWORD: 888888
- MYSQL_MASTER_SERVICE_HOST: masterhost
- MYSQL_REPLICATION_USER: rep
- MYSQL_REPLICATION_PASSWORD: 888888
- volumes:
- - ./slave0:/etc/mysql/extend.conf.d
- restart: always
- slave1:
- image: bolingcavalry/mysql-slave:0.0.1
- depends_on:
- - master
- links:
- - master:masterhost
- environment:
- MYSQL_ROOT_PASSWORD: 888888
- MYSQL_MASTER_SERVICE_HOST: masterhost
- MYSQL_REPLICATION_USER: rep
- MYSQL_REPLICATION_PASSWORD: 888888
- volumes:
- - ./slave1:/etc/mysql/extend.conf.d
- restart: always
该脚本定义了三个容器: 一主二从;
主库的镜像是 bolingcavalry/mysql-master:0.0.1, 从库的镜像是 bolingcavalry/mysql-slave:0.0.1, 都是自制的;
主库的环境变量 MYSQL_REPLICATION_USER,MYSQL_REPLICATION_PASSWORD 是用来设置主从同步账号的;
docker-compose.yml 所在目录下的 master 文件夹被映射到容器的 / etc/mysql/extend.conf.d 目录, 这个目录在 my.cnf 中已经被指定为存放配置文件的目录, 所以我们在 master 文件夹下创建的 master.cnf 文件会被 MySQL 识别和加载, 此文件中有 server id, 以及开启 bin log 的设置, 如下:
- [mysqld]
- log-bin=mysql-bin
- server-id=1
5. 从库的设置和主库差不多, 有个特别之处就是通过 link 参数引用了 master 容器, 并且 MYSQL_MASTER_SERVICE_HOST 参数的值就是 link 的值 masterhost, 这样在 docker-entrypoint.sh 脚本中, 使用 MYSQL_MASTER_SERVICE_HOST 变量的时候, 其实就是 master 容器的 IP 地址;
至此, 搭建 MySQL 主从同步环境所做的全部工作已经完成, 但是有个小问题还没有搞清楚: 在Docker 下手工配置 MySQL 主从一文中我们记录下主库的 bin log 文件名和日志位置, 然后设置从库时会用到, 但是本章却没有用到这个参数, 此参数究竟有什么影响呢? 除了看官方文档的参数说明, 我们还可以通过实战来验证此参数的作用, 这些就留给下一个章节吧, 作为Docker 下 MySQL 主从三部曲的终篇;
前文链接
Docker 下 MySQL 主从三部曲之一: 极速体验
后文链接
Docker 下 MySQL 主从三部曲之三: binlog 日志参数实战
来源: https://blog.csdn.net/boling_cavalry/article/details/79775617