近年微服务架构在互联网应用领域中愈来愈火,引入微服务主要解决了单体应用多个模块的紧耦合、无法扩展和运维困难等问题。微服务架构就是按照功能粒度将业务模块进行垂直拆分,对单体应用本身进行服务化和组件化,每个组件单独部署为小应用(从DB到UI)。微服务与微服务之间通过Service API进行交互,同时为了支持水平扩展、性能提升和服务可用性,单个服务允许同时部署一个或者多个服务实例。在运行时,每个实例通常是一个云虚拟机或者Docker容器。
微服务系统内部多个服务的实例之间如何通信?如何感知到彼此的存在和销毁?生产者服务如何知道消费者服务的地址?如何实现服务与注册中心的解耦?这就需要一个第三方的服务注册中心,提供对生产者服务节点的注册管理和消费者服务节点的发现管理。
1. 服务发现与注册
1.1. 具体流程
一个较为完整的服务注册与发现流程如下:
1.2. 相关组件
一个服务发现系统主要由三部分组成:
1.3. 第三方实现
对于第三方的服务注册与发现的实现,现有的工具主要有以下三种:
简单对比:
与Zookeeper和etcd不一样,Consul内嵌实现了服务发现系统,不需要构建自己的系统或使用第三方系统,客户只需要注册服务,并通过DNS或HTTP接口执行服务发现。
2. Consul和Registrator
2.1. Consul简介
Consul是什么
Consul 是一种分布式的、高可用、支持水平扩展的的服务注册与发现工具。它大致包括以下特性:
Consul的几个概念
下图是Consul官方文档提供的架构设计图:
图中包含两个Consul数据中心,每个数据中心都是一个consul的集群。在数据中心1中,可以看出consul的集群是由N个SERVER,加上M个CLIENT组成的。而不管是SERVER还是CLIENT,都是consul集群的一个节点。所有的服务都可以注册到这些节点上,正是通过这些节点实现服务注册信息的共享。除了这两个,还有一些小细节 一一 简单介绍。
CLIENT表示consul的client模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER节点,本身是不持久化这些信息。
SERVER表示consul的server模式,表明这个consul是个server节点。这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地。这样遇到故障,信息是可以被保留的。
中间那个SERVER下面有LEADER的描述,表明这个SERVER节点是它们的老大。和其它SERVER不一样的一点是,它需要负责同步注册信息给其它的SERVER,同时也要负责各个节点的健康监测。
其它信息包括各个节点之间的通信方式,还有一些协议信息、算法。它们是用于保证节点之间的数据同步、实时性要求等等一系列集群问题的解决。这些有兴趣的自己看看官方文档。
2.2. Registrator简介
什么是Registrator
Registrator是一个独立于服务注册表的自动服务注册/注销组件,一般以Docker container的方式进行部署。Registrator会自动侦测它所在的宿主机上的所有Docker容器状态(启用/销毁),并根据容器状态到对应的服务注册列表注册/注销服务。
事实上,Registrator通过读取同一台宿主机的其他容器Container的环境变量进行服务注册、健康检查定义等操作。
Registrator支持可插拔式的服务注册表配置,目前支持包括Consul, etcd和SkyDNS 2三种注册工具。
2.3. Docker安装Consul集群
2.3.1. 集群节点规划
我本地的使用的是Ubuntu16.04的虚拟机:
Consul的配置参数信息说明:
2.4. Docker安装Consul集群
2.4.1. 拉取consul官方镜像
madison@ubuntu:~$ docker pull consul:latest
2.4.2. 启动Server节点
运行consul镜像,启动Server Master节点node1:
node1:
madison@ubuntu:~$ docker run -d --name=node1 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": 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 \
-h node1 \
consul agent -server -bind=172.17.0.2 -bootstrap-expect=3 -node=node1 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
查看node1的日志,追踪运行情况:
现在集群中还没有选举leader节点,继续启动其余两台Server节点node2和node3:
node2:
madison@ubuntu:~$ docker run -d --name=node2 --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 \
-h node2 \
consul agent -server -bind=172.17.0.3 \
-join=192.168.127.128 -node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node2 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
查看node2节点的进程启动日志:
node3:
madison@ubuntu:~$ docker run -d --name=node3 --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 \
-h node2 \
consul agent -server -bind=172.17.0.4 \
-join=192.168.127.128 -node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node3 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
查看node3节点的进程启动日志:
当3个Server节点都启动并正常运行时,观察node2和node3的进程日志,可以发现node1被选举为leader节点,也就是这个数据中心的Server Master。
再次查看node1节点的进程启动日志:
观察日志发现,node2和node3都成功join到了node1所在的数据中心dc1。当集群中有3台Consul Server启动时,node1被选举为dc1中的主节点。然后,node1会通过心跳检查的方式,不断地对node2和node3进行健康检查。
2.4.4. 启动Client节点
node4:
madison@ubuntu:~$ docker run -d --name=node4 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": 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 \
-h node4 \
consul agent -bind=172.17.0.5 -retry-join=192.168.127.128 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node4 -client 0.0.0.0 -ui
查看node4节点的进程启动日志:
可以发现:node4是以Client模式启动运行的。启动后完成后,把dc1数据中心中的以Server模式启动的节点node1、node2和node3都添加到本地缓存列表中。当客户端向node4发起服务发现的请求后,node4会通过RPC将请求转发给Server节点中的其中一台做处理。
2.4.5. 查看集群状态
madison@ubuntu:~$ docker exec -t node1 consul members
dc1数据中心中的4个节点node1, node2, node3和node4分别成功启动,Status表示他们的状态,都为alive。node1, node2, node3以Server模式启动,而node4以Client模式启动。
2.5. Docker安装Registrator
2.5.1. 拉取Registrator的镜像
madison@ubuntu:~$ docker pull gliderlabs/registrator:latest
2.5.2. 启动Registrator节点
madison@ubuntu:~$ docker run -d --name=registrator \
-v /var/run/docker.sock:/tmp/docker.sock \
--net=host \
gliderlabs/registrator -ip="192.168.127.128" consul://192.168.127.128:8500
--net指定为host表明使用主机模式。-ip用于指定宿主机的IP地址,用于健康检查的通信地址。consul://192.168.127.128:8500: 使用Consul作为服务注册表,指定具体的Consul通信地址进行服务注册和注销(注意:8500是Consul对外暴露的HTTP通信端口)。
查看Registrator的容器进程启动日志:
Registrator在启动过程完成了以下几步操作:
2.5.3. 查看Consul的注册状态
Consul提供了一个Web UI来可视化服务注册列表、通信节点、数据中心和键/值存储等,直接访问宿主机的8500端口。
服务注册列表:
NODES节点下挂载着dc1数据中心中的所有的Consul节点,包括Consul Server和Client。
通信节点列表:
启动Registrator以后,宿主机中的所有容器把服务都注册到Consul的SERVICES上,测试完成!
单数据中心的Consul集群的搭建就完成了!!!后续章节我会介绍如何使用Registrator进行服务注册的标签化。然后通过docker部署多实例的Web容器来实现基于HTTP的RESTful Service和基于TCP的RPC Service的服务注册和健康检查定义,并演示如何以标签标识一个服务的多个实例。