HTTP Connection Manager 与 HTTP 过滤器链
Envoy 如何将原始字节变成它可以处理的 HTTP 请求,以及你可以将自己的逻辑接入该管线的几种方式。
人们日常在 Envoy 中配置的大部分内容 —— 头部操作、访问日志、重试、外部授权 —— 都发生在一个特定的网络过滤器内部:HTTP Connection Manager。本指南介绍它的作用、它自己的过滤器链是如何组织的,以及你可以用哪些方式,用内置之外的逻辑去扩展它。
HCM:从字节到 HTTP
listener 的过滤器链从网络层开始,处理原始字节流,此时还没有”请求”的概念。**HTTP Connection Manager(HCM)**是改变这一点的网络过滤器:它将字节流解析为 HTTP 请求、头、主体和 trailer,从此之后,链中位于它之后的一切都可以使用 HTTP 语义,而不是原始字节。
这里也是请求级功能所在的位置:访问日志、请求与响应的修改,以及至关重要的 HCM 自己的嵌套 HTTP 过滤器链,这是一个独立于它所处的网络级过滤器链的概念。
HTTP 过滤器链
在 HCM 内部,HTTP 过滤器按既定顺序对每个 HTTP 请求运行:CORS 处理、限流、外部授权(调用另一个服务来决定请求是否被允许)、Lua 脚本、WebAssembly(Wasm)模块等等。链的最后一个过滤器通常是终端过滤器,几乎总是 router 过滤器,它的工作是把请求交给路由匹配,并由此交给某个 cluster。
顺序很重要。位于认证过滤器之前的限流过滤器,其行为与位于其之后的不同,Envoy 对过滤器链的配置会明确指出每个过滤器相对于其他过滤器的位置,而不仅仅是存在哪些过滤器。
扩展 Envoy:你的选择
当 Envoy 的内置过滤器都不能满足你的需求时,有几种方式可以添加自定义逻辑,每种的成本和覆盖面都不同:
- 原生 C++ 过滤器,编译进自定义的 Envoy 构建中。这是能力最强、也最繁琐的选项:这意味着你要维护自己的 Envoy 构建,而不是使用上游的。Istio 自己的 Envoy 发行版对其部分内置行为就走的是这条路。
- 外部处理(ext_proc),Envoy 在请求中途调用一个独立的服务,该服务完成所需的处理,然后把控制权交还给过滤器链。这使自定义逻辑完全不在 Envoy 自己的进程内,代价是每个请求多一次网络跳转。
- Lua 脚本,由 Lua 过滤器针对请求和/或响应运行一段内嵌脚本。编写和迭代都很快,无需重新构建或另建服务,代价是受限于 Lua 以及 Envoy 的 Lua API 所暴露的能力。
- WebAssembly(Wasm),你用自己选择的语言将过滤器编译为
.wasm模块,Envoy 将其加载并作为链的一部分执行。这是最适合将过滤器作为可移植制品来分发和复用的扩展机制,而不是脚本或自定义构建。
选哪个取决于你需要多大的控制力,以及你愿意承担多少运维开销:Lua 脚本通常是尝试某件事最快的方式,Wasm 模块是确认可行之后更便于交付的方式,而当两者都做不到你需要的事情时,剩下的就是自定义 C++ 构建或外部服务。
配置一个过滤器究竟需要什么
无论你使用哪种扩展机制,配置一个 HTTP 过滤器归根结底都是三样东西:
- 过滤器的名称,一个稳定的标识符。
- 它的类型 URL,告诉 Envoy 的配置系统这到底是哪种过滤器,以及它的配置是什么形态。
- 过滤器特有的配置本身,即该过滤器实际接受的字段。
Envoy 自己为每个过滤器提供的文档页面列出了它的类型 URL 和配置字段,不过某个过滤器的文档往往分散在多个相互链接的参考页面中,而不是集中在一处;查找某个字段的确切语法,常常意味着沿着一串链接跳转,而不是自上而下阅读。一旦你知道了过滤器的类型 URL,在公共代码托管站点上搜索它就是一条实用的捷径:对于任何较常见的东西,通常已有人发布了可用的配置,你可以以此为起点,而不必单靠参考文档去拼装。
如果你使用服务网格,它会出现在哪里
如果 Envoy 作为 sidecar 运行在 Istio 或类似的网格之下,那么在常见情况下你很少需要手写原始的 HTTP 过滤器配置,网格自己的 API(路由规则、安全策略)会为你生成它。但对于网格的高层 API 没有涵盖的任何内容 —— 而且总有一些是没有涵盖的 —— 你最终会通过网格提供的某个较低层的逃生舱口来直接配置 HTTP 过滤器。本系列的下一篇指南正好介绍这一点,以 Istio 的 EnvoyFilter 资源为例。