Kubernetes一键部署Mycat+Mysql主从集群

Kubernetes一键部署一主一从,读写分离,自动切换的mycat+mysql架构,其中mycat配置文件、mysqsl数据文件的volume挂载未涉及,可根据实际情况进行修改。所有代码参照github k8smysqlcluster。后期进行双主双从、mycat高可用的扩展。


1. Mysql复制原理

Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。
请注意当你进行复制时,所有对复制中的表的更新必须在主服务器上进行。否则,你必须要小心,以避免用户对主服务器上的表进行的更新与对从服务器上的表所进行的更新之间的冲突。

1.1 mysql支持的复制类型:

  • 基于语句的复制:在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。一旦发现没法精确复制时, 会自动选着基于行的复制。
  • 基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持
  • 混合类型的复制:默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。

1.2 复制解决的问题

MySQL复制技术有以下一些特点:

  • 数据分布 (Data distribution )
  • 负载平衡(load balancing)
  • 备份(Backups)
  • 高可用性和容错行(High availability and failover)

1.3 复制如何工作

整体上来说,复制有3个步骤:

  • master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
  • slave将master的binary log events拷贝到它的中继日志(relay log);
  • slave重做中继日志中的事件,将改变反映它自己的数据。

下图描述了复制的过程:

该过程的第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,master通知存储引擎提交事务。
下一步就是slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。
SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。
此外,在master中也有一个工作线程:和其它MySQL的连接一样,slave在master中打开一个连接也会使得master开始一个线程。复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作。


2. Mycat介绍

从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。

MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。

详细内容可参照Mycat官网
Mycat高可用方案:

Mycat双主架构:


3. Kubernetes部署基于Mycat的读写分离,自动切换的主从Mysql架构

Build出mysql-master和mysql-slave的镜像实现主从配置,build mycat镜像实现读写分离和自动切换配置,基于rc和svc yaml文件使用kubernetes进行快速部署。

3.1 mysql-master

基于官方mysql镜像,修改mysqld.cnf添加server-id和binlog,修改docker-entrypoint.sh在mysql上创建同步账号并授权。

mysql-master Dockerfile
1
2
3
4
5
FROM mysql

RUN sed -i '/\[mysqld\]/a server-id=1\nlog-bin=mysql-bin' /etc/mysql/mysql.conf.d/mysqld.cnf

RUN echo "$(tac /usr/local/bin/docker-entrypoint.sh | sed "28a echo \'FLUSH PRIVILEGES;\' | \"\$\{mysql\[@\]\}\"\necho \"GRANT REPLICATION SLAVE ON *.* TO \'\$MYSQL_REPLICATION_USER\'@\'%\' IDENTIFIED BY \'\$MYSQL_REPLICATION_PASSWORD\';\" | \"\$\{mysql\[@\]\}\"" | tac)" > /usr/local/bin/docker-entrypoint.sh

执行如下命令本地build mysql-master镜像;

1
docker build -t mysql-master -f ./Dockerfile .

3.2 mysql-slave

基于官方mysql镜像,修改mysqld.cnf添加server-id和binlog,修改docker-entrypoint.sh添加主机信息并开启从机模式。

mysql-slave Dockerfile
1
2
3
4
5
FROM mysql

RUN RAND="$(date +%s | rev | cut -c 1-2)$(echo ${RANDOM})" && sed -i '/\[mysqld\]/a server-id='$RAND'\nlog-bin=mysql-bin' /etc/mysql/mysql.conf.d/mysqld.cnf

RUN echo "$(tac /usr/local/bin/docker-entrypoint.sh | sed "28a echo \"START SLAVE;\" | \"\$\{mysql\[@\]\}\"\necho \"CHANGE MASTER TO master_host=\'\$MYSQL_MASTER_SERVICE_HOST\', master_user=\'\$MYSQL_REPLICATION_USER\', master_password=\'\$MYSQL_REPLICATION_PASSWORD\';\" | \"\$\{mysql\[@\]\}\"\necho \"STOP SLAVE;\" | \"\$\{mysql\[@\]\}\"" | tac)" > /usr/local/bin/docker-entrypoint.sh

上面slave的配置中,master_host一项用的是$MYSQL_MASTER_SERVICE_HOST,这个环境变量(enviromnent variable)是由k8s生成的。
k8s的service创建后,会自动分配一个cluster ip,这个cluster ip是动态的,我们没法直接使用或硬编码,k8s为了service对容器的可见,生成了一组环境变量,这些环境变量用于记录service name到cluster ip地址的映射关系,这样容器中就可以使用这些变量来使用service。(类似的,Docker中提供了links。)

举例:如果service的名称为foo,则生成的环境变量如下:
FOO_SERVICE_HOST
FOO_SERVICE_PORT
更多介绍请参考k8s官方资料:http://kubernetes.io/docs/user-guide/container-environment/

执行如下命令本地build mysql-slave镜像;

1
docker build -t mysql-slave -f ./Dockerfile .

3.3 mycat

基于gaven/mycat镜像,修改schema.xml进行一主一从读写分离自动切换配置,修改server.xml添加db用户名密码等。

mycat Dockerfile
1
2
3
4
5
6
7
FROM gaven/mycat

COPY schema.xml /usr/local/mycat/conf/schema.xml
COPY server.xml /usr/local/mycat/conf/server.xml
COPY log4j2.xml /usr/local/mycat/conf/log4j2.xml
COPY entrypoint.sh /root/
CMD ["/root/entrypoint.sh"]

配置文件参照github k8smysqlcluster

执行如下命令本地build mycat镜像;

1
docker build -t jacob/mycat -f ./Dockerfile .

3.4 kubernetes files

rc和svc文件参照github k8smysqlcluster

执行如下命令启动,注意顺序为先启动mysql-master,再启动mysql-slave,最后启动mycat;

1
2
3
4
5
6
kubectl create -f ./rc-mysql-master
kubectl create -f ./svc-mysql-master
kubectl create -f ./rc-mysql-slave
kubectl create -f ./svc-mysql-slave
kubectl create -f ./rc-mycat
kubectl create -f ./svc-mycat


参照资料:
利用Kubernetes搭建mysql主从复制集群
基于Mycat的MySQL主从读写分离及自动切换的docker实现
基于Docker容器的MyCat高可用方案
centos7配置mysql主从复制
Mycat官网
mycat实现mysql读写分离,热切换,集群
使用mycat实现mysql读写分离以及主备自动切换模式
mycat读写分离配置