中文站

【Service Mesh基础】Envoy-入门介绍与xDS协议

微服务架构应用渐广,服务网格热度陡升,赋予了API网关越来越重要的地位。API 网关将各个微服务对外暴露的接口聚合起来,所有接口调用都需要通过 API 网关进行访问。作为网格或者一众微服务的流量入口,API网关负责流量分发、监控、限流、日志收集等等,是微服务架构和服务网格中不可或缺的关键组件。而提到API网关,则不可不提到Envoy。作为一款强大的开源服务代理软件(类比于Nginx),Envoy为许多API网关产品提供基石和支撑。

Envoy是由lyft开源的边缘和和服务代理,后被捐赠给CNCF基金会。可以说,Envoy已经是云原生时代数据平面的事实标准。新兴API网关如Gloo,Ambassador都基于Envoy进行扩展开发;而在服务网格中,Istio、Kong社区Kuma、亚马逊AWS App Mesh都使用Envoy作为默认数据面。

与HAProxy以及Nginx等传统Proxy依赖静态配置文件来定义各种资源以及数据转发规则不同,Envoy几乎所有配置都可以通过订阅来动态获取,如监控指定路径下的文件、启动gRPC流或轮询REST接口,对应的发现服务以及各种各样的API统称为xDS。Envoy与xDS之间通过Proto约定请求和响应的数据模型,不同类型资源,对应的数据模型也不同。

以Istio中Pilot为例,当Pilot发现新的服务或路由规则被创建(通过监控K8S集群中特定CRD资源变化、或者发现Consul服务注册和配置变化),Pilot会通过已经和Envoy之间建立好的gRPC流将相关的配置推送到Envoy。Envoy接收到相关配置并校验无误之后,就会动态的更新运行时配置,使用新的配置更新相关资源。Pilot工作原理如图1所示。


图1. Envoy配置下发流程

利用xDS协议,Envoy可以实现配置的完全动态化,配置实时更新而无需重启Envoy或者影响业务。此外,利用其L3/L4/L7 Filter机制,Envoy可以完全无侵入的扩展各种强大的功能。利用其内置的Tracing机制和Stats模块,可以很方便的实现对流量的跟踪以及监控,保证Envoy中流量的可观察性。无论是Envoy Filter或者其Stats,都包含大量的内容,此处不会详述。本文只会概括性介绍Envoy中一些关键概念以及xDS相关内容。

资源类型

资源本身是很抽象的概念,此处所谓的资源是指Envoy根据相关配置创建出来的具有某种特定功能或者目的的实体。在Envoy中,具有最为核心的四种资源:Listener,Router,Cluster,以及Filter。

Listener:Envoy工作的基础

简单理解,Listener是Envoy打开的一个监听端口,用于接收来自Downstream(客户端)连接。Envoy可以支持复数个Listener。多个Listener之间几乎所有的配置都是隔离的。Listener配置中核心包括监听地址、Filter链等。

Listener对应的配置/资源发现服务称之为LDS。LDS是Envoy正常工作的基础,没有LDS,Envoy就不能实现端口监听(如果启动配置也没有提供静态Listener的话),其他所有xDS服务也失去了作用。

Cluster:对上游服务的抽象

在Envoy中,每个Upstream上游服务都被抽象成一个Cluster。Cluster包含该服务的连接池、超时时间、endpoints地址、端口、类型(类型决定了Envoy获取该Cluster具体可以访问的endpoint方法)等等。

Cluster对应的配置/资源发现服务称之为CDS。一般情况下,CDS服务会将其发现的所有可访问服务全量推送给Envoy。与CDS紧密相关的另一种服务称之为EDS。CDS服务负责Cluster资源的推送。而当该Cluster类型为EDS时,说明该Cluster的所有endpoints需要由xDS服务下发,而不使用DNS等去解析。下发endpoints的服务就称之为EDS。

Router:上下游之间的桥梁

Listener可以接收来自下游的连接,Cluster可以将流量发送给具体的上游服务,而Router则决定Listener在接收到下游连接和数据之后,应该将数据交给哪一个Cluster处理。它定义了数据分发的规则。虽然说到Router大部分时候都可以默认理解为HTTP路由,但是Envoy支持多种协议,如Dubbo、Redis等,所以此处Router泛指所有用于桥接Listener和后端服务(不限定HTTP)的规则与资源集合。

Route对应的配置/资源发现服务称之为RDS。Router中最核心配置包含匹配规则和目标Cluster,此外,也可能包含重试、分流、限流等等。

Filter:强大源于可扩展

Filter,通俗的讲,就是插件。通过Filter机制,Envoy提供了极为强大的可扩展能力。在Envoy中,很多核心功能都使用Filter来实现。比如对于Http流量和服务的治理就是依赖HttpConnectionManager(Network Filter,负责协议解析)以及Router(负责流量分发)两个插件来实现。利用Filter机制,Envoy理论上可以实现任意协议的支持以及协议之间的转换,可以对请求流量进行全方位的修改和定制。强大的Filter机制带来的不仅仅是强大的可扩展性,同时还有优秀的可维护性。Filter机制让Envoy的使用者可以在不侵入社区源码的基础上对Envoy做各个方面的增强。

Filter本身并没有专门的xDS服务来发现配置。Filter所有配置都是嵌入在LDS、RDS以及CDS(Cluster Network Filter)中的。

xDS以及各个资源之间的关系如图2所示。


图2. xDS协议

两种角色

作为一个服务代理软件,Envoy并不限定自己的使用方法。它最常扮演的是两种不同的角色,一种是作为集群流量入口API网关(Gateway),管理南北流量;一种是作为服务Sidecar,拦截并治理服务网格中东西流量。


图3. Envoy的两种角色

API网关负责集中管理集群或者网格对外暴露的接口,为集群外或者网格外客户端调用集群内或网格内服务提供了统一的流量入口和治理方案,其重要性自然不必多言。因此,此处重点解释Envoy作为Sidecar的功能和作用。

Sidecar是当前微服务领域先进的实践。微服务框架解决了单体应用过于复杂,难以维护,语言绑定,不易扩展等问题,使得多种语言、多种框架的多个微服务构成一个整体,并通过API网关向外提供统一接口。但是微服务框架也带来了服务治理复杂、故障定位难、服务注册、服务发现等新挑战。

克服这些挑战,业界有如下三种技术方案。

○ 第一种,服务自身感知服务发现、服务注册等流程,每个服务去实现相关逻辑保证服务之间协同工作正常。但是此类方案过于复杂,成本太高,每个服务都必须修改代码,难以维护。

○ 第二种,通过在服务中集成服务发现、服务注册等相关功能的SDK,由SDK去完成服务治理的相关功能。SDK可以减少服务业务代码的复杂性,而且也避免了不同服务间的重复冗余的代码编写。但是SDK首先会带来语言绑定的问题,否者必须为每种语言实现对应的SDK;其次,SDK和服务绑定,所以当SDK升级时,也必须重新编译甚至适配SDK。

○ 第三种,就是使用Sidecar拦截服务的进出口流量。Sidecar与服务从功能、到语言到框架都完全解耦,是两个完全独立的进程。Sidecar升级和服务更新互不影响。服务可以专注于业务功能,所有服务注册、服务发现、服务治理等能力都放置在Sidecar中实现。


图4. 服务网格三种方案

作为Sidecar时,Envoy通过修改IP Table实现对服务的进出口流量的拦截,并进一步实现对进出口流量的管理。每个Sidecar通过xDS协议和控制面交互,获取集群中其他服务的相关信息以及各种服务治理相关(鉴权、分流、流量复制等等)的配置。服务本身只需要专注于业务逻辑,所有网络流量相关的工作都委托给Envoy Sidecar。

增量xDS

Envoy通过xDS协议与控制面实现配置数据的交换。当控制面检测中配置变化(比如通过Kubernetes Watch到新service或者其他的CRD资源更新),会向Envoy发送一个discoveryResponse来将更新后的配置下发到Envoy。之后,Envoy主线程在接受到数据之后,通过向各个工作线程中追加配置更新事件来完成配置的实际更新和生效。

但是,需要注意的是,控制面下发的discoveryResponse是一个全量的配置。换言之,哪怕是修改了一条路由中的一个小小请求头匹配,所有Listener、Cluster、Router都必须更新,Envoy会用接受到的新配置替换旧配置。使用现有更新方案虽然逻辑简单明了,但是在负载较多、配置量较大时,会造成大量的流量浪费和不必要的计算开销。

尤其是对于Sidecar模式下的Envoy,该问题会更加的明显。网格中服务需要访问其他服务时,其流量首先会被Envoy Sidecar所拦截,之后由Sidecar将请求转发给对应的服务。由于Sidecar并不了解其代理的服务依赖网格中哪些其他服务,所以它会记录服务网格中所有服务的相关信息。但是,事实上一个网格服务往往只会依赖网格中少量的几个服务。

因为上述问题,Envoy社区提出了delta xDS方案,实现增量的xDS配置更新。简单的说,在delta增量更新方案中,当配置发生变化时,只有发生变化的一项配置(配置的最小单位为一个完整的proto message)需要下发和更新。

基于delta增量更新方案,可以实现以下三种新的功能:

○ Lazy loading:按照具体需要订阅相关资源。全量xDS中,每个Envoy Sidecar都会缓存大量的Cluster配置,但是实际部分Cluster从未被访问过,甚至将数据流量导向此类Cluster的相关路由配置都不存在,此类的Cluster配置只会浪费内存和降低Envoy效率。使用delta增量更新方案,可以在实际的配置被使用时再订阅该资源,从控制面获取相关配置(首次访问性能会受到一定影响)。

○ 增量更新:当部分资源更新时,如某个Cluster配置发生变化,某条Router修改了参数,那么只有对应的Cluster或Router配置会被下发和更新。在负载较多、配置量较大时,该功能可以有效减少网络内因配置更新而引入的数据流量。

○ 缓存擦除(或者说on demand loading:根据Envoy当前负载实际请求动态调整订阅资源类型,对于不再活跃的配置,取消订阅,从Envoy内存中擦除,直到相关配置再次被使用。通过该功能,可以有效限制Envoy配置所占用的数据量,在超大规模应用场景中,可以有效减少Envoy内存开销。

本文简单的介绍了Envoy中的一些基础概念以及xDS相关的简单内容,希望对于那些对Envoy有兴趣的同学能够有所帮助。本文涉及的内容只是Envoy中的冰山一角,很多内容也只是简单略过,如有纰漏,希望能够一起讨论,共同进步。

来源:网易云

作者简介:

王佰平,网易杭州研究院云计算技术部轻舟团队工程师,负责轻舟Envoy网关与轻舟Service Mesh数据面开发、功能增强、性能优化等工作。对于Envoy数据面开发、增强、落地具有较为丰富的经验。