Envoy 架构及其在网易轻舟的落地实践

2020-11-09 10:27:31 浏览数 (1)

Envoy 是由 Lyft 开源的一款高性能数据和服务代理软件,后被捐赠给 CNCF,并成为 CNCF 继 Kubernetes 和 Prometheus 之后第三个毕业项目。如今,Envoy 已经被广泛应用于 API 网关(Gloo、Ambassador)与服务网格(Istio、AWS Mesh、Open Service Mesh)之中,作为核心数据面组件。本文将详细介绍 Envoy 整体架构以及 Envoy 在网易数帆轻舟团队中落地实践的一些经验,希望能够增加大家对 Envoy 本身及其实际使用的了解。

1 什么是 Envoy

首先解释什么是 Envoy。Envoy 社区的定义是:Envoy 是一个开源的边缘与服务代理,专为云原生应用而设计。此处只需要抓住最核心的词--代理。

代理,本质属性就是做两件事:第一,代替客户端向服务端发送请求,第二,代理服务端向客户端提供服务。前者隐藏客户端,后者隐藏服务端。在开端代理软件领域,比较著名有 HA Proxy 以及 Nginx,而 Envoy 则是该领域的的一个后起之秀。作为为云原生而设计的高性能网络代理,它具备以下的优点:

  • 高性能:Envoy 具备非常优越的性能。尽管 Envoy 在设计之初没有将性能作为最终的目标,而是更加强调模块化、易测试、易开发等特性,可它仍旧拥有足可媲美 Nginx 等经典代理软件的超高性能。
  • 可观察:相比于 Nginx 等传统代理,Envoy 具备更好的可观察性,包括灵活可定制的日志,丰富的指标监控,原生的多种分布式跟踪协议支持等等,在后文中会做更详细的介绍。
  • 社区活跃:Envoy 的社区完全开放,不存在对应的商业版本,所以不用担心部分高级功能会被锁死在商业版当中。而且 Envoy 社区非常活跃,在 Envoy 使用过程中的问题或者新的功能需求向社区提出后都可以得到很快的反馈。
  • 动态配置:xDS 协议的提出使得 Envoy 几乎所有的配置都可以动态的下发、加载和生效,而无需重载进程。并且 xDS 协议已经成为了构建通用数据面接口协议(UDPA)的基础。
  • 易扩展:作为一款云原生网络代理软件,可扩展性自然也是必不可少。Envoy 提供了 L4/L7 Filter 机制,可以让开发者在不侵入 Envoy 主干的前提下在各个层级对 Envoy 进行扩展和增强。
  • 多协议:最后 Envoy 支持代理多种协议数据,包括 HTTP,Kafka,Dubbo 等等。此类多协议治理能力其实也是构建在 Envoy 强大可扩展性上的。因为 Envoy 所有的协议解析和治理都是使用 Filter 来实现的。基本上每一种协议代理能力都对应一个 L4 Filter。

Envoy 具备很多优点,但它核心的概念却很少,只有四个。基本上,Envoy 中绝大部分的模块和功能都是围绕着这四个概念展开的:

  • Listener(监听器):监听器负责监听数据端口,接受下游的连接和请求。作为代理软件,无论是正向代理还是反向代理,肯定要接受来自下游的连接并进行数据处理。Envoy 把相关的功能都抽象在了名为监听器的资源当中。
  • Cluster(集群):集群是对真实后端服务的抽象和封装,管理后端服务连接池、负责后端服务健康检查、实现服务级熔断等等。
  • Filter(过滤器):过滤器主要负责将 Listener 接收的客户端二进制数据包解析为结构化的协议数据,比如 HTTP 二进制流解析为具体的 Header、Body、Trailer、Metadata 诸如此类并进行各种流量治理。Envoy 中 Filter 分为多种类型,覆盖不同的层级和场景,是 Envoy 强大功能的源泉。
  • Route(路由):路由一般是作为某个协议解析 Filter 的一部分存在。筛选器解析出结构化数据后会根据路由中具体规则选择一个 Cluster,最终发数据转发给后端服务。 注:为了避免歧义,后文中 Envoy 中 Cluster 仍旧使用英文,而不是使用中文翻译“集群”。

以上的四个概念或者说资源类型,构成了 Envoy 架构最核心的骨架。此外,Envoy 将请求的来源方向称之为下游(Downstream),而请求转发的去向称之为上游(Upstream)。

总结来说:Downstream 请求自 Listener 进入 Envoy,流经 Filter 被解析、修改、记录然后根据 Route 选择 Cluster 将其发送给 Upstream 服务。

了解以上的核心概念,对于 Envoy 的使用者和学习者来说都非常有用,因为 Envoy 的功能模块和配置使用都是和这些核心概念紧密相关的。

2 Envoy 关键特性

在了解了 Envoy 的背景和最核心的概念之后,接下来介绍 Envoy 最核心的三个特性。分别是基于 xDS 协议的动态配置,基于核心概念 Filter 的可扩展性,以及可观察性。

2.1 Envoy xDS 协议

xDS 协议是 Envoy 带来的最大的改变之一。Envoy 使用 Protobuf 来定义几乎所有的配置,然后通过 xDS 协议实现配置项的动态化加载和生效。

xDS 全称是 x Discovery Service,x 表示某种资源。在 Envoy 当中,最为核心资源自然是 Listener、Cluster、Route 和 Filter。除了 Filter 之外,其他三种资源都有一个对应的 DS 与之关联,分别是 LDS、CDS、RDS:

  • 通过 LDS,就可以动态的下发 Listener 的配置,可以在运行时打开新的监听端口,关闭旧的监听端口或者更新某个 Listener 的 Filter 配置等等。
  • 通过 CDS,就可以动态控制 Envoy 可以访问哪些服务。比如说,新启动了一个 Service,就可以通过 CDS 向 Envoy 下发一个新的 Cluster。之后,来自客户端的流量就可以通过 Envoy 访问到对应的服务。
  • 通过 RDS,可以实现路由规则的动态更新和加载,可以动态的对路由表进行删改和生效。

前面最核心的四种资源之中,Filter 比较特殊,它的配置一般来说都嵌入在 LDS/CDS/RDS 之内。比如 Listener 中除了监听端口之外,最关键的配置内容就是在该端口下接收的数据需要执行哪些 Filter 链以及相关 Filter本身的配置项。这些最终都是通过 LDS 来下发。而 Cluster 中也有对应的 Filter 链,可以通过 CDS 更新和下发。而 RDS 则提供了路由粒度的 Filter 配置能力。

此外,还有一类关键 DS:EDS。EDS 是对 CDS 的补充。很多时候,服务一旦创建,就不会经常变动。但是服务的实例可能会经常变动,比如 Deploy 滚动更新之类的。EDS,就是用于在 CDS 不变的前提下,动态更新每个 Cluster 后面可用的实例。

大部分情况下,如果要做 Envoy 相关的工作,LDS/CDS/RDS/EDS 四种 DS 协议是必须了解的。

目前 Envoy 支持 gRPC 服务、Restful 接口以及磁盘文件三种不同类型的 xDS 数据源。以最经典的gRPC 为例,Envoy 会定义了一个流式的 gPRC Service,xDS 服务提供方只需要实现对应的 gRPC Service。然后在需要配置更新时,向 gRPC 流推送配置数据即可。Envoy 侧会接受配置数据,然后加载更新。

Restful xDS 就是由 Envoy 开放一个 Post 接口由配置的提供方去调用;磁盘文件 xDS 则是由 Envoy 去 Watch 指定文件的变化并在文件更新时更新配置。这两种 xDS 配置方法在实践当中都很少使用。

本质上 xDS 协议并不复杂,真正困难的在于如何把各种资源抽象出来并通过一种通用的协议来封装和传输、如何管理数据的版本以及保证更新过程流量的平稳。这些都涉及到一些实现的细节,就不再赘述了。

下图是一个相对实际的例子。当使用 Isito Pilot 作为 xDS Server 时,如何利用 xDS 来动态更新 Envoy 配置。一般情况下,是用户修改了 K8s 集群中的一些 CRD 资源亦或者注册中心有配置更新才会触发配置更新;之后 Pilot watch 到相关变化变更将相关变化抽象成各种 Envoy 中对应的资源,如 Listener、Cluster,然后通过各个 xDS 将对应资源推送到 Envoy。

2.2 Envoy 可扩展性

可扩展性是 Envoy 最关键的特性之一。Envoy 提供了一个 Filter 机制,让开发者可以完全无侵入的在各个层级定义和扩展 Envoy 功能。

前文介绍过,下游的数据从 Listener 流入到 Envoy。到了 Envoy 之后,Listener 就会把数据交给 Filter 来处理。但是 Filter 也是很多种的。

  • 最底层的,是 Listener Filter,在下游和 Envoy 连接建立之后,首先被执行处理,它主要用于获取连接协议相关信息并进行插件链匹配。在网关或者代理的场景中很少使用,因为端口和协议相对固定。可以缺省。
  • 在 Listener Filter 处理完之后,就会由 Network Filter。Network Filter 是 Envoy 管理各种协议和流量的基础。它负责对二进制数据进行解析并根据解析后的数据以及路由配置选择合适的 Cluster 把请求发出去。Network Filter 一般是不可缺省的,比如 HTTP 协议就得要配置 HCM 才能处理,而 Dubbo 协议也只有 Dubbo proxy 可以处理。
  • 最上层的就是 L7 Filter。Network Filter 把二进制数据解析完成之后,如果想要更复杂的流量治理怎么办?最简单是全部都让 Network Filter 自己做。但是这样显然不够灵活,扩展性不好。所以在这里又可以抽象出一层 L7 Filter,负责治理解析后的具体数据。L7 Filter 一般和 HTTP Filter 划等号,因为目前只有 HTTP 协议对应的 L7 Filter 做的最完善、功能支持最丰富。

总结来说就是:Listener Filter 处理连接、Network Filter 处理二进制数据、L7 Filter 处理解析后结构化数据。

其中,L7 Filter 都是作为某个 Network Filter 的子 Filter 存在。这也很好理解,L7 处理解析后结构化数据,总要有一个 Network Filter 来解析二进制数据然后把解析后数据传递给它。

下图是一个相对完整的 Envoy 插件链执行流程图。原本应该还有所谓的 encoder/decoder 层级的,它们才是真正负责协议解析的组件。但是在目前实现当中,encoder/decoder 一般都是嵌在 Network Filter 中作为某个 Network Filter 的一部分,所以这里干脆简化掉了。


Envoy 可扩展性方面还有最后一个问题:功能更新和升级带来的运维成本。Envoy 是使用 C 来实现的。每实现一个新的功能性的 Filter,无论是复杂的 L4 Network Filter,还是相对简单的 L7 HTTP Filter,都需要重新构建整个 Envoy。相比于配置的动态化,Envoy 功能上似乎没那么动态化。

为了解决这个问题,Envoy 社区提出了基于 WASM 的 Filter 扩展机制。WASM 是一种前端的技术,最初设计是用于加速 JS 脚本以及将 C 等语言带到 WEB 上。Envoy 内置了 WASM 虚拟机,开发者可以将自己的功能扩展使用如 C 、Go 、AS 等各种语言开发,然后编译成 WASM 字节码文件。之后,Envoy 动态的加载文件就可以实现功能的增强。

此外,Envoy 社区也提供了 Lua Filter,可以让 Envoy 通过动态的下发一个 Lua 脚本来实现功能扩展。举例来说,用户可以使用 Lua 来编写一段 Lua 逻辑,只要实现 envoy_on_request 和 envoy_on_response 两个函数,然后将 Lua 脚本通过 xDS 动态的下发给 Envoy,Envoy 在处理请求的过程中,就会执行对应的 Lua 脚本代码。

目前 WASM 还没有完全落地,但是 Gloo、Istio 社区都在力推,前景美好。而 Lua 相对来说,比较成熟,但是社区提供的功能较弱一些。所以轻舟团队也做了一些优化工作,后文当中也会讲到。

2.3 Envoy 可观察性

接下来需要介绍的 Envoy 关键特性是可观察性。可观察性其实是一个很大的主题,完全可以做一个单独的文章分享。但是这里就是主要介绍一下可观察性的概念以及 Envoy 的一些优势。希望有机会在其他文章中在对 Envoy 可观察性做更详细的介绍。

可观察性是指在软件程序运行过程中,获取软件程序内部状态或者发生的一个能力。如果程序执行起来之后,开发者和运维人员就对内部状态一无所知,那很多问题就根本无法定位。

按照侧重面的不同,Envoy 的可观察性主要依靠三个部分构成。分别是日志、指标、以及分布式追踪。

  • 日志:日志是对 Envoy 内部事件(或者直白的说,就是一个请求处理过程)的详细记录。Envoy 提供了非常丰富的日志字段,而且可以灵活的配置。同时还提供了类似 L7 Filter 一样的 Access Log Filter 来自定义日志过滤和筛选、二次修改之类的能力。
  • 指标监控数据是对 Envoy 内部事件的数值化统计,本质上,指标数据就是一个个计数器。记录诸如请求次数、正确请求次数、错误请求次数之类的统计数据。指标监控数据要结合 Prometheus 之类的时间数据库来使用,用于观察 Envoy 的整体流量趋势。 Envoy 提供了非常丰富的指标数据。包括 xDS 指标监控, Cluster 维度的请求统计、连接统计,Listener 维度请求统计、连接统计。而且对于连接上发生的一些事件也会进行记录。比如连接断开了,根据断开的原因不同,会提供不同的计数,这样就非常方便开发者去定位问题。
  • 指标监控和日志描述的都是单个实例内部的状态。但是在微服务架构中,往往不同的服务实例内事件是有关联的。比如服务 A 调用服务 B ,服务 B 又调用了服务 C 和 服务 D。在服务 A B C D 中的 4 个事件是具有因果关系的。分布式跟踪就是为了记录这种因果关系,进行全链路的监控。 分布式跟踪一般都要对接分布式跟踪后端。服务将自己内部事件的一些数据上报给分布式跟踪后端,后端做数据聚合和分析,构造出服务拓扑和链路关系。分布式跟踪是快速发现微服务集群中问题点和性能瓶颈的利器。Envoy 本身原生提供了对 ZipKin、OpenTracing 等多种分布式跟踪系统。目前轻舟团队也在和社区合作,一起开发 Envoy 对 SkyWalking 的支持。

xDS 协议、可扩展性、可观察性,这是我个人认为的在 Envoy 之中,最为核心的三个特性。它们横跨多个模块,基本上,可以说,这三者就是使得 Envoy 如此独特的基石。

3 Envoy 落地实践

最后分享从 Envoy 数据面的视角看 Envoy 如何在网易轻舟落地。通过这一部分的介绍,大家可以更好地理解 Envoy 的各个特性是如何在实践之中发挥效用的。

最简单的用法是把 Envoy 当作一个纯粹的七层网络代理,没有任何其他的依赖,只有单独的一个 Envoy,就像使用单体的 Nginx 一样。可以使用静态配置,或者基于磁盘文件作为数据源的 xDS 协议。

对于广大的个人开发者以及个人网站维护者而言,如果需要一个七层代理,不妨尝试一下 Envoy,立刻就能拥有一个高性能的网络代理,而且具备丰富的观察手段。同时对于希望深入学习 Envoy 的开发者来说,这种方法也是上手 Envoy 最快的方式。

此外,也可以将 Envoy 应用于 API 网关之中。使用 Envoy 作为核心数据面,结合控制台、日志分析系统、指标监控报警系统、APM 链路跟踪系统、外部注册中心等等来构建一个功能完善、可观察、易扩展的 API 网关。也可以将 Envoy 作为 Service Mesh 服务网格中数据面,接管微服务集群东西向流量,构建全新的微服务架构。

注:有兴趣的同学也可以了解一下 Envoy Mobile 项目,那是另一个更加有趣的场景。

接下来要着重介绍的,就是Envoy 在网关和网格两种场景下的落地和实践。

3.1 整体架构

Envoy 在网易轻舟实践中的整体架构如下。当然,图中简化了很多细节,只留下了核心的一些模块。

Envoy 会作为 API 网关以及服务网格数据面,承接整个微服务集群中东西向和南北向的流量,实现微服务集群的全流量接管。

在 Envoy 之上,就是所谓的控制面。前面也说了,Envoy 使用 xDS 协议来实现配置的动态化加载,这些配置本身就是各种各样的流量治理策略,比如如何路由请求、是否限流、是否熔断、如何健康检查等等,它们依赖一个 xDS 服务提供方来向 Envoy 推送。控制面主要的工作就是作为 Envoy 配置提供方来负责整个集群中的 Envoy 配置,并以此实现集群中流量的治理和控制。

在网易轻舟,控制面是基于开源的 Istio Pilot 做的增强和扩展。Istio 应该是目前开源社区发展最好的服务网格软件。轻舟服务网格控制面使用 Isito 是一个自然而然的选择。而网关控制面,则是直接复用的网格控制面中的核心组件 Pilot。这样,在轻舟 API 网关以及轻舟服务网格之中,控制面和数据面的组件都是完全共享的,实现了基础设施技术栈的统一。不用单独维护工程和代码,功能也可以通用,只是部署模式和配置分发略有差异。

而在 Istio Pilot 之上,则是轻舟单独抽象的一层 API Plane。它是对 Pilot 以及集群基础设施如 K8s API 的一个封装和聚合,屏蔽下层的细节。

在 API Plane 之上,就是轻舟控制台,用户操作和管理集群的入口。通过对接 API Plane,也可以实现自定义的控制台。

此外,Prometheus 组件会负责自动发现并采集集群中各个组件(尤其是 Envoy)的指标监控数据。轻舟控制台会把一些关键指标数据进行可视化展示。

APM 系统会实现全链路的跟踪和集群服务拓扑分析。轻舟原本是使用私有协议,目前将要切换到 SkyWalking,当然,如果业务方或者客户有需求,ZipKin 之类的也可以支持。

最后,Envoy 日志会被采集到 ELK 日志分析系统之中,轻舟以此来实现各种审计功能。

基本上,轻舟 API 网关和轻舟服务网格大体架构就是如此。接下来,将分别详细介绍一下轻舟 API 网关以及轻舟服务网格。

3.2 轻舟 API 网关

API 网关是整个微服务集群的流量入口,主要用于解决微服务架构中服务暴露的问题。相比于数据面纯粹的代理,API 网关更强调流量的治理

在 API 网关之中,个人认为,从一个数据面开发的角度看,最重要的是五个点,分别是:性能、可观察性、治理能力、稳定性以及控制台。

  • 性能其实是很模糊的一个点。因为性能的影响因素太多了,网络、CPU、业务复杂程度。一般来说能够满足业务或者客户需求的性能就是好性能。当然,考虑到需求会变,性能留下的余地当然是越大越好。
  • 第二个,可观察性。作为流量的入口,在出现问题或者故障时,毋庸置疑的,网关必然会成为第一个怀疑对象,同时也往往是问题排查的入口。所以网关应该具备: 所有此类能力都依赖可观察性来保障,详细的日志、丰富的监控、及时的报警、准确的链路分析,这些才能撑起一个 API 网关所必须的可观察性。
    • 快速自证清白的能力;
    • 帮助业务方或者客户快速定位到问题的能力;
    • 万一网关真的出现故障第一时间明确根因并做处理以减少损失的能力;
    • 提前预见风险把问题扼杀在摇篮的能力。
  • 第三个是治理能力,包括鉴权、限流、熔断等等对集群入口请求进行检查、过滤、筛选、修改的能力。而且由于业务方和客户的需求千变万化,所以这些能力必须很容易扩展甚至可以让客户自定义。
  • 第四个是稳定性,这一方面非常重要,非常非常重要。网关出问题波及的是整个微服务集群。但是在这一方面实在没有花巧可言,它更多是依赖流程和规范的来保障。如果说要有一个判断标准的话,就是必须具备实际落地案例并经过大规模流量验证,那才可以说,它大致是稳定的网关。
  • 最后是控制台,它是管理人员操作和使用的入口。上述的四项都是一个优秀 API 网关必须具备的基础,而控制台的易用程度则直接决定网关本身的易用性。

比上文概览图详细一些的轻舟 API 网关架构简图如下所示。在控制面:控制台作为操作的入口,通过 API Plane 操作 K8s 或者 Pilot。Pilot 发现服务和配置并抽象成 xDS 协议约定的数据推动给 Envoy。在数据面:Envoy 接收来自控制面的配置数据并利用可扩展的各种 Filter 完成流量的治理。这里不过多展开,接下来分别介绍轻舟 API 网关对于前述五个要点的实践。

性能

在性能方面,Envoy 本身就是保障,在都使用最简配置的情况下,它的性能与 Nginx 相差仿佛,比 HA Proxy 要稍差一些。根据实际测试结果,一个 8C8G 的轻舟 API 网关,在容器网络下,可以达到 8w QPS,而物理网络下,可以达到 10w QPS。

并发量

Fails

TPS

MRT(ms)

50

0

66089.44

0.71

100

0

75219.25

1.27

300

0

81199.04

3.59

500

0

82626.56

5.92

在 Envoy API 网关性能保障方面,有三个小的 Tips 分享一下:

  • 第一,Envoy 提供了一个 virtual host 的概念,对应着域名。每个 virtual host 下都会有一组对应域名下的路由。virtual host 的搜索复杂度是 O(1),而路由的搜索和匹配是线性的。所以一定要合理的规划域名和路由,使用 virtual host 来把路由表隔离开来。
  • 第二,Envoy 日志提供了 JSON Access log。使用 JSON log,可以方便后续的日志分析和解析,但是性能极差。Envoy 可以把日志输出到 gRPC 服务,没有验证过它在这种情况下的性能如何。但是JSON Access Log 写入磁盘文件时,由于会使用 ProtoBuf 自身提供的 JSON 序列化方法,会严重影响性能。
  • 第三,对于核心的治理功能扩展,使用原生的 C 实现,并且使用 PPROF 功能来定位性能瓶颈做优化。这里是最需要开发去下功夫打磨的地方。性能,大部分时候都是需要一点点挤压出来的。

可观察性

在可观察性方面,轻舟 API 网关构建了完整的监控体系。由前文可知,Envoy 本身就提供了非常丰富的指标监控数据,以及灵活的日志系统,所以重点是如何把这些 Envoy 本身提供的能力利用起来的问题。

首先是指标监控,前面也提到过,轻舟使用的是 Prometheus 数据库。指标监控就是一个个计数器,而把这些计数器在时间轴上排列出来时,就可以形成线和图,用于观察整个网关的流量趋势。对于一些核心的数据,我们会对接到控制台。同时,也提供了 Grafana 来把 Prometheus 中的数据做可视化。此外,Prometheus 还负责提供根据指标数据监控告警的能力。比如说,一段时间内,4xx/5xx 的请求数暴涨,这个时候就立刻会把相关告警推送给对应的负责人。这些告警能力,也是通过控制台来对外暴露,让业务方或者客户配置。

其次是日志,Envoy JSON 日志存在性能问题,普通的文本日志解析起来又差一些。所以这里有一个取巧的办法,使用文字字符串拼接成 JSON 格式字符串。同时,使用日志提供的 Log Filter 对包含特殊字符串的字段做转义,这样就可以保证性能的同时,输出标准的 JSON 字符串了。

而分布式跟踪,Envoy 原生支持了 Zipkin、OpenTracing、LightStep 等多种分布式跟踪系统,可以直接对接。而我们自己开发了 SkyWalking 的 Tracing 支持,还在持续的优化之中,而且也准备贡献给社区。

治理能力

前文之中花了不小的篇幅来介绍 Envoy 的可扩展性,实际上都是为了介绍 API 网关的治理能力做铺垫。得益于 Envoy 强大的 L4/L7 Filter 扩展机制,开发者可以很轻松地在不侵入 Envoy 源码的前提下,扩展出各种流量治理能力。Envoy 提供了全局/本地限流、黑白名单、服务/路由熔断、动静态降级、流量染色等等流量治理功能,基本已经覆盖了 API 网关的绝大部分需求了。

而考虑到各个方业务的特殊性,也考虑到 Envoy C 扩展的开发门槛,以及运维部署的成本,所以轻舟网关也做了一些其他方面的工作。首先是 WASM 扩展,它肯定是社区的发展方向,而且兼具性能、安全、动态分发、多语言支持多种优点。目前轻舟正在内部预研中,用它开发一些简单插件,评估性能和稳定性。

另一个就是利用 Lua 脚本扩展。在这方面,轻舟主要做了两个方面的动作:

  • 第一个,是针对社区本身的 Lua 插件的增强。原本的社区的 Lua 插件是一个全局插件,一旦开启,就会对所有请求生效,而且可以执行的脚本还是唯一的。而轻舟对它做了一个很大的修改,可以支持路由粒度的 Lua 脚本的分发和配置。结合控制台以及 xDS 协议,最终实现的效果就是:用户或者开发者可以控制台上直接写一段 Lua 脚本逻辑并动态下发到某一条路由上去。对于命中该路由的请求,Lua 脚本就会生效执行。不同的路由上,可以执行完全不同的脚本。而且,也可以在控制台灵活的去修改它。该功能目前已经贡献给了社区。
  • 第二个,是轻舟参考 OpenResty 搭建了一个使用 Lua 脚本来构建 Envoy Filter 的开发框架。这里 Lua 相比于前面那种的动态化脚本功能更强大,更像是一个完整的 Filter 包括配置的能力以及更加丰富的 API。内部也正在评估开源的相关事宜。

稳定性

网关稳定性除了代码质量之类的基础保障之外,最重要的就是依靠流程和规范去保证。但是这一方面的经验往往不太通用,不同的公司、组织可能完全不一样。所以,这里也简单的列出了一些比较大而化之的小 Tips:

  • 完善测试:通过单元测试、集成测试、混沌测试、常态化性能及稳定性测试构建完善的测试体系,并保证所有功能都经过完善的测试之后,才能应用于生产环境。
  • 使用生产 灰度双集群策略。同时双集群同时承载线上流量。所有变更自灰度集群发起,经过充分的生产流量验证之后,再升级至生产网关。升级过程之中,通过四层 LB 分流保障流量安全和平滑过渡。需要注意,除了灰度过程中,其他任何时刻都该保证生产和灰度的完全一致,包括但不限于镜像的版本、配置项、网络环境等,这需要控制面要做好两个集群配置相互隔离、不会相互干扰的同时,又保证好双方的一致以及同步。
  • 将生产环境变更流程化、规范化,尽可能使用脚本或者 DevOps 工具,减少人为操作引入的不确定性。

控制台

控制台主要负责两方面的工作:第一,将指标、监控、APM 数据聚合并做可视化展示,帮助开发人员快速定位问题;辅助团队了解流量动态;第二,简化各个功能模块使用、通过封装对外暴露友好、易操作的交互页面。尽可能减少直接 CRD/配置文件操作,降低风险。

前面提到的四项能力都是 API 网关的基础能力。但最终网关好不好用,一般都取决于控制台。这其实就需要产品去思考如何设计API 网关的问题了。个人暂时还没有突破到这个领域,没有什么特别的经验分享,只是要说明,对 API 网关来说,一个对用户和开发者友好的控制台很重要。

以上是关于轻舟 API 网关的一些分享。相对来说,API 网关是一个很成熟的技术,有很多优秀的实践,经典的比如 Kong,新兴的比如 Gloo、Ambassador,如果大家有兴趣,都可以多多了解一下。

3.3 轻舟服务网格

接下来再介绍一下 Envoy 在轻舟服务网格中的应用。相比 API 网关,服务网格是一个相对新颖的服务架构。最初微服务架构提出是为了解决单体应用复杂度太高,伸缩性差等问题。但是微服务架构本身又带来了新的挑战:1)服务的拆分使得需要付出额外的成本进行服务发现与服务管理管理、较为复杂的服务调用链路导致故障以及问题定位困难等;2)服务暴露问题。

后者可以使用 API 网关来解决。而前者,一般通过引入一些 RPC 框架来应对。但是 RPC 也有问题,SDK 和语言绑定,作为基础设施的 RPC 框架与业务本身绑定;SDK 提供方需要为不同的语言、不同的服务组件提供不同的 SDK 并进行维护;SDK 功能更新,业务也必须更新甚至要做修改和适配;跨 RPC 协议交互困难。

所以业界又提出了一个全新的架构:为每一个业务进程启动一个 Sidecar 进程,它来代理业务进程的所有进出流量,并且将服务发现、流量治理、监控、安全等一系列必须但是又业务本身无关的功能剥离出来下沉到 Sidecar 中。

下图是一个很简单直观的 Sidecar 工作原理图。当业务进程作为服务提供方时,会由 Envoy 作为反向代理,流量先到 Envoy 再到业务本身。当业务进程作为客户端希望调用其他服务时,Envoy Sidecar 会作为正向代理,流量也要经过 Envoy 流转。一系列的流量的治理功能就可以从 SDK 剥离到独立的 Sidecar 进程中。

至于流量拦截的过程,一般通过修改 IP Tables 实现,对业务可以做到无感知。当然,也可以做到由业务进程直接向 Sidecar 某个特定端口发送请求的方式来让 Envoy 拦截流量,这样做可以减少 IP Tables 的开销,但是 Sidecar 就没有办法做到透明。

在服务网格实践当中,诸如性能、七层治理能力、可观察等问题和 Envoy 网关实践中都类似,毕竟服务网格本质上也只是无数代理的聚合,很多特性都是通用的。所以这里主要说的是另外两方面的问题:

  • 多协议治理能力:Envoy 网关主要关注的是 HTTP 协议。因为无论集群内部使用何种协议,对外暴露的时候,往往都是以 HTTP 来暴露的。但是当 Envoy 要在服务网格中实践的时候,就必须关注到集群内部的各种私有 RPC 协议以及 gRPC、Dubbo、Thrift 等已经使用得非常广泛的 RPC 协议。如果要把微服务中各种中间件如 Redis、MQ 也纳入到服务网格管理中来,就还必须要 Envoy 支持各个中间件协议。这需要服务网格具备多协议支持和扩展的能力。
  • 网格平滑落地:作为一种全新的技术架构,对现有业务冲击是比较大的。因此必须保障现有业务接入服务网格过程的平滑和稳定,实现按需接入。 而且作为基础设施存在的服务网格本身的更新和迭代不能够对业务本身造成明显影响,应当在提供响应治理能力以及数据监控的同时,尽可能的使得服务网格透明化。

多协议治理

在多协议治理能力方面,再次需要提到 Envoy 提供的丰富的可扩展性,它的 L4/L7 Filter 扩展机制。前面在网关部分,主要利用的其实是 Envoy 的 L7 扩展能力,大部分的 Filter 开发都是针对 HTTP 协议,在 Envoy 提供的 HCM 基座之上扩展 HTTP Filter。

而 L4 Filter 则允许开发者扩展新的基座,实现多协议治理能力的增强。而且新开发的 L4 Filter 之上,同样可以构建对应协议的 L7 Filter。

现在社区已经提供了大量的 L4 Filter。几乎涵盖的大部分的常用的协议,诸如 Dubbo、Thrift、Redis、RocketMQ等等。但是相比于 HTTP 协议的 HCM,它们的能力还比较孱弱,所以具体实践之中,必须对它们做增强。目前,轻舟主要针对 Thrift、Dubbo 和 Redis 做了优化和扩展,包括可观察性增强、动态 RDS 支持、七层治理能力等等。

理论上,任何新的基于 TCP/UDP 的协议,都可以通过开发新的 L4 Filter 接入到服务网格中来。

平滑接入

针对第二个网格平滑接入的问题,自然需要控制面的大量投入,实现诸如 Envoy Sidecar 版本管理、动态流量劫持等能力。但是本文主要针对数据面 Envoy 进行分享。所以此处要介绍的是在服务网关平滑接入过程之中发挥重要作用的一项关键特性:热重启。

Envoy 提供了热重启机制保证在需要重新载入 Envoy 或者需要更新 Envoy 二进制过程中,Envoy 始终处于可用状态。新旧两个 Envoy 进程通过 UNIX Socket 进行通信和数据交换。同时,部分的文件通过共享内存在新旧 Envoy 进程之中共享。

具体来说,当新的 Envoy 进程启动之后,新 Envoy 进程会载入配置(通过 BootStrap 以及 xDS 等)。在载入 Listener 过程之中,会通过 UNIX Socket 从父进程中获取对应 Listener 的套接字描述符(实际上就只是一个整型数)。如果不存在,则创建新的套接字。

当子进程配置加载完成并启动工作线程事件循环之后。子进程向父进程发起请求,由父进程优雅的清理监听套接字上的剩余连接以及正在进行中的请求。此外,子进程在工作线程就绪之后还会尝试从父进程处获取父进程当前的指标监控数据(Counter 和 Gauge)并合并到当前的指标监控数据中来。如此可以保证新旧两个进程监控指标数据的一致性。

Envoy 热重启机制使得 Envoy 在重新载入的过程当中,始终可以正常的对外提供服务,让作为基础设施的 Envoy 可以做到业务无感知的热升级和热替换,为业务平滑接入服务网格,保障服务网格整体的稳定性提供了基础。

要做到真正的平滑接入,单单数据面热重启自然是不够的,Envoy Sidecar 版本管理和分发,动态流量劫持,配置智能加载等等控制面能力也是必不可少。限于篇幅与主题,此处不再展开,有机会再分享。

4 That Is All

本文整体介绍了 Envoy 以及构筑在 Envoy 之上的网关网格技术。服务网格、API 网关本身都是非常宏大的主题,涉及知识面很广,其中的某一个小点都有很多可以挖掘的地方。限于作者目前的知识和理解,本文难免存在不够全面或错谬之处,敬请读者指正。

作者简介

王佰平,网易数帆轻舟事业部研发工程师,负责轻舟 Envoy 网关与轻舟Service Mesh 数据面开发、功能增强、性能优化等工作。对于 Envoy 数据面开发、增强、落地具有较为丰富的经验。

0 人点赞