因为公司目前的主要产品使用的注册中心是consul
,consul
须要用集群来保证高可用,传统的方式(Nginx/HAProxy)会有单点故障问题,为了解决该问题,我开始研究如何只依赖consul
作集群的注册的方式,通过一天的折腾,总算验证了能够经过集群版ConsulClient来进行集群注册,在部署实施过程当中也遇到了一些问题,特此记录分享,但愿能对有须要的同窗有帮助。node
client+server转发模式的集群部署涉及到两种选择,第一种是直接主机模式部署,2个client
+3个server
,每一个consul
实例部署一台主机(适合土豪使用),此种模式的好处就是简单暴力,并且运维相对简单。此种模式的架构部署图以下: git
咱们选择的是另一种经济节约模式,docker
化部署,好处就是节约资源,劣势就是要管理许多docker
镜像,在有引入k8s这些容器管理平台以前,后续docker
的运维会比较麻烦,这种模式的架构部署图以下:github
经过以上两种模式的架构图咱们很清楚的就能知道主机部署模式是最简单直接的,而docker
的模式虽然节省了资源,可是加大了复杂性,增长了运维的难度。可是这种模式应该是在目前容器化的环境下很好的选择,缘由很简单,由于充分的利用了资源,容器的运维能够交给容器运维平台去完成,好比k8s等。下面咱们来实践下如何进行容器化的consul
集群部署。web
这里准备了两台虚拟主机,因为是虚拟的主机,对外ip是同样的,因此咱们以端口区分。spring
主机A:192.168.23.222:10385 内网ip:192.168.236.3
主机B:192.168.23.222:10585 内网ip:192.168.236.5
复制代码
步骤一:主机安装Docker环境(以Centos为例)docker
yum install docker
复制代码
步骤二:拉取Consul镜像进行部署shell
docker pull consul
复制代码
步骤三:给主机Docker分配ip段,防止多主机ip重复json
docker
的/etc/docker/daemon.json
文件,添加下面的内容"bip": "172.17.1.252/24"
复制代码
docker
的/etc/docker/daemon.json
文件,添加下面的内容"bip": "172.17.2.252/24"
复制代码
这里的配置是给主机的docker
实例分配ip,由于后续docker
会进行跨主机注册,若是默认注册的话,docker
是用的主机内网,从而致使ip重复,因此这里手动进行ip分配,固然上述的ip配置你能够自定义。bootstrap
步骤四:在主机A部署Consulbash
Node1:
docker run -d --name=node_31 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 11300:8300 \
-p 11301:8301 \
-p 11301:8301/udp \
-p 11302:8302/udp \
-p 11302:8302 \
-p 11400:8400 \
-p 11500:8500 \
-p 11600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node31 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
这里重点说明几个参数:
--name
:是docker
容器的名字,每一个容器实例不同
-node
:是consul
节点的名字,每一个节点不同
-bootstrap-expect
:是启动集群指望至少多少个节点,这里设置是3个。
-data-dir
:是consul
的数据中心的目录,必须给与consul
读写权限,不然启动会报错。
启动成功后,执行命令查看consul
的节点。
docker exec -t node_31 consul members
复制代码
显示结果以下:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
复制代码
这说明第一个节点正常启动了,接下来正常启动主机A剩下的节点。
Node2:
docker run -d --name=node_32 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 9300:8300 \
-p 9301:8301 \
-p 9301:8301/udp \
-p 9302:8302/udp \
-p 9302:8302 \
-p 9400:8400 \
-p 9500:8500 \
-p 9600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node32 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
Node3:
docker run -d --name=node_33 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 10300:8300 \
-p 10301:8301 \
-p 10301:8301/udp \
-p 10302:8302/udp \
-p 10302:8302 \
-p 10400:8400 \
-p 10500:8500 \
-p 10600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node33 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
三个节点启动完毕后,执行命令,查看节点的状态:
docker exec -t node_31 consul operator raft list-peers
复制代码
结果以下:
Node ID Address State Voter RaftProtocol
node32 ee186aef-5f8a-976b-2a33-b20bf79e7da9 172.17.1.2:8300 follower true 3
node33 d86b6b92-19e6-bb00-9437-f988b6dac4b2 172.17.1.3:8300 follower true 3
node31 0ab60093-bed5-be77-f551-6051da7fe790 172.17.1.1:8300 leader true 3
复制代码
这里已经显示,三个server
的节点已经完成了集群部署,而且选举了node_31
做为主节点。最后给该主机集群部署一个client就大功告成了。
Node4(client节点)
docker run -d --name=node_34 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' \
-p 8300:8300 \
-p 8301:8301 \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8302:8302 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
consul agent -retry-join=172.17.1.1 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node34 -client 0.0.0.0 -ui
复制代码
执行docker exec -t node_31 consul members
命令,结果以下:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
复制代码
这里说明,主机A的consul
节点所有启动完成,而且完成了集群部署,能够说这就是一个单主机版的consul
集群,那么接下来咱们要作的就是把主机B的consul
加入到主机A的集群中便可。
步骤五:在主机B部署Consul
Node5
docker run -d --name=node_51 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 11300:8300 \
-p 11301:8301 \
-p 11301:8301/udp \
-p 11302:8302/udp \
-p 11302:8302 \
-p 11400:8400 \
-p 11500:8500 \
-p 11600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_51 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
Node6
docker run -d --name=node_52 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 9300:8300 \
-p 9301:8301 \
-p 9301:8301/udp \
-p 9302:8302/udp \
-p 9302:8302 \
-p 9400:8400 \
-p 9500:8500 \
-p 9600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_52 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
Node7
docker run -d --name=node_53 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 10300:8300 \
-p 10301:8301 \
-p 10301:8301/udp \
-p 10302:8302/udp \
-p 10302:8302 \
-p 10400:8400 \
-p 10500:8500 \
-p 10600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_53 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
复制代码
主机B的三个server节点部署完成后,咱们执行命令docker exec -t node_51 consul members
查看下集群节点状态
Node Address Status Type Build Protocol DC Segment
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
复制代码
为何只有node_51
这个单独的节点呢?是否是节点的问题?咱们在主机B中查询一样查询一下,结果以下:
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
复制代码
主机A的节点只有他们本身机器的节点,主机B中的节点所有未注册过来,这是为何呢?缘由就是consul
绑定的ip是容器的内网ip,主机内部通信是能够的,跨主机通信是没法经过内网地址进行通信的,那么咱们怎么作呢?咱们经过路由规则进行转发便可,把主机A请求主机B容器的内网地址转发到主机B便可,这里就体现出咱们开始给容器分配ip的做用了。 咱们在主机A执行以下命令:
route add -net 172.17.2.0 netmask 255.255.255.0 gw 192.168.236.5
复制代码
这条命令的意思是,添加一个路由规则172.17.2.1~172.17.2.254
范围的ip请求,所有转发到192.168.236.5
地址下,也就是咱们的主机B。 同理主机B也执行以下命令:
route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.236.3
复制代码
添加完成后,在执行docker exec -t node_53 consul members
命令:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
node_52 172.17.2.2:8301 alive server 1.6.2 2 dc1 <all>
node_53 172.17.2.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
复制代码
集群加入就成功了,这就完成了跨主机的docker容器加入。 最后给主机B部署一个client
Node8(client节点)
docker run -d --name=node_54 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' \
-p 8300:8300 \
-p 8301:8301 \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8302:8302 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
consul agent -retry-join=172.17.1.1 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node54 -client 0.0.0.0 -ui
复制代码
最后的集群节点所有加入成功了,结果以下:
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
node_52 172.17.2.2:8301 alive server 1.6.2 2 dc1 <all>
node_53 172.17.2.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
node54 172.17.2.4:8301 alive client 1.6.2 2 dc1 <default>
复制代码
执行节点状态命令docker exec -t node_31 consul operator raft list-peers
:
node32 ee186aef-5f8a-976b-2a33-b20bf79e7da9 172.17.1.2:8300 follower true 3
node33 d86b6b92-19e6-bb00-9437-f988b6dac4b2 172.17.1.3:8300 follower true 3
node31 0ab60093-bed5-be77-f551-6051da7fe790 172.17.1.1:8300 leader true 3
node_51 cfac3b67-fb47-8726-fa31-158516467792 172.17.2.1:8300 follower true 3
node_53 31679abe-923f-0eb7-9709-1ed09980ca9d 172.17.2.3:8300 follower true 3
node_52 207eeb6d-57f2-c65f-0be6-079c402f6afe 172.17.2.2:8300 follower true 3
复制代码
这样一个包含6个server
+2个client
的consul
容器化集群就部署完成了,咱们查看consul
的web
面板以下:
集群版本的consul
咱们就部署好了,那么咱们如何与应用集成呢?咱们只要集成集群版本的consul
注册客户端就好了。 首先加入依赖
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>spring-cloud-starter-consul-cluster</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
复制代码
第二步在bootstrap.yml|properties
中指定spring.cloud.consul.host
为多节点,以下所示:
spring.cloud.consul.host=192.168.23.222:10385,192.168.23.222:10585
复制代码
若是想输出注册的相关日志的话也能够在logback上加上日志配置
<logger name="org.springframework.cloud.consul" level="TRACE"/>
复制代码
这样配置完成后启动成功就能看到咱们的应用注册成功了,下图是我测试的注册成功的效果:
这里显示个人应用节点分别都注册到了集群的2个client
上面,经过client
的代理转发请求到健康的server
,从而实现了consul
的高可用。
这篇文章没有研究什么技术干货,纯粹是工做经验分享,主要讲了consul
集群部署的方式,传统模式能够经过HAProxy
来完成集群的部署,可是这种方式的弊端很明显,经过虚拟ip仍是可能会指向故障的节点,因此咱们用consul
的client
+server
模式的集群部署,经过docker
化来充分利用了机器的资源,只须要2台机器就能完成集群的高可用效果。