服务发现之Consul介绍、部署和使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lsy0903/article/details/85331939

为什么使用服务发现

微服务的框架体系中,服务发现是不得不提的一个模块。我相信了解或者熟悉微服务的应该都知道它的重要性。这里简单的介绍一下。我们看下面的一幅图片:

Alt

图中,客户端的一个接口,需要调用服务A-N。客户端必须要知道所有服务的网络位置的,以往的做法是将配置放到配置文件或数据库中。这里就带出几个问题:

  • 需要配置N个服务的网络位置,加大配置的复杂性
  • 服务的网络位置变化,都需要改变每个调用者的配置
  • 集群的情况下,难以做负载(反向代理的方式除外)

总结起来一句话:服务多了,配置很麻烦,问题多多

既然有这些问题,那么服务发现就是解决这些问题的。话说,怎么解决呢?我们再看一张图

Alt

与之前一张不同的是,加了个服务发现模块。服务A-N把当前自己的网络位置注册到服务发现模块(这里注册的意思就是告诉),服务发现就以K-V的方式记录下,K一般是服务名,V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了(这就是健康检查)。客户端在调用服务A-N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢?客户端完全不需要记录这些服务网络位置,客户端和服务端完全解耦!

这个过程大体是这样,当然服务发现模块没这么简单。里面包含的东西还很多。这样表述只是方便理解。

图中的服务发现模块基本上就是微服务架构中服务发现的作用了。

扫描二维码关注公众号,回复: 4735029 查看本文章

Consul 简介

做服务发现的框架常用的有

Consul和Eureka的区别

Eureka是一个服务发现工具。该体系结构主要是客户端/服务器,每个数据中心有一组Eureka服务器,通常每个可用区域一个。通常Eureka的客户使用嵌入式SDK来注册和发现服务。对于非本地集成的客户,使用功能区边框等透过Eureka透明地发现服务。

Eureka提供了一个弱一致的服务视图,使用尽力而为复制。当客户端向服务器注册时,该服务器将尝试复制到其他服务器,但不提供保证。服务注册的生存时间(TTL)较短,要求客户端对服务器心存感激。不健康的服务或节点将停止心跳,导致它们超时并从注册表中删除。发现请求可以路由到任何服务,由于尽力而为的复制,这些服务可能会导致陈旧或丢失数据。这个简化的模型允许简单的群集管理和高可扩展性。

领事提供了一套超级功能,包括更丰富的健康检查,关键/价值存储以及多数据中心意识。Consul需要每个数据中心都有一套服务器,以及每个客户端的代理,类似于使用像Ribbon这样的边车。Consul代理允许大多数应用程序成为Consul不知情者,通过配置文件执行服务注册并通过DNS或负载平衡器sidecars发现。

Consul提供强大的一致性保证,因为服务器使用Raft协议复制状态 。Consul支持丰富的健康检查,包括TCP,HTTP,Nagios / Sensu兼容脚本或基于Eureka的TTL。客户端节点参与基于八卦的健康检查,该检查分发健康检查工作,而不像集中式心跳检测那样成为可扩展性挑战。发现请求被路由到选举出来的领事领导,这使他们默认情况下强烈一致。允许陈旧读取的客户端使任何服务器都可以处理他们的请求,从而实现像Eureka这样的线性可伸缩性。

Consul强烈的一致性意味着它可以作为领导选举和集群协调的锁定服务。Eureka不提供类似的保证,并且通常需要为需要执行协调或具有更强一致性需求的服务运行ZooKeeper。

Consul提供了支持面向服务的体系结构所需的一系列功能。这包括服务发现,还包括丰富的运行状况检查,锁定,密钥/值,多数据中心联合,事件系统和ACL。Consul和consul-template和envconsul等工具生态系统都试图尽量减少集成所需的应用程序更改,以避免需要通过SDK进行本地集成。Eureka是一个更大的Netflix OSS套件的一部分,该套件预计应用程序相对均匀且紧密集成。因此,Eureka只解决了一小部分问题,希望ZooKeeper等其他工具可以一起使用。

CAP中,Consul使用CP体系结构,有利于实现可用性的一致性。

Consul概述

Consul是分布式的、高可用、横向扩展的。consul提供的一些关键特性:

  • service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。
  • health checking:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。
  • key/value storage:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。
  • multi-datacenter:无需复杂的配置,即可支持任意数量的区域。
  • Consul使用Go语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合 。

Consul内部原理

下面这张图来源于Consul官网,很好的解释了Consul的工作原理,先大致看一下。

Alt

首先Consul支持多数据中心,在上图中有两个DataCenter,他们通过Internet互联,同时请注意为了提高通信效率,只有Server节点才加入跨数据中心的通信。

在单个数据中心中,Consul分为Client和Server两种节点(所有的节点也被称为Agent),Server节点保存数据,Client负责健康检查及转发数据请求到Server;Server节点有一个Leader和多个Follower,Leader节点会将数据同步到Follower,Server的数量推荐是3个或者5个,在Leader挂掉的时候会启动选举机制产生一个新的Leader。

集群内的Consul节点通过gossip协议(流言协议)维护成员关系,也就是说某个节点了解集群内现在还有哪些节点,这些节点是Client还是Server。单个数据中心的流言协议同时使用TCP和UDP通信,并且都使用8301端口。跨数据中心的流言协议也同时使用TCP和UDP通信,端口使用8302。

集群内数据的读写请求既可以直接发到Server,也可以通过Client使用RPC转发到Server,请求最终会到达Leader节点,在允许数据轻微陈旧的情况下,读请求也可以在普通的Server节点完成,集群内数据的读写和复制都是通过TCP的8300端口完成。

现在我们只看DataCenter,可以看出Consul的集群是由N个SERVER,加上M个CLIENT组成的。而不管是SERVER还是CLIENT,都是consul的一个节点,所有的服务都可以注册到这些节点上,正是通过这些节点实现服务注册信息的共享。除了这两个,在看下面的一些小细节。

  • CLIENT

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

  • SERVER

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

  • SERVER-LEADER

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

  • 其它信息

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

Consul服务发现原理

下面这张图基本描述了服务发现的完整流程,先大致看一下。

Alt

首先需要有一个正常的Consul集群,有Server,有Leader。这里在服务器Server1、Server2、Server3上分别部署了Consul Server,假设他们选举了Server2上的Consul Server节点为Leader。这些服务器上最好只部署Consul程序,以尽量维护Consul Server的稳定。

然后在服务器Server4和Server5上通过Consul Client分别注册Service A、B、C,这里每个Service分别部署在了两个服务器上,这样可以避免Service的单点问题。服务注册到Consul可以通过HTTP API(8500端口)的方式,也可以通过Consul配置文件的方式。Consul Client可以认为是无状态的,它将注册信息通过RPC转发到Consul Server,服务信息保存在Server的各个节点中,并且通过Raft实现了强一致性。

最后在服务器Server6中Program D需要访问Service B,这时候Program D首先访问本机Consul Client提供的HTTP API,本机Client会将请求转发到Consul Server,Consul Server查询到Service B当前的信息返回,最终Program D拿到了Service B的所有部署的IP和端口,然后就可以选择Service B的其中一个部署并向其发起请求了。如果服务发现采用的是DNS方式,则Program D中直接使用Service B的服务发现域名,域名解析请求首先到达本机DNS代理,然后转发到本机Consul Client,本机Client会将请求转发到Consul Server,Consul Server查询到Service B当前的信息返回,最终Program D拿到了Service B的某个部署的IP和端口。

图中描述的部署架构笔者认为是最普适最简单的方案,从某些默认配置或设计上看也是官方希望使用者采用的方案,比如8500端口默认监听127.0.0.1,当然有些同学不赞同,后边会提到其他方案。

Consul基本使用

为了更快的熟悉Consul的原理及其使用方式,最好还是自己实际测试下。

Consul安装十分简单,但是在一台机器上不方便搭建集群进行测试,使用虚拟机比较重,所以这里就演示下docker下部署使用Consul。容器与宿主机的端口映射忽略,正常生产环境每个宿主机一个Consul,端口需要映射到宿主机

安装Docker

更新yum

yum update

安装依赖包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

设置阿里云镜像源

sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装 Docker-CE

sudo yum install docker-ce

检查并启动 Docker-CE

docker -v
sudo yum install docker-ce
sudo systemctl start docker

GUI 管理配置(可选)

这里推荐使用 Portainer 作为容器的 GUI 管理方案。

官方地址:https://portainer.io/install.html

安装命令:

docker volume create portainer_data
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

访问你的 IP:9000 即可进入容器管理页面。

部署Consul

拉取镜像

docker search consul
docker pull consul

不指定tag就拉取last,当前版本是0.8.1

启动Consul

这里启动4个Consul Agent,3个Server(会选举出一个leader),1个Client。

  • 启动第一个Server节点同时开启管理界面

docker run -d -p 8500:8500 -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node1 consul agent -server -bind=172.17.0.2  -bootstrap-expect=3 -node=node1 --bootstrap-expect=3 --client=0.0.0.0 -ui

-node:节点的名称
-p: 将容器8500端口映射到主机8500端口
-bind:绑定的一个地址,用于节点之间通信的地址,可以是内外网,必须是可以访问到的地址
-server:这个就是表示这个节点是个SERVER
-bootstrap-expect:这个就是表示期望提供的SERVER节点数目,数目一达到,它就会被激活,然后就是LEADER了
-ui 开启管理界面

  • 启动第2-3个Server节点,并加入集群

docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node2 consul agent -server -bind=172.17.0.3  -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node2
docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node3 consul agent -server -bind=172.17.0.4  -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node3 -client=172.17.0.4

-join:这个表示启动的时候,要加入到哪个集群内,这里就是说要加入到节点1的集群
-node-id:这个貌似版本8才加入的,这里用这个来指定唯一的节点ID,可以查看这个issue
-client:这个表示注册或者查询等一系列客户端对它操作的IP,如果不指定这个IP,默认是127.0.0.1。

  • 启动第4个Client节点,并加入集群

docker run -d -e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' --name=node4 consul agent -bind=172.17.0.5 -retry-join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node4

除了没有 -server,其它都是一样的,没有这个就说明这个节点是CLIENT

  • 查看集群状态

docker exec -t node1 consul members

集群状态

4个节点都列出来了。Status表示它们的状态,都是alive。Type表示它们的类型,三个SERVER一个CLIENT,和我们之前启动的一样。DC表示数据中心,都是dc1。

Consul的其它部署架构

如果你实在不想在每个主机部署Consul Client,还有一个多路注册的方案可供选择

Consul的其它部署架构

如图所示,在专门的服务器上部署Consul Client,然后每个服务都注册到多个Client,这里为了避免服务单点问题还是每个服务部署多份,需要服务发现时,程序向一个提供负载均衡的程序发起请求,该程序将请求转发到某个Consul Client。这种方案需要注意将Consul的8500端口绑定到私网IP上,默认只有127.0.0.1。

这个架构的优势:

  • Consul节点服务器与应用服务器隔离,互相干扰少;
  • 不用每台主机都部署Consul,方便Consul的集中管理;
  • 某个Consul Client挂掉的情况下,注册到其上的服务仍有机会被访问到;

但也需要注意其缺点:

  • 引入更多技术栈:负载均衡的实现,不仅要考虑Consul Client的负载均衡,还要考虑负载均衡本身的单点问题。
  • Client的节点数量:单个Client如果注册的服务太多,负载较重,需要有个算法(比如hash一致)合理分配每个Client上的服务数量,以及确定Client的总体数量。
  • 服务发现要过滤掉重复的注册,因为注册到了多个节点会认为是多个部署(DNS接口不会有这个问题)。

这个方案其实还可以优化,服务发现使用的负载均衡可以直接代理Server节点,因为相关请求还是会转发到Server节点,不如直接就发到Server。

是否可以只有Server?

这个问题的答案还是有关服务数量的问题,首先Server的节点数量不是越多越好,3个或者5个是推荐的数量,数量越多数据同步的处理越慢(强一致性);然后每个节点可以注册的服务数量是有上限的,这个受限于软硬件的处理能力。所以如果你的服务只有10个左右,只有Server问题是不大的,但是这时候有没有必要使用Consul呢?因此正常使用Consul的时候还是要有Client才好,这也符合Consul的反熵设计。


猜你喜欢

转载自blog.csdn.net/lsy0903/article/details/85331939