Envoy Filter Chain 深度剖析:Wasm Filter 自定义扩展实践
Envoy Filter Chain 深度剖析:Wasm Filter 自定义扩展实践
1. 什么是 Envoy Filter Chain?
1.1 常见的 Filter 类型
1.2 Filter Chain 的执行流程
2. 为什么需要自定义 Filter?
3. Wasm Filter:Envoy 自定义扩展的新选择
3.1 Wasm Filter 的优势
4. Wasm Filter 开发实践:以 Rust 为例
4.1 环境准备
4.2 项目创建
4.3 添加依赖
4.4 编写 Filter 代码
4.5 编译 Wasm Filter
4.6 配置 Envoy
4.7 运行 Envoy
4.8 测试
5. 总结
6. 进阶知识点
7. 实际应用场景举例
Envoy Filter Chain 深度剖析:Wasm Filter 自定义扩展实践
作为一名资深的 DevOps 工程师,你一定对 Envoy 不陌生。Envoy 作为云原生时代高性能、可扩展的代理,在微服务架构中扮演着至关重要的角色。而 Envoy Filter Chain,作为 Envoy 的核心机制之一,更是理解 Envoy 工作原理的关键。今天,我们就来深入探讨一下 Envoy Filter Chain 的工作原理,以及如何利用 Wasm Filter 进行自定义扩展,并结合实际代码示例进行讲解。
1. 什么是 Envoy Filter Chain?
Envoy Filter Chain 可以理解为一系列“过滤器”的有序集合。当 Envoy 接收到一个请求或响应时,会按照预定义的顺序依次执行这些过滤器。每个过滤器都可以对请求或响应进行检查、修改或转发,甚至可以终止整个处理流程。
想象一下工厂里的流水线,每个工人(过滤器)负责一个特定的任务,最终将原材料(请求/响应)加工成成品。Filter Chain 的作用就类似于这条流水线,它将复杂的网络处理逻辑分解成一个个独立的、可重用的模块。
1.1 常见的 Filter 类型
Envoy 内置了许多常用的 Filter,主要分为以下几类:
- Listener Filter: 监听器过滤器,在连接建立之初进行处理,例如 TLS 证书校验、连接重定向等。
- Network Filter: 网络过滤器,处理 TCP 连接的读写事件,例如 TCP 代理、Redis 代理等。
- HTTP Filter: HTTP 过滤器,处理 HTTP 请求和响应,例如路由、负载均衡、gRPC-Web 转换、请求头修改等。
- Router Filter: 路由过滤器, 处于 HTTP Filter 链路的末端, 将请求路由到上游集群。
1.2 Filter Chain 的执行流程
当一个请求到达 Envoy 时,Filter Chain 的执行流程大致如下:
- Listener Filter Chain: 如果配置了 Listener Filter,Envoy 会首先执行 Listener Filter Chain。Listener Filter 主要处理连接建立阶段的逻辑。
- Network Filter Chain: 连接建立后,Envoy 会执行 Network Filter Chain。Network Filter 主要处理 TCP 连接的读写事件。
- HTTP Filter Chain: 如果是 HTTP 请求,Envoy 会继续执行 HTTP Filter Chain。HTTP Filter 可以对请求和响应进行各种处理。
- Router Filter: 最后, HTTP Filter 链路会执行 Router Filter, Router Filter 将请求转发到上游集群, 之后响应会逆序再次经过 HTTP Filter.
每个 Filter 都有机会决定是否继续将请求传递给下一个 Filter,或者直接返回响应,甚至终止连接。
2. 为什么需要自定义 Filter?
Envoy 内置的 Filter 虽然功能强大,但有时并不能完全满足我们的需求。例如:
- 特殊的协议处理: 如果你需要处理一些非标准的协议,或者对现有协议进行特殊处理,内置 Filter 可能无法满足。
- 安全策略定制: 你可能需要实现一些定制化的安全策略,例如请求限流、IP 黑白名单、自定义认证等。
- 业务逻辑嵌入: 你可能需要在代理层嵌入一些简单的业务逻辑,例如根据请求参数进行特殊处理。
这时,我们就需要自定义 Filter 来扩展 Envoy 的功能。
3. Wasm Filter:Envoy 自定义扩展的新选择
在过去,自定义 Envoy Filter 通常需要使用 C++ 编写,并重新编译 Envoy。这对于很多不熟悉 C++ 的开发者来说,门槛较高,而且维护成本也比较高。
Wasm(WebAssembly)的出现,为 Envoy 自定义扩展带来了新的选择。Wasm 是一种可移植、安全、高效的字节码格式,可以在多种环境中运行,包括浏览器和服务器。
Envoy 支持 Wasm Filter,这意味着我们可以使用各种支持编译到 Wasm 的语言(例如 C/C++、Rust、Go、AssemblyScript 等)来编写自定义 Filter,而无需重新编译 Envoy。
3.1 Wasm Filter 的优势
- 多语言支持: 可以使用多种语言编写 Filter,降低了开发门槛。
- 安全隔离: Wasm Filter 运行在沙箱环境中,与 Envoy 主进程隔离,即使 Filter 崩溃也不会影响 Envoy 的稳定性。
- 动态加载: Wasm Filter 可以动态加载和卸载,无需重启 Envoy。
- 性能优异: Wasm 的执行效率接近原生代码,性能损耗较小。
4. Wasm Filter 开发实践:以 Rust 为例
接下来,我们将以 Rust 为例,演示如何开发一个简单的 Wasm Filter。
4.1 环境准备
- 安装 Rust: 参考 Rust 官方文档安装 Rust 开发环境。
- 安装 wasm-pack:
cargo install wasm-pack
用于将 Rust 代码编译成 Wasm。 - 安装 Envoy: 可以通过 Docker 快速安装 Envoy。
4.2 项目创建
cargo new --lib my-wasm-filter cd my-wasm-filter
4.3 添加依赖
在 Cargo.toml
文件中添加以下依赖:
[dependencies] proxy-wasm = "0.2.0" # Envoy Wasm SDK log = "0.4" [lib] crate-type = ["cdylib"]
4.4 编写 Filter 代码
在 src/lib.rs
文件中编写 Filter 代码:
use proxy_wasm::traits::*; use proxy_wasm::types::*; use log::info; #[no_mangle] pub fn _start() { proxy_wasm::set_log_level(LogLevel::Info); proxy_wasm::set_http_context(|context_id, _| -> Box<dyn HttpContext> { Box::new(MyWasmFilter { context_id }) }); } struct MyWasmFilter { context_id: u32 } impl Context for MyWasmFilter {} impl HttpContext for MyWasmFilter { fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { info!("[{}] Received request headers", self.context_id); // 获取请求头 if let Some(authority) = self.get_http_request_header(":authority") { info!("[{}] Authority: {}", self.context_id, authority); } // 添加自定义请求头 self.set_http_request_header("X-My-Wasm-Filter", Some("Hello from Wasm")); Action::Continue } fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { info!("[{}] Received response headers", self.context_id); // 获取响应头 if let Some(status) = self.get_http_response_header(":status") { info!("[{}] Status: {}", self.context_id, status); } Action::Continue } }
这段代码实现了一个简单的 Wasm Filter,它会在接收到请求头和响应头时打印日志,并在请求头中添加一个自定义字段 X-My-Wasm-Filter
。
4.5 编译 Wasm Filter
wasm-pack build --target=web
编译完成后,会在 pkg
目录下生成 my_wasm_filter_bg.wasm
文件,这就是我们的 Wasm Filter。
4.6 配置 Envoy
我们需要修改 Envoy 的配置文件,加载我们的 Wasm Filter。
static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: cluster: service_backend http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm config: name: my_wasm_filter vm_config: vm_id: my_vm runtime: envoy.wasm.runtime.v8 # 使用 V8 引擎 code: local: filename: /path/to/my_wasm_filter_bg.wasm # Wasm 文件路径 - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service_backend connect_timeout: 0.25s type: LOGICAL_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_backend endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 # 后端服务地址 port_value: 8080 # 后端服务端口
在这个配置中,我们在 http_filters
中添加了一个 envoy.filters.http.wasm
过滤器,并指定了 Wasm 文件的路径。
4.7 运行 Envoy
envoy -c /path/to/envoy.yaml -l debug
4.8 测试
向 Envoy 发送一个请求:
curl -H "Host: example.com" http://localhost:10000
你会在 Envoy 的日志中看到类似以下的输出:
[2023-10-27 15:35:14.123][1][debug][filter] [external/envoy/source/extensions/filters/http/wasm/wasm_filter.cc:66] [C1] wasm log: [1] Received request headers [2023-10-27 15:35:14.123][1][debug][filter] [external/envoy/source/extensions/filters/http/wasm/wasm_filter.cc:69] [C1] wasm log: [1] Authority: example.com
同时,在响应头中,你会发现多了一个 X-My-Wasm-Filter: Hello from Wasm
字段,说明我们的 Wasm Filter 已经生效。
5. 总结
Envoy Filter Chain 是 Envoy 的核心机制之一,通过 Filter Chain,我们可以实现各种复杂的网络处理逻辑。Wasm Filter 的出现,为 Envoy 自定义扩展提供了更加便捷、安全、高效的方式。通过本文的介绍,相信你已经对 Envoy Filter Chain 和 Wasm Filter 有了更深入的了解,并掌握了基本的 Wasm Filter 开发技能。希望你在实际工作中能够灵活运用这些知识,构建更加强大、稳定的微服务架构。
6. 进阶知识点
- Context ID: Envoy 使用 Context ID 来区分不同的请求/连接。每个 Filter 实例都有一个唯一的 Context ID,可以通过 Context ID 来访问和修改请求/连接的状态。
- Root Context: 除了每个请求/连接的 Context,还有一个 Root Context,Root Context 在整个 Envoy 实例中是唯一的,可以用来共享数据或状态。
- Wasm VM: Envoy 支持多种 Wasm VM,例如 V8、Wavm 等。不同的 VM 在性能、安全性、资源消耗等方面有所差异,可以根据实际需求进行选择。
- ABI 兼容性: 编写 Wasm Filter 时,需要注意 ABI 兼容性问题。不同的 Envoy 版本可能使用不同的 Wasm ABI,需要使用对应版本的 SDK 进行开发。
- 性能优化: Wasm Filter 的性能虽然接近原生代码,但仍然存在一定的开销。在编写 Filter 时,需要注意性能优化,例如避免频繁的内存分配、减少不必要的计算等。
- 共享数据和状态: 利用
proxy_wasm::hostcalls
提供的get_shared_data
和set_shared_data
函数,可以在不同的 Filter 实例之间共享数据. 这对于实现一些全局性的功能(例如限流)非常有用. - 异步调用: 利用
proxy_wasm::hostcalls::http_call
函数, Filter 可以发起异步的 HTTP 调用. 这可以用来与外部服务进行交互, 例如进行认证授权、获取配置信息等. - Timer: 利用
proxy_wasm::hostcalls::set_tick_period
函数, Filter 可以设置定时器. 这可以用来执行一些周期性的任务, 例如定期上报指标、清理过期数据等.
希望这些进阶知识点能够帮助你更好地理解和使用 Wasm Filter。
7. 实际应用场景举例
- 自定义认证授权: 可以通过 Wasm Filter 实现 OAuth 2.0、JWT 等认证授权机制。
- 请求限流: 可以通过 Wasm Filter 实现基于 IP、用户、API 等维度的请求限流。
- 请求/响应修改: 可以通过 Wasm Filter 修改请求/响应头、Body 等内容。
- 协议转换: 可以通过 Wasm Filter 实现不同协议之间的转换,例如 HTTP/1.1 到 HTTP/2、REST 到 gRPC 等。
- 监控指标收集: 可以通过 Wasm Filter 收集各种自定义监控指标,例如请求延迟、错误率等。
- 安全审计: 可以通过 Wasm Filter 记录所有请求/响应的详细信息,用于安全审计。
Envoy Wasm Filter 的应用场景非常广泛,只要你能想到的,几乎都可以通过 Wasm Filter 来实现。 尽情发挥你的想象力吧!