8

Consul Service Mesh实战

 1 year ago
source link: http://servicecomb.apache.org/cn/docs/consul-servicemesh
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Consul Service Mesh实战

在最近发布的Consul1.2中,HashiCorp宣布支持Service Mesh。作为一个优秀的分布式服务发现解决方案,Consul是如何支持Service Mesh的呢?本文将带读者一探究竟。

在Consul 1.2的release blog中,Consul定义了Service Mesh的3个要素:

  1. Discovery:服务可以互相找到
  2. Configuration:服务在运行时,可以从一个集中的源头获取配置
  3. Segmentation:服务之间的通信必须是加密并通过认证的

实际上,Consul本身已经支持服务发现,并且实现了一个集群共享的KV存储方案,可以实现服务配置的存储。而最新的Consul 1.2中,新发布的Connect功能,可以为服务创建一个网络层的代理,这成为Consul Service Mesh方案中,数据面代理的基础。接下来,我们从这三个方面一一对Consul进行解析。

Discovery

Consul是一个跨数据中心的分布式服务发现解决方案。在Consul集群的每一个节点上,都运行一个Consul进程,成为一个Consul agent,一个agent可以以server或client两种模式运行。Client agent非常轻量,只需与Server agent交互,并维护Client自身很少的状态。而Server agent除了Client agent的所有功能,还提供额外的能力来完成基于Raft的一致性方案,因此Server agent的负载会更高,也需要更多的资源,运行在更加稳定的节点上。Consul agent可以通过join命令,指定集群中任意一个agent的IP,以加入集群,形成一个Consul cluster。如下图所示:

consul-cluster

在单个节点上,Consul agent读取该节点服务的配置文件,获取该节点上服务的信息,并与cluster中其他agent通过gossip协议互相通信,完成集群内的服务注册与发现。当Consul cluster中的服务需要调用另一个服务时,可以通过API和DNS解析两种方式,向所在节点的Consul agent获取目标服务的具体地址,然后调用目标服务。

Configuration

除了服务注册与发现,Consul还内置了一个KV存储系统,用于Cluster之间共享数据。KV的Key是分层的,通过斜线/分隔,这样,可以在概念上对key进行组织和管理,用来保存不同的配置,并把配置分配给集群内的所有服务。例如,在一个集群内,运行了webfront服务,webfront服务需要获知当前是开发环境还是测试环境,以提供不同的页面表现。那么,我们在可以在节点A上用KV存储保存环境信息:

nodeA$ consul kv put services/webapi/metadata/env dev #假设env=dev表示测试环境

服务运行过程中,假设任务调度器将webfront服务的实例调度到节点B和节点C,那么在节点B和C上可以通过Consul agent访问到环境信息:

nodeB$ consul kv get services/webapi/metadata/env
nodeB$ dev

当然,在实际的使用中,我们不仅可以通过命令行来获取KV数据,也可以通过RESTful API,向所在节点的Consul agent发送HTTP请求来获取数据。

Segmentation

按照Consul的说明,Segmentation的意义在于使服务之间的网络请求都是经过TLS加密并认证的。那么,Consul是如何做到请求的自动TLS加密和认证的呢?

Consul 1.2新增了Connect功能,只需要在服务中增加一个connect配置,即可为该服务启动一个代理:

{
    "service": {
        "name": "webfront",
        "connect": {
            "proxy": {}
        }
    }
}

这样,Consul agent就会为webfront服务启动一个内置的proxy,proxy启动时,向所在节点的Consul agent(为了便于说明,假设为Client agent,其实Client/Server都是可能的)请求webfront所需要的root证书和leaf证书。Client agent检查本地的缓存,若已有所需证书,则直接将证书和私钥返回。否则就会生成一个新的私钥,并向Server agent发送CSR。Server对CSR进行验证,并返回签名的证书,Client agent拿到证书,把证书和私钥返回给proxy。这样,proxy就可以接受TLS请求了。这个过程如下:

consul-tls

并且,Consul中还有一个自动更新的机制,当Server中的根证书变化时,Client重新生成私钥,向Server发送CSR,获取签名的证书,并把签名的证书和私钥再发给proxy,完成证书的更新。

了解了Consul Connect,我们会发现,其实Connect功能帮每个服务启动了一个Proxy,对服务的inbound/outbound请求进行了TLS加密和认证,这个场景是否似曾相识?没错,就是Service Mesh中数据面sidecar的概念。只是这个Consul内建的sidecar目前能实现的功能还比较单一,只有TLS加密和认证。

现在,我们对Consul Service Mesh的三个要素都有了较为具体了解,那么,相比于一般意义上的Service Mesh方案,例如Istio,Conduit等,Consul的Service Mesh有何不同呢?我们不妨以Istio为例,看看Istio中是如何实现这三个要素的:

Discovery:由Pilot结合服务发现机制(kubernetes,eureka,consul等)完成

Configuration:由Pilot结合Kubernetes完成,用户使用kubectl定义Istio所需的Kubernetes资源,Pilot读取到这些资源后,转换为自己的Model,并通过xDS API将配置信息下发给Envoy

Segmentation:Citadel结合Envoy完成,Citadel将证书颁发给每个服务的Envoy

因此,Istio的结构是这样的:

istio-architecture

可以看到Discovery和Configuration都是在Istio控制面完成的。Segmentation是由控制面的Citadel+数据面的Envoy代理完成的。

根据HashiCorp的说明,在Consul的Service Mesh方案中,Consul作为控制面运行,Consul agent实现了服务发现和配置存储。同时,Consul agent再结合Connect启动的Proxy,完成了网络流量的加密与认证。如下图所示:

consul-proxy-certs

请注意,Consul的KV系统只完成了配置的存储,并没有像Istio一样,可以对配置进行动态修改并下发到数据面,目前Proxy的配置也是通过读取文件,然后在启动或重启代理时传入的,因此我们认为Consul的配置管理能力还需要完善。此外,因为Consul Connect启动的Proxy是一个4层代理,无法提供路由、熔断、重试等更高级的微服务治理能力,因此也不需要获取服务治理的配置。这是Consul Service Mesh的方案中治理能力欠缺的地方。不过,HashiCorp也表示Connect在1.2版本中只是Beta状态,未来会继续围绕Connect开发新的功能,包括UI的增强,支持Envoy作为proxy,与Nomad和Kubernetes的集成等。

等等,听说Consul已经可以集成Envoy了?

没错!其实,在Consul1.2中,Connect已经可以通过指定一个可执行文件的路径来启动第三方代理了。Consul会以daemon模式启动第三方代理,在后台运行:

{
    "service": {
        "name": "webfront",
        "connect": {
            "proxy": {
                "exec_mode": "daemon",
                "command": ["/usr/bin/my-proxy", "--listen", "8800"]
            }
        }
    }
}

那么,按照通常的思路,是不是可以直接指定Envoy,让Consul启动Envoy来实现集成呢?理论上确实可行,但是Consul是无法管理第三方代理的配置的,Envoy的配置只能由用户自行定义,增加了运维成本,也不便于管理。

这时,就轮到HashiCorp好基友solo.io出品的Gloo Connect闪亮登场了!solo.io是一家提供Cloud Native工具和集成方案的公司,例如支持Kubernetes和Istio的微服务debugger squash,基于Envoy的function gateway解决方案Gloo。在刚刚过去的HashiDay 2018上,solo.io和HashiCorp联合发布了Gloo Connect。Gloo产品线本身很有意思,Gloo与英文单词”胶水”(Glue)的发音一样,暗示着Gloo会作为”胶水”,将Cloud Native中的各种服务粘合在一起,那么,Gloo Connect是如何把Consul和Envoy粘合在一起的呢?

Gloo Connect作为Consul的第三方代理,在启动时会把Consul Connect proxy的配置和Gloo的配置转换成Envoy的配置,并启动Envoy实例。同时,Gloo Connect也可以通过命令行对Envoy的配置进行动态修改。下面,我们根据Gloo Connect的官方示例来演示如何将Consul与Envoy集成。

根据Gloo Connect的Getting Started文档,Consul 1.2.0中,存在一个已知bug,运行示例需要下载一个预先编译的Consul版本,或者使用1.2.1版本,并把Consul配置到系统路径,或者当前目录。

首先,我们将gloo-connect项目clone到本地,进入getting-started目录,并编译运行service.go

$ git clone https://github.com/solo-io/gloo-connect/
$ cd gloo-connect/docs/getting-started
$ go run service.go &

service.go运行后监听8080端口,交替返回200和500状态码。接下来,我们执行当前目录下的run-consul.sh脚本,该脚本会在./run-data/consul-config目录下生成两个配置信息connect.jsonservice.json,前者将gloo-connect指定为Consul Connect默认的proxy,后者定义了两个服务microsvc1(即我们运行的service.go) 和test,并定义了两者之间的代理:microsvc1配置了一个空规则的代理,test配置了一个监听1234端口的代理,任何访问1234端口的请求,都将被当作testmicrosvc1的访问。最后,脚本启动consul,并设定consul的配置目录为./run-data/consul-config。此外,脚本还会运行gloo-connect和envoy,需要确保两者在系统路径或当前目录下并具有执行权限,gloo-connect项目的发布页面可以下载编译好的gloo-connect和envoy

脚本启动后,我们通过进程管理工具,可以看到Consul为每个服务启动了第三方代理gloo-connect,而每个gloo-connect又启动了一个Envoy进程,并把转换好的配置传给Envoy:

gloo-connect

此时,真正监听1234端口的就是Envoy进程了。下面,我们访问1234端口,会看到服务交替返回状态码200和500:

req-microsvc1-by-proxy

我们用gloo-connect命令为microsvc1的Envoy sidecar配置一条retry规则:

$ ./gloo-connect set service microsvc1 --http --retries=3

这样,当通过Envoy代理访问microsvc1时,如果遇到500,会重试3次,其实在第一次重试时就会返回200了,因此我们访问1234端口,每次都会返回200:

gloo-connect-retries

这样,我们就借助一个第三方工具gloo-connect,实现了Consul和Envoy的集成!需要说明的是,gloo-connect也处于早期阶段,目前支持的配置还比较少,除了retries,只支持http(是否使用http模式,默认为false)和timeout

目前来看,Consul Connect虽然还刚刚起步,但是已经让我们非常惊喜的看到了一个相对完整、简单易用的Service Mesh方案,通过第三方的集成,未来也许会有更多优秀的代理,甚至是用户自己定义的代理,不断加入到Consul的阵营中。随着Consul Connect的不断演进和HashiCorp的持续投入,我们相信,Consul会在未来的Service Mesh竞争中占有一席之地,也希望业界不断涌现Consul这样优秀的方案,给用户更多选择,让Service Mesh的世界越来越精彩。

不同于Istio、Conduit等“天生Service Mesh”的微服务方案,Consul本身是一个成熟的服务发现工具,其实现Service Mesh的思路非常有意思。在笔者看来,除去“控制面”“数据面”这种基于大的框架角度的划分,Service Mesh从功能上要完成三件事情:服务注册与发现,配置管理(存储、下发),服务治理。Consul在第一点已经做得非常好了,配置方面,Consul的集群内共享KV为实现配置管理奠定了坚实的基础,完全可以通过第三方工具、或者Consul自身增强的特性,基于KV实现配置的下发。服务治理方面与Consul本身关系不大,但是通过Connect,Consul具备了为服务配置代理或者集成第三方代理的能力,完全可以用已有的优秀代理来实现Service Mesh数据面,补足服务治理的能力。

Consul的方案,也给已经在业界流行并经过生产环境验证的“上一代”微服务框架带来很多启发。在Service Mesh概念流行之前,已有很多基于SDK的侵入式微服务框架。这些框架往往提供服务发现机制或者或者兼容开源的服务发现机制,有一些也提供配置的管理,只是需要在应用程序中引入SDK来实现服务治理,因此对于不同的语言,都要提供相应的SDK。但是相对于Consul,毕竟这些框架已经具备了微服务治理的能力,它们向Service Mesh转型的过程中,更需要的是将治理能力从SDK转移到网络层面的代理中。在这一点上,华为开源的微服务框架ServiceComb的做法非常值得参考。在ServiceComb中,已有Service Center实现服务的注册与发现,基于archaius的配置更新机制,以及提供治理能力的SDK(支持Java和Go两种语言),为了实现数据面的代理,ServiceComb团队将SDK中实现治理能力的代码剥离出来,基于此开发了网络代理Mesher,Mesher的治理能力与SDK是基本对等的,并且共享同一套配置。这样,已经使用SDK的服务,不需要做任何修改,而使用其他语言开发的新应用,则可以通过Mesher获得ServiceComb的服务治理能力。最终,ServiceComb成为同时支持SDK和网络代理的混合式微服务方案,也是一个真正意义上的Service Mesh方案。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK