Envoy具有极为强大的可扩展能力,通过L3/L4/L7 插件机制,可以在各个层级对Envoy实现功能扩展,本文概述了Envoy的插件模型并详细介绍了HTTP插件配置相关的内容,但不涉及任何插件开发的细节,希望对Envoy的使用者或者更上层的工作能有所帮助。
本文由作者授权发布,未经许可,请勿转载。
作者:王佰平,网易杭州研究院轻舟微服务平台研发工程师
作为服务代理软件中的新秀,Envoy具有极为强大的可扩展能力,这也是网易轻舟微服务平台采用Envoy的重要原因之一。利用Envoy的L3/L4/L7 插件机制,网易轻舟微服务团队可以在各个层面对Envoy进行功能扩展。而且由于Envoy良好的封装和现代C++对各种操作的简化,其插件开发过程已经相当简单。真正复杂的部分往往是插件本身的逻辑,而不是C++语言或者Envoy。并且在插件开发过程中,无需过多的考虑线程安全的问题,大部分情况下,都可以将插件的执行逻辑当作单线程来处理。本文将简单介绍Envoy的插件模型。
需要额外说明的是,Envoy中的插件的重要性可能远比“插件”这一名词所体现出来的要多的多。在Envoy中,大量的核心功能都使用插件来实现,包括对Http流量的治理、Tracing机制、多协议支持等等。为了方便,后续插件可能使用单词Filter来表述,在本文中,所有“插件”与“Filter”等同。
此外,本文并不包含任何插件开发的相关内容,而是仅仅根据网易轻舟微服务团队的Service Mesh实践经验,科普性地梳理Envoy中的插件机制以及插件的配置模型。希望对Envoy的使用者或者更上层的工作能有所帮助。
三种插件
Envoy包含多种不同类型的插件,如Listener、Network、Http以及Access log Filter等。其中Access log Filter仅仅用于对access log做一些个性化定制处理,所以不多做赘述。Envoy插件整体模型如下图1所示。
图1. Envoy插件模型
Listener Filter
Listener Filter在连接建立之后,首先被执行处理,但是由于Listener Filter只能获得极少数的信息(remote address),所以在此进行的操作极少。在绝大多数情况下,也无需扩展定制Listener Filter。
Network Filter
Network Filter是在Listener Filter处理完成之后开始执行的插件。其工作在四层,相比与Listener Filter,Network Filter可以获取相对更加丰富的信息,但是也非常有限。Network Filter是envoy管理各种协议和流量的基础,通过扩展Network Filter,可以实现envoy对各种不同类型流量的治理,如Dubbo、redis、MQ等等。
在所有的四层插件之中,有一个特例需要特别强调:HttpConnectionManager。该插件负责处理HTTP协议的四层流量。该插件是Envoy强大的能力的基础,可以说是整个Envoy中最为重要的插件。
此外,在Envoy 1.2之后,引入了一种特殊的cluster network Filter,作用于某个cluster。指向该cluster的四层流量首先需要在该cluster下的所有cluster network Filter完成处理之后才会发送出去。而在接收到来自上游服务的响应之后,首先也会由该cluster下的所有cluster network Filter处理后再继续传递给其他Filter或者下游客户端。借助cluster network Filter,envoy可以实现任意协议之间的流量转换。以HTTP和Dubbo为例:当cluster A指向一个Dubbo服务时,可以为该cluster配置一个HTTP2Dubbo的cluster network Filter。之后,可以将HTTP请求发送给cluster A。cluster A下的HTTP2Dubbo会在四层将HTTP请求重新解析并且拼接编码出Dubbo请求的二进制序列并发送给上游Dubbo服务。在接收到Dubbo服务响应之后,又可以利用同样的方式将Dubbo响应转换为HTTP响应。如此,cluster A可以完全被当作一个HTTP服务来治理,所有的细节都被屏蔽在cluster network Filter之中。其数据处理流程如下图2所示。
图2. Cluster Network Filter
Http Filter
无需多言,互联网是在HTTP协议之上构建的,所以在Envoy中,处处可见对于HTTP协议的强调和特化处理。实际上,Http Filter只是前文提到的HttpConnectionManager插件下的一系列子插件,但是却在envoy中占据了最重要的位置。HttpConnectionManager插件在完成HTTP数据解析之后,会依次调用Http Filter提供的接口,对HTTP数据做进一步处理(简单的处理如替换header、复杂的处理如限流、错误注入、缓存等等)。Envoy丰富而强大的功能,很多都是依赖Http Filter插件来实现。大部分情况下,在对Envoy做扩展增强时,也是通过扩展Http Filter来做到。Http Filter实际工作流程如图3所示。
图3. Http Filters
插件配置
之所以要单独讲插件配置,是因为大部分人都只是插件的使用者而非开发者。插件的使用者只需要知道如何下发正确配置给插件并让插件生效即可。此处只介绍HttpConnectionManager以及Http Filter插件配置,因为对于大部分人来说,无需了解四层及以下的插件内容(HttpConnectionManager是特例)。
在介绍之前,必要要再三强调:所有配置的生效与否、作用、影响都依赖于插件本身实现,没有任何强制手段可以让插件必须使用哪些配置。
虽然插件开发比如有一定的规范,并且大部分插件也会遵循规范,但是规范并不是强制。
插件启用
如前所述:所有Http Filter实际上都只是HttpConnectionManager的子插件,因此,启用哪些插件,禁用哪些插件实际上都是由HttpConnectionManager来控制。其配置形式如下(此处只做科普,所以暂时无需纠结更深的细节,只需要有大概的认识即可):
http_filters:
- name: com.netease.iprestriction
config: {}
- name: envoy.router
config: {}
所有在上述配置列表中的插件将会被启用,其他插件则处于禁用状态。必须要额外强调的是:HttpConnectionManager的配置是Listener级别配置,所以插件的启用或者禁用也是以Listener(一个Listener就是一个监听端口,映射到控制面Pilot中,就是一个Gateway资源)为单位。换言之,当一个插件被启用时,在该Listener下的所有路由、所有请求上,都会生效;当一个插件被禁用时,那么在该Listener下的所有路由、所有请求上,都会禁用。
当然,插件启用只是说明HttpConnectionManager会调用该插件提供的接口,用于处理解析后的header以及body。但是插件本身具体的执行逻辑依旧由插件自身决定。
目前不存在某一个插件只在某一条路由上生效。如果有,是该插件本身内部的逻辑,该插件内部做了相关判断,如在不匹配的路由执行该插件时,直接返回等。
在Envoy中,存在两种不同的配置用于控制插件的行为,一种是基础配置,一种是路由配置。两种配置的数据模型可能相同,可能完全不同,可能部分相同,可能缺省,完全由插件设计者决定。举例来说,transformation插件没有基础配置,只有路由配置;iprestriction插件有基础配置,但是可以缺省,也有路由配置;supercache有基础配置,且不可缺省,也有路由配置。
基础配置
首先介绍插件基础配置。插件基础配置即是插件初始化配置,该配置在启用插件(如上一段所述,在HttpConnectionManager配置中)时,在config字段中直接提供,如上一段中示例配置中的supercache插件。显然,该配置作用范围和插件的启用类似,是对整个listener内所有路由生效的。假设网关只有一个listener(一般情况下都是一个listener),那么可以认为该配置就是在整个Envoy内全局生效的。
但是,之所有称之为基础配置,而不是全局配置,是因为该配置是无法被应用到其他作用范围的,它是插件初始化的基础,作用范围不可调整(Kong的使用者往往会认为插件全局配置就是把一份配置的作用范围调整为全局,全局开启该插件;但是在需要的情况下,也可以把该配置降级为路由级,只在某一条或者某些符合条件的路由上生效。但是在Envoy中,基础配置作用范围就是全局,一旦插件启用,相关配置全局生效,不可更改)。
功能:插件初始化,一些可全局共享的插件基础信息与配置
作用范围:全局,不可修改
路由配置
客户端的请求千变万化,不同的路由往往对插件的要求也不同,需要插件在不同路由中,有不同的执行逻辑和操作。如果仅仅依赖基础配置,那么插件在所有路由中的行为都是完全一致的,显然不可能满足需求。
所以对于每一个插件,Envoy都提供了一个额外的数据模型,用于创建路由粒度的插件配置。在插件的具体执行过程中,Envoy可以根据需要搜索路由粒度的插件配置并使用该配置决策自己的执行逻辑。
在此强调:路由配置的数据模型可能和基础配置相同、不同或缺省,换言之,路由配置和基础配置是完全不相关的两种配置。
在插件执行(此时,该插件已经根据基础配置完成了初始化)的过程中(即被HttpConnectionManager调用了相关的接口时),Envoy会主动搜索当前路由下,是否具有当前插件的路由配置。如有配置,则会根据路由配置进一步操作。每条路由下的插件路由配置完全隔离。
考虑到可能多条相似的路由需要使用相同的插件路由配置,Envoy提供了virtualhost级别的插件路由配置。配置在virtualhost级别的插件路由配置可以被该virtualhost下的所有路由所共享。
注:真正可以和Kong中插件配置相对应的实际上是路由配置,只是当前的Envoy插件路由配置不支持全局作用范围(可以考虑上层封装),所以略有差异。
功能:在插件执行时,动态获取并根据配置做相关操作
作用范围:virtualhost、route(可以由上层封装到任意作用范围)
ROUTER
如果说HttpConnectionManager是Network Filter中的特例的话,那么Router插件就是Http Filter中的一个特例。Router插件是HTTP路由的基础,所有的HTTP请求都最终由Router插件向上游服务发送。对于其他插件而言,可能需要区分基础配置和路由配置。它们的基础配置作为HttpConnectionManager配置的一部分(http_filters字段)下发,而路由配置则作为路由的一部分下发(路由per_filter_config字段)。Router插件特殊的一点在于:它即是路由本身。一条路由包含的相关配置本质上就是对Router插件的配置。所以在更多的时候。Router不应该被视为一个Http Filter,而应该被视为整个HttpCononectionManager的一种功能(插件可拔插,但是为了保证整个HTTP服务治理功能的正常工作,Router不可拔插)。
Router支持常见的路由匹配(前缀匹配、精确匹配、正则匹配)、前缀重写、重定向、直接返回、分流、header添加与移除等等。事实上,利用Router插件已经可以完成Http流量管理的很多功能。需要再三强调的是,Router插件应该看作Network Filter HttpConnectionManager的一部分,而非一个独立的插件。换言之,如果仅仅从Http的层次来看,Router应该看作是基础的功能,而非可扩展或者拔插的插件。
本文介绍了Envoy中常见的三种插件并对其中Http Filter及其配置进行较为详细的分析。希望通过本文,读者能够对Envoy Filter多一些了解。如果觉得文中有一些不解或者生涩之处,欢迎在评论区留言讨论。
作者简介:
王佰平,网易杭州研究院云计算技术部轻舟团队工程师,负责轻舟Envoy网关与轻舟Service Mesh数据面开发、功能增强、性能优化等工作。对于Envoy数据面开发、增强、落地具有较为丰富的经验。