基于Docker+Consul+Registrator的服务注册与发现集群搭建

前言

微服务架构在互联网应用领域中愈来愈火,引入微服务主要解决了单体应用多个模块的紧耦合无法扩展运维困难等问题。微服务架构就是按照功能粒度将业务模块进行垂直拆分,对单体应用本身进行服务化组件化,每个组件单独部署为小应用(从DBUI)。微服务与微服务之间通过Service API进行交互,同时为了支持水平扩展性能提升服务可用性,单个服务允许同时部署一个或者多个服务实例。在运行时,每个实例通常是一个云虚拟机或者Docker容器

微服务系统内部多个服务的实例之间如何通信?如何感知到彼此的存在和销毁?生产者服务如何知道消费者服务的地址?如何实现服务与注册中心的解耦?这就需要一个第三方的服务注册中心,提供对生产者服务节点的注册管理和消费者服务节点的发现管理。

服务发现与注册

具体流程

  • 服务注册中心:作为整个架构中的核心,要支持分布式持久化存储注册信息变动实时通知消费者。
  • 服务提供者:服务以 docker 容器化方式部署(实现服务端口动态生成),可以通过 docker-compose 的方式来管理。通过 Registrator 检测到 docker 进程信息以完成服务的自动注册
  • 服务消费者:要使用服务提供者提供的服务,和服务提供者往往是动态相互转位置的。

一个较为完整的服务注册与发现流程如下:

  1. 注册服务:服务提供者到注册中心注册
  2. 订阅服务:服务消费者到注册中心订阅服务信息,对其进行监听
  3. 缓存服务列表:本地缓存服务列表,减少与注册中心的网络通信;
  4. 调用服务:查找本地缓存,找不到再去注册中心拉取服务地址,然后发送服务请求;
  5. 变更通知:服务节点变动时 (新增删除等),注册中心将通知监听节点,更新服务信息。

相关组件

一个服务发现系统主要由三部分组成:

  1. 注册器(registrator):根据服务运行状态,注册/注销服务。主要要解决的问题是,何时发起注册/注销动作。
  2. 注册表(registry):存储服务信息。常见的解决方案有zookeeper、etcd、cousul等。
  3. 发现机制(discovery):从注册表读取服务信息,给用户封装访问接口。

第三方实现

对于第三方的服务注册与发现的实现,现有的工具主要有以下三种:

  1. zookeeper:一个高性能、分布式应用程序协调服务,用于名称服务、分布式锁定、共享资源同步和分布式配置管理。
  2. Etcd:一个采用HTTP协议的健/值对存储系统,主要用于共享配置和服务发现,提供的功能相对Zookeeper和Consul相对简单。
  3. Consul:一个分布式高可用的服务发现和配置共享的软件,支持服务发现与注册、多数据中心、健康检查和分布式键/值存储。

简单对比:

与Zookeeper和etcd不一样,Consul内嵌实现了服务发现系统,不需要构建自己的系统或使用第三方系统,客户只需要注册服务,并通过DNS或HTTP接口执行服务发现。

Consul和Registrator

Consul简介

Consul是什么

Consul 是一种分布式的、高可用支持水平扩展的的服务注册与发现工具。它大致包括以下特性:

  • 服务发现: Consul 通过 DNS 或者 HTTP 接口使服务注册和服务发现变的很容易。一些外部服务,例如 saas 提供的也可以一样注册;
  • 健康检查:健康检测使 consul 可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面;
  • 键/值存储:一个用来存储动态配置的系统。提供简单的 HTTP 接口,可以在任何地方操作;
  • 多数据中心:支持多数据中心以避免单点故障,内外网的服务采用不同的端口进行监听。而其部署则需要考虑网络延迟, 分片等情况等。zookeeperetcd均不提供多数据中心功能的支持;
  • 一致性算法:采用 Raft 一致性协议算法,比Paxos算法好用。 使用 GOSSIP 协议管理成员和广播消息, 并且支持 ACL 访问控制;
  • 服务管理Dashboard:提供一个 Web UI 的服务注册于健康状态监控的管理页面。

Consul的几个概念

下图是Consul官方文档提供的架构设计图:

图中包含两个Consul数据中心,每个数据中心都是一个consul的集群。在数据中心1中,可以看出consul的集群是由NSERVER,加上MCLIENT组成的。而不管是SERVER还是CLIENT,都是consul集群的一个节点。所有的服务都可以注册到这些节点上,正是通过这些节点实现服务注册信息的共享。除了这两个,还有一些小细节 一一 简单介绍。

  • CLIENT

CLIENT表示consulclient模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发SERVER节点,本身是不持久化这些信息。

  • SERVER

SERVER表示consulserver模式,表明这个consul是个server节点。这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地。这样遇到故障,信息是可以被保留的。

  • SERVER-LEADER

中间那个SERVER下面有LEADER的描述,表明这个SERVER节点是它们的老大。和其它SERVER不一样的一点是,它需要负责同步注册信息给其它的SERVER,同时也要负责各个节点健康监测

  • 其它信息

其它信息包括各个节点之间的通信方式,还有一些协议信息算法。它们是用于保证节点之间的数据同步实时性要求等等一系列集群问题的解决。这些有兴趣的自己看看官方文档。

Registrator简介

什么是Registrator
Registrator是一个独立于服务注册表的自动服务注册/注销组件,一般以Docker container的方式进行部署。Registrator会自动侦测它所在的宿主机上的所有Docker容器状态(启用/销毁),并根据容器状态到对应的服务注册列表注册/注销服务。

事实上,Registrator通过读取同一台宿主机的其他容器Container环境变量进行服务注册健康检查定义等操作。

Registrator支持可插拔式服务注册表配置,目前支持包括Consul, etcdSkyDNS 2三种注册工具。

Docker安装Consul集群

集群节点规划

我本地的使用的是Ubuntu16.04的虚拟机:

容器名称容器IP地址映射端口号宿主机IP地址服务运行模式
node1172.17.0.28500 -> 8500192.168.127.128Server Master
node2172.17.0.39500 -> 8500192.168.127.128Server
node3172.17.0.410500 -> 8500192.168.127.128Server
node4172.17.0.511500 -> 8500192.168.127.128Client

Consul集群安装

Consul的配置参数信息说明:

参数列表参数的含义和使用场景说明
advertise通知展现地址用来改变我们给集群中的其他节点展现的地址,一般情况下-bind地址就是展现地址
bootstrap用来控制一个server是否在bootstrap模式,在一个datacenter中只能有一个server处于bootstrap模式,当一个server处于bootstrap模式时,可以自己选举为raft leader
bootstrap-expect在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap共用
bind该地址用来在集群内部的通讯IP地址,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0
clientconsul绑定在哪个client地址上,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1
config-file明确的指定要加载哪个配置文件
config-dir配置文件目录,里面所有以.json结尾的文件都会被加载
data-dir提供一个目录用来存放agent的状态,所有的agent允许都需要该目录,该目录必须是稳定的,系统重启后都继续存在
dc该标记控制agent允许的datacenter的名称,默认是dc1
encrypt指定secret key,使consul在通讯时进行加密,key可以通过consul keygen生成,同一个集群中的节点必须使用相同的key
join加入一个已经启动的agent的ip地址,可以多次指定多个agent的地址。如果consul不能加入任何指定的地址中,则agent会启动失败,默认agent启动时不会加入任何节点
retry-interval两次join之间的时间间隔,默认是30s
retry-max尝试重复join的次数,默认是0,也就是无限次尝试
log-levelconsul agent启动后显示的日志信息级别。默认是info,可选:trace、debug、info、warn、err
node节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
protocolconsul使用的协议版本
rejoin使consul忽略先前的离开,在再次启动后仍旧尝试加入集群中
server定义agent运行在server模式,每个集群至少有一个server,建议每个集群的server不要超过5个
syslog开启系统日志功能,只在linux/osx上生效
pid-file提供一个路径来存放pid文件,可以使用该文件进行SIGINT/SIGHUP(关闭/更新)agent

拉取consul官方镜像

1
docker pull consul:latest

启动Server节点

运行consul镜像,启动Server Master节点node1

node1:

1
2
3
4
5
6
7
8
9
10
11
12
13
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节点node2node3

node2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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节点都启动并正常运行时,观察node2node3的进程日志,可以发现node1被选举为leader节点,也就是这个数据中心Server Master

再次查看node1节点的进程启动日志:

观察日志发现,node2node3都成功join到了node1所在的数据中心dc1。当集群中有3台Consul Server启动时,node1被选举为dc1中的主节点。然后,node1会通过心跳检查的方式,不断地对node2node3进行健康检查。

启动Client节点

node4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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模式启动的节点node1node2node3都添加到本地缓存列表中。当客户端向node4发起服务发现的请求后,node4会通过RPC将请求转发给Server节点中的其中一台做处理。

查看集群状态

1
docker exec -t node1 consul members

dc1数据中心中的4个节点node1, node2, node3node4分别成功启动,Status表示他们的状态,都为alivenode1, node2, node3Server模式启动,而node4Client模式启动。

Docker安装Registrator

拉取Registrator的镜像

1
docker pull gliderlabs/registrator:latest

启动Registrator节点

1
2
3
4
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在启动过程完成了以下几步操作:

  1. 查看Consul数据中心的leader节点,作为服务注册表;
  2. 同步当前宿主机的启用容器,以及所有的服务端口;
  3. 分别将各个容器发布的服务地址/端口注册到Consul的服务注册列表。

查看Consul的注册状态

Consul提供了一个Web UI来可视化服务注册列表通信节点数据中心键/值存储等,直接访问宿主机的8500端口。

服务注册列表

NODES节点下挂载着dc1数据中心中的所有的Consul节点,包括Consul ServerClient

通信节点列表

启动Registrator以后,宿主机中的所有容器把服务都注册到ConsulSERVICES上,测试完成!


总结

单数据中心Consul集群的搭建就完成了!!!后续章节我会介绍如何使用Registrator进行服务注册的标签化。然后通过docker部署多实例Web容器来实现基于HTTPRESTful Service和基于TCPRPC Service服务注册健康检查定义,并演示如何以标签标识一个服务的多个实例。

-------------本文结束感谢您的阅读-------------

本文标题:基于Docker+Consul+Registrator的服务注册与发现集群搭建

文章作者:WanDouDuoDuo

发布时间:2019年06月25日 - 19:06

最后更新:2019年11月01日 - 18:11

原始链接:https://wandouduoduo.com/articles/a2710f6.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

原创技术分享,您的支持将鼓励我继续创作