最近因为公司项目需要,所以搭建了一个DBLE+zookeeper集群的框架。在此记录一下。
简单介绍一下DBLE:
业内称作Mycat Plus。是一款分布式数据库中间件,一般用于MySQL分片。在知名数据库中间Mycat的基础上进行了大量的优化和定制,修复了很多Mycat的bug,有专业的团队维护,可以提供商业服务。
官方特性一览:
官方网站:DBLE官网
zookeeper我想我不用过多介绍了,时下算是最火的一个分布式服务框架。很多中间件,比如Kafka、Hadoop、HBase,都用到了 Zookeeper。
(以下内容来自知乎柳树 https://zhuanlan.zhihu.com/p/69114539?utm_source=wechat_session)
为什么需要 Zookeeper?
正经点来回答,就是我们需要一个用起来像单机但是又比单机更可靠的东西。
不正经的回答:
一个团队里面,需要一个leader,leader是干嘛用的?管理什么的咱不说,就说如果外面的人,想问关于这个团队的一切事情,首先就会去找这个leader,因为他知道的最多,而且他的回答最靠谱。
比如产品经理小饼过来要人,作为leader,老吕发现小耀最近没有项目安排,于是把小耀安排给了小饼的项目;
过了一会,另一个产品小西也过来要人,老吕发现刚刚把小耀安排走了,已经没人,于是就跟小西说,人都被你们产品要走了,你们产品自己去协调去。
如果老吕这时候忘了小耀已经被安排走了,把小耀也分配给小西,那到时两个产品就要打架了。
这就是leader在团队里的协调作用。
同样的,在分布式系统中,也需要这样的协调者。
比如我们搭建了一个数据库集群,里面有一个Master,多个Slave,Master负责写,Slave只读,我们需要一个系统,来告诉客户端,哪个是Master。
有人说,很简单,我们把这个信息写到一个Java服务器的内存就好了,用一个map,key:master,value:master机器对应的ip
但是别忘了,这是个单机,一旦这个机器挂了,就完蛋了,客户端将无法知道到底哪个是Master。
于是开始进行拓展,拓展成三台服务器的集群。
这下问题来了,如果我在其中一台机器修改了Master的ip,数据还没同步到其他两台,这时候客户端过来查询,如果查询走的是另外两台还没有同步到的机器,就会拿到旧的数据,往已经不是master的机器写数据。
所以我们需要这个存储master信息的服务器集群,做到当信息还没同步完成时,不对外提供服务,阻塞住查询请求,等待信息同步完成,再给查询请求返回信息。
这样一来,请求就会变慢,变慢的时间取决于什么时候这个集群认为数据同步完成了。
假设这个数据同步时间无限短,比如是1微妙,可以忽略不计,那么其实这个分布式系统,就和我们之前单机的系统一样,既可以保证数据的一致,又让外界感知不到请求阻塞,同时,又不会有SPOF(Single Point of Failure)的风险,即不会因为一台机器的宕机,导致整个系统不可用。
这样的系统,就叫分布式协调系统。谁能把这个数据同步的时间压缩的更短,谁的请求响应就更快,谁就更出色,Zookeeper就是其中的佼佼者。
它用起来像单机一样,能够提供数据强一致性,但是其实背后是多台机器构成的集群,不会有SPOF。
其实就是CAP理论中,满足CP,不满足A的那类分布式系统。
(以上内容来自知乎柳树 https://zhuanlan.zhihu.com/p/69114539?utm_source=wechat_session)
好了,概念说完,我们开始动手操作!说的这么牛逼,是骡子是马拉出来溜溜!
一.4G内存的虚拟机(VirtualBox)
二.CentOS7系统
三.安装docker
四.拉取MySQL镜像:docker pull mysql:5.7(我用的mysql5.7版本)
五.制作DBLE镜像:
我是直接在DBLE的GitHub上面拉取的源码到本地进行制作的,这样以方便我们项目以后有需要可以进行DBLE源码的修改,然后再重新打包镜像。
DBLE源码地址:DBLE源码地址,我选的2.19.11.0版本
源码下下来后,等待下载依赖包。
依赖下载完成后,我们需要在pom.xml文件中增加:
com.spotify docker-maven-plugin 1.0.0 ${project.artifactId}:${project.version} ./docker-images / ${project.build.directory} ${project.build.finalName}.jar
用于docker镜像打包。
然后我们先运行命令:
mvn clean package -DskipTests
将项目打包。
BUILD SUCCESS!
通过打包的信息我们可以看到生成的包在target下:
DBLE帮我们把项目打包成了几种格式,现在我们是要打包成镜像部署在服务器上运行的,所以我们选择xxx-linux.tar.gz这种压缩包的格式。我们把这个包拷贝到项目的docker-images目录下:
然后修改我们的Dockerfile文件:
FROM centos:centos7RUN yum install -y wget java mysqlCOPY dble-2.19.11.0-20200813140947-linux.tar.gz /optCOPY quick-start/schema.xml /opt/dble/conf/COPY quick-start/server.xml /opt/dble/conf/COPY quick-start/rule.xml /opt/dble/conf/COPY quick-start/wrapper.conf /opt/dble/conf/COPY quick-start/docker_init_start.sh /opt/dble/bin/COPY wait-for-it.sh /opt/dble/bin/RUN tar zxvf /opt/dble-2.19.11.0-20200813140947-linux.tar.gz -C /opt && \ chmod 777 /opt/dble/bin/* && \ rm -f /opt/dble-2.19.11.0-20200813140947-linux.tar.gzVOLUME /opt/dbleCMD ["/opt/dble/bin/docker_init_start.sh"]
启动我们本地的docker(切记!一定要启动docker,有时候会忘记,然后各种查找原因,很冤!),然后运行命令:
mvn clean package -DskipTests docker:build
(这里其实应该可以只运行mvn docker build -Dmaven.test.skip,但是会报找不到target目录的错,后面有时间再细研究这个问题)
感谢上帝,我们又看见了BUILD SUCCESS!图中红框的部分就是我们打包好的镜像ID。也可以通过命令docker images查看所以你打在本地的镜像。
接下来就是上传我们的镜像啦。
我一般是先把镜像传到我的阿里云镜像仓库:
sudo docker login --username=阿凯的帽子反戴 registry.cn-hangzhou.aliyuncs.com
输入密码,然后:
sudo docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/age/agestore:[镜像版本号]
这里的镜像版本号是我们自己取的镜像名字,反正只要能标识你仓库里面的这个镜像就可以,一般都是以应用+版本名称来,类似dble1.0.1、dble1.0.2这样的。
然后上传:
sudo docker push registry.cn-hangzhou.aliyuncs.com/age/agestore:[镜像版本号]
上传完成后,我们到我的镜像仓库去看看:
可以看到我们的镜像已经上传成功了,很舒服。(原谅我的镜像名字起的比较随意,毕竟只是测试嘛)
接下来终于进入正题啦!我们先来看一下我们的架构图:
现在我们要实现的是浅绿色虚线框中的部分架构。
开整!
首先,登陆我的虚拟机。
我们先来创建一个docker网络:
docker network create -o "com.docker.network.bridge.name"="my-net" --subnet 172.18.0.0/16 my-net
启动两个mysql容器:
docker run --name backend-mysql1 --ip 172.18.0.2 -e MYSQL_ROOT_PASSWORD=123456 -p 33061:3306 --network=my-net -d mysql:5.7 --server-id=1docker run --name backend-mysql2 --ip 172.18.0.3 -e MYSQL_ROOT_PASSWORD=123456 -p 33062:3306 --network=my-net -d mysql:5.7 --server-id=2
然后从我的阿里云镜像仓库把我们上面创建的dble镜像拉取下来:
sudo docker login --username=阿凯的帽子反戴 registry.cn-hangzhou.aliyuncs.comsudo docker pull registry.cn-hangzhou.aliyuncs.com/age/agestore:[镜像版本号]
拉取完成后
启动两个dble容器:
docker run -d -i -t --name dble-server01 --ip 172.18.0.4 -p 8066:8066 -p 9066:9066 --network=my-net eca6b28ad63bdocker run -d -i -t --name dble-server02 --ip 172.18.0.5 -p 8166:8066 -p 9166:9066 --network=my-net eca6b28ad63b
现在我们的虚拟机上,就跑了两个dble应用和两个mysql应用了。
然后我们先用dble连接mysql试试。
可以用命令
docker exec -it [镜像ID] /bin/bash
进入运行的容器中,在/opt/dble/conf目录下修改server.xml、schema.xml、rule.xml这三个文件(网上有一些测试的配置,这里我不再做赘述),两个dble服务使用相同配置即可。配置完成后,重启dble容器,然后用一个SQL工具测试一下:
配置成功如图所示。
这样,我们的单体dble服务就配置完成了。那么现在我们要把这两个单体dble服务统一管理起来,我们的zookeeper就登场啦!
先下载zookeeper镜像:
docker pull zookeeper:3.4.14
我这里用的3.4.14版本,使用3.5以上版本的小伙伴,可能到时候的配置不太一样哦,要注意。
查看一下zookeeper的镜像ID:
这里我们启动三个zookeeper容器:
docker run -d --name zookeeper01 --ip 172.18.0.6 -p 22181:2181 --network=my-net 5273b3db83ffdocker run -d --name zookeeper02 --ip 172.18.0.7 -p 22182:2181 --network=my-net 5273b3db83ffdocker run -d --name zookeeper03 --ip 172.18.0.8 -p 22183:2181 --network=my-net 5273b3db83ff
为什么是三个呢?这就和zookeeper集群的特性有关啦。
zookeeper集群中,只有当半数以上的节点都挂掉,整个集群才会挂掉。
什么是半数以上?
假如我是三个节点,挂掉一个,还有两个节点存活,半数以上,集群还能工作。再挂掉一个,那么只有一个节点存活,少于半数以上了,整个集群就挂掉了。
假如我是四个节点,挂掉一个,还有三个节点存活,半数以上,集群还能工作。再挂掉一个,只有两个节点存活,只有半数节点存活,少于半数以上了,整个集群挂掉。
小伙伴们发现什么问题?三个节点的集群,和四个节点的集群,容错率是一样的!
也就是说,我们的2n-1和2n个节点的集群拥有一样的容错率。但是可以少部署一台机器。所以zookeeper的集群,通常都是奇数个节点。
书归正传,三台zookeeper容器启动后,我们要配置一下,才能使它们三成为一个集群。
运行命令把容器中的zoo.cfg文件拷贝出来:
docker cp zookeeper01容器id:conf/zoo.cfg zoo.cfg
vim命令修改一下,在文件末尾增加集群配置:
server.1=zookeeper01:2881:3881server.2=zookeeper02:2881:3881server.3=zookeeper03:2881:3881
这里因为三个容器都注册到同一个bridge,所以可以通过容器名称进行访问。
然后将zoo.cfg文件分别复制到三个容器中:
docker cp zoo.cfg zookeeper01容器id:conf/zoo.cfgdocker cp zoo.cfg zookeeper02容器id:conf/zoo.cfgdocker cp zoo.cfg zookeeper03容器id:conf/zoo.cfg
修改完zoo.cfg后我们还要修改容器data/myid,myid这个文件中就写一个数字,分别对应上面的集群id:
将myid修改并复制到容器的data/myid:
docker cp myid(内容:1) zookeeper01容器id:data/myiddocker cp myid(内容:2) zookeeper02容器id:data/myiddocker cp myid(内容:3) zookeeper03容器id:data/myid
最后,重启我们这三台zookeeper容器。
分别进入三台容器内部验证一下集群配置生效没:
可以看到,两台follower,一台leader,集群搭建成功,舒服!
接下来,我们就把dble服务注册到我们的zookeeper集群上。
随便进入一个dble容器,修改/opt/dble/conf/myid.properties文件:
## Copyright (C) 2016-2020 ActionTech.# License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.##set false if not use cluster ucore/zkcluster=zk#clinet infoipAddress=172.18.0.6:2181,172.18.0.7:2181,172.18.0.8:2181#cluster namespace, please use the same one in one clusterclusterId=cluster-1#it must be different for every node in clustermyid=server_01
这里要注意,myid这个配置,两个dble服务一定要不一样。我这里一个设为server_01,一个设为server_02,其他的配置一样的即可。
配置完成后,重启两个dble容器。
启动完成后可以进入容器中/opt/dble/logs/wrapper.log中查看启动日志,看是否有报错,如下则表示成功启动:
然后我们再到zookeeper容器中看下服务有没有成功注册上去。
随便进入一个zookeeper容器:
在/bin路径下,执行zkCli.sh:
可以看到,我们的dble服务已经注册上去啦!
我们再用我们的SQL工具连接dble试试:
可以连接,舒服!
现在我们来把zookeeper集群里面的zookeeper01这个节点挂了,会怎么样?
我们来试试:
暴脾气!说停就停!
我们再来看看zookeeper02和zookeeper03中的服务注册,是否要有dble的两个容器:
可以看到我们的zookeeper02和zookeeper03节点上依然注册着dble的两个服务。
然后我们在通过SQL工具测试一下:
两个dble服务依然能正常提供服务。说明我们的zookeeper集群依然能正常提供服务,舒服!
由于我这里只启动了两个dble服务(虚拟机内存所限,一个字:穷),所以没有办法演示当dble集群中有节点挂掉时,另外的节点依然能作为一个集群对外暴露服务。什么意思呢?
假如我的dble集群有三个节点A、B、C,我们的节点A停掉,然后在节点B中上对数据库中的任意一张表的结构进行修改,然后通过节点C查看这张表结构,发现查看到的是修改后的表结构。
所以,DBLE 集群中,当其他节点掉线或故障时,不影响其他节点提供服务。有兴趣的同学可以自己尝试一下~