WebAssembly:让 Istio 变得更强大
1
Wasm 为 Envoy 带来新的扩展性
Envoy 是一个高性能、可编程的 L3/L4 和 L7 网络代理,许多服务网格和网关都采用 Envoy 作为数据面。
Envoy 通过监听器(Listener)捕获网络数据包,根据数据包的内容匹配某个过滤器链(Filter Chain)中,之后按顺序执行该链中的过滤器(Network Filter)对捕获的数据包进行操作,实现用户定义的各种流量治理策略。Envoy 本身自带很多种类的过滤器,这些开箱即用的过滤器 cover 了大部分的应用场景,但是在某些需要自定义功能的场景下,用户必须实现自己的过滤器。
Envoy filter chains[5]
在 Wasm 出现之前,添加过滤器有两种方式:
1. 修改 Envoy 代码,使用 C++语言编写原生的过滤器(Native C++ filters)。在早期(2019 年初),Envoy 是一个静态编译的二进制文件,其所有扩展都在构建时编译。这意味着提供自定义扩展的项目必须维护和分发自己的二进制文件。这种方式需要开发者熟悉 C++语言和 Envoy 过滤器的开发模式,在开发完成后重新编译一个新的 Envoy 二进制且维护它与上游社区的版本。目前 Istio 社区采用的就是这种方式,Istio fork 了上游的 Envoy,在其基础之上添加了自己定制的一些插件。然而,这种方式对开发者要求较高,且需要花费额外的精力维护。
2. 使用 Lua 脚本编写过滤器。这种方式相比于上一种更加简单,用户可以直接在 xDS 配置中直接编写 Lua 脚本(inline)或指定本地的一个 Lua 脚本文件,适合过滤器逻辑非常简单的情况。然而,这种方式并不适用于过滤器逻辑复杂的情况,而且需要用户在 Istio 中手动创建 EnvoyFilter 进行额外的配置。
可以看出,上述两种扩展 Envoy 的方式都不是非常“优雅”。为了解决这个问题,Envoy 社区提出了 Wasm Filter 特性,在 Envoy 中内嵌了一个 Wasm 运行时,通过 proxy-wasm 定义的网络代理相关接口来运行 Wasm 二进制。这意味着 Envoy 可以在运行中动态地加载用户开发的 Wasm 模块,并将其作为一个过滤器插入到过滤器链中。Wasm 能够解决上述传统方式的各种问题,用户能够用任何支持的语言开发自己的 Wasm 过滤器,对 Envoy 本身无侵入。Envoy 发起者称“Wasm 是 Envoy 可扩展性的未来”。
Envoy 与 Wasm 的交互[6]
2
Istio WasmPlugin API
Envoy 对 Wasm 的支持为 Istio 带来了全新的扩展机制。在早期,用户可以使用 EnvoyFilter 手动在 Envoy 配置中添加 Wasm filter,这种方式非常地繁琐,用户体验并不友好,且 EnvoyFilter 是一个“break glass”API,社区并不保证其不同版本的向后兼容性。
为了更好地支持 Wasm,Istio 在 1.12 版本中添加了一个新的 CRD,即 WasmPlugin。用户可以通过 WasmPlugin 方便灵活地将 Wasm 插件下发到指定的工作负载,使得开发人员可以更简单健壮地扩展网格功能。
WasmPlugin yaml 示例[7]
上图展示了一个最基本的 WasmPlugin 配置,在配置中用户需要指定 Wasm 模块的 url,该 url 可以是像容器镜像一样的 OCI 格式的 Wasm,也可以是代理本地的一个文件(通常要求用户手动挂载到容器中),或者是 http 协议的 url,用于直接获取远程 Wasm 模块文件。一般推荐的方式第是一种,将编写好的代码编译成 Wasm 二进制后,打包成一个 OCI 镜像,方便分发和复现。
用户可以通过 selector 指定要下发 Wasm 的 proxy,如果 selector 为空,则代表下发到该 namespace 下的所有 proxy。
除此之外,如果用户需要指定 Wasm 插件在过滤器链中执行的位置,可以通过 phase 和 priority 两个参数来控制。phase 用来指定在 http filter 链中的何处插入此 Wasm 插件,可以设置为 authentication,authorization 或 istio stats filter 之前,未设置时会在 istio stats filter 之后插入;priority 用来控制多个 WasmPlugin 在同一个 phase 中的执行顺序,priority 值大的优先。
3
Istio 下发 Wasm 配置流程解析
Istiod 推送过程
每当用户更新 WasmPlugin 时,istiod 就会触发一次 config update。首先,istiod 会更新本次 xDS push 的 context,将当前的 WasmPlugin 信息按照 namespace 分类,保存到 push context 中。
之后,在 LDS(Listener Discovery Service)推送的过程中,istiod 为 proxy 构建 Listener 的 http filter 时,会从 push context 中找出与该 proxy 匹配的 WasmPlugin 并按照 priority 排序,最后根据 phase 将 Wasm filter 插入到 http filter chain 中的某个位置。
Istio 插入 Wasm filter 代码[8]
注意此处插入的只是 Wasm filter 的名称,具体的 Wasm filter 配置则是通过后续的 ECDS(Extension Config Discovery Service)下发的。在 ECDS 中,istiod 会构建出实际的 Wasm filter 配置并推送给 proxy。
Wasm filter envoy 配置示例[9]
4
Proxy 接收过程
Proxy 接收过程
Envoy 的 Wasm filter 配置本身是不支持使用 OCI 镜像格式作为 data source 的。那么 Istio 是如何支持使用 OCI 镜像分发 Wasm 二进制的呢?
答案是通过 istio-agent 的代理。Istio 的 proxy 中包含两个进程,一个是 Envoy 本身,另一个是 istio-agent。istio-agent 会代理 Envoy 与 istiod 之间的 xDS 通信。对于 ECDS,istio-agent 在收到推送时,会读取其内容,假如其中的 Wasm filter 使用 OCI 镜像或者 http/https 作为 data source(即需要执行的 Wasm 二进制),那么 istio-agent 会从远程仓库中拉取该 Wasm 二进制并缓存至本地。之后会修改 ECDS 的内容,将 Wasm filter 的 data source 改为刚才保存的本地文件,再将修改后的 ECDS 内容发送给 Envoy。
对 Wasm 为 Envoy 带来新的扩展性的价值简要总结:
综上,Envoy 和 Istio 对 Wasm 的支持大大加强了服务网格的扩展性。用户通过 Wasm 能够以可扩展、灵活、安全的方式对代理进行自定义配置,应对各种场景的业务需求,例如认证、授权、Tracing、请求内容转换/检查等等。同时,Istio 提供的 API 使 Wasm 成为了服务网格中的“一等公民”,用户可以方便地将 Wasm 下发到指定的工作负载,该过程是完全动态的,应用无需重启。这种高效率的扩展方式使得服务网格具备了可编程性。
5
未来展望
目前,Wasm 仍在快速发展,相关的特性在 Istio 和 Envoy 中还处在 alpha 阶段。为了加速 Wasm 生态,让所有 Wasm 程序有一个“common language”,社区正在设计一个标准的 Wasm interface —— WASI(WebAssembly System Interface),用于访问和操作系统资源。未来 proxy-wasm 可能会与 WASI 融合,为 Wasm 程序提供一个标准的交互接口。同时,Wasm 将支持更多高级语言作为前端,用于构建轻量、高性能的应用程序。随着 Wasm 生态的逐步成熟,期待它能在云计算领域中带来更多令人兴奋的可能性。
评论