博客 深入理解 Envoy Dynamic Module:原理、开发与全流程实战
全面解析 Envoy Proxy Dynamic Module(动态模块)的原理、配置、开发与 Demo 实战,结合官方 Rust SDK 与 Go 多语言能力,手把手带你实现高性能原生扩展。
查看本文大纲
在上一篇《理解 Envoy 的扩展与集成机制:从内置扩展到动态模块》中,我系统梳理了 Envoy 的各类扩展与集成方式,并特别推荐了 Dynamic Module 这一新兴机制。本篇将围绕 Dynamic Module 的原理与完整实操,帮助你高效掌握 Envoy 原生扩展的落地方案。
什么是 Dynamic Module?
Dynamic Module 是 Envoy Proxy v1.34 起引入的实验特性,允许开发者以共享库(.so)形式动态加载自定义扩展。它不依赖虚拟机或外部进程,直接通过 C ABI 与主进程对接,具有极高性能和现代 SDK 支持(如官方 Rust SDK)。
技术原理
Dynamic Module 本质上是一个实现了特定 ABI 入口的共享库(.so),通过定义在 C 头文件中的接口与 Envoy 进行交互。当前官方主推 Rust SDK(类型安全,开发体验佳),但也可用 Go、C++ 等语言编译出 C ABI 兼容的动态库。社区已经有 Go 的实验性实现。
- 通过
ENVOY_DYNAMIC_MODULES_SEARCH_PATH
环境变量设置查找路径 .so
文件命名需符合lib{name}.so
规则,其中{name}
对应配置文件的dynamic_module_config.name
- 加载后直接作为 HTTP Filter 加入请求处理链,与原生 Filter 平权
注意:Dynamic Module 与主进程完全共享内存和权限,无隔离机制,安全级别等同于原生 C++ 扩展,务必只加载可信模块。
优势与局限
在上一篇文章中我已经总结过不同机制的优缺点。Dynamic Module 的主要优势体现在:
- 性能极高:无中间虚拟机,无跨进程/语言序列化,支持高吞吐业务场景
- 开发现代:Rust SDK 类型安全、文档完善,开发体验远优于 Wasm
- 高度灵活:能实现几乎所有 HTTP 层面的自定义操作
- 多语言潜力:虽然官方仅支持 Rust,但 Go、C++ 等理论上都可支持
局限:
- 没有安全沙箱,只能用于受控环境
- 与 Envoy 版本强耦合,升级需同步重编模块
实践:Dynamic Module 官方 Demo 全流程与细节解读
接下来演示如何基于 dynamic-modules-examples 官方仓库,从源码到容器一键体验 Envoy 动态模块(Rust/Go 版本)。
本实验内置了多种典型模块,便于理解 Dynamic Module 的扩展能力:
- access_logger
- 结构化采集所有请求/响应 header,日志输出 JSON,适合观测与调试
- header_mutation
- 支持 header 增删、策略注入,适合灰度、A/B 测试和策略统一下发场景
- passthrough(Rust/Go 双实现)
- 演示多语言兼容性及动态模块链式加载能力(不做任何操作)
1. 克隆源码仓库
git clone https://github.com/envoyproxy/dynamic-modules-examples.git
cd dynamic-modules-examples
git checkout 74e9dbf80c0ea056e0ee5fc1b8f1c35d3a4c9a98
2. 使用 Docker 构建所有动态模块与 Envoy 镜像
docker build -t envoy-with-dynamic-modules:latest .
- 该命令会自动:
- 编译 Rust/Go 动态模块,生成
.so
共享库 - 构建定制的 Envoy 主程序
- 打包成可直接运行的 Docker 镜像
- 编译 Rust/Go 动态模块,生成
- 默认仅支持本机架构(如 x86_64 或 ARM64),如需跨平台需手动适配 Dockerfile
- 动态模块是以
.so
形式被编译出来的共享库,通过 Envoy 的 C ABI 加载机制动态注入到 Envoy 进程,实现扩展。 - 构建时请确保宿主机与目标容器的架构一致,否则模块或 Envoy 本体可能出现不兼容问题。
3. Envoy 配置与挂载说明
本仓库内置的 envoy.yaml
配置文件,已经正确地集成了动态模块加载配置。这里简要解读其核心结构:
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 1062 # 监听端口
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
route_config: ...
http_filters:
- name: envoy.filters.http.dynamic_module
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_modules.v3.DynamicModuleFilter
dynamic_module_config:
name: access_logger # 需与 .so 文件名对应
filter_name: access_logger
- name: envoy.filters.http.router
clusters:
- name: echo_service
connect_timeout: 0.25s
type: STATIC
load_assignment: ...
- 动态模块通过
dynamic_module_config.name
字段指定,Envoy 会从ENVOY_DYNAMIC_MODULES_SEARCH_PATH
环境变量指定的目录下加载名为lib{name}.so
的文件。 - 例如配置中
name: access_logger
,则需有libaccess_logger.so
文件存在。 filter_name
用于 filter 链路标识,无需与.so
名一致,但推荐保持一致以便调试。- 你可以在
docker-compose.yaml
中看到环境变量和卷挂载配置,确保模块和配置文件都被正确加载进容器:
services:
envoy:
image: envoy-with-dynamic-modules:latest
volumes:
- ./integration/envoy.yaml:/etc/envoy/envoy.yaml
- ./target/x86_64-unknown-linux-gnu/release:/usr/local/lib
- ./integration/access_logs:/access_logs
ports:
- 1062:1062
environment:
- ENVOY_DYNAMIC_MODULES_SEARCH_PATH=/usr/local/lib
echo:
image: hashicorp/http-echo
command: ["-text=Hello from echo server"]
ports:
- 1234:1234
- 所有动态模块都存放于
/usr/local/lib
,可根据需要挂载更多.so
文件(如header_mutation
、passthrough 等) - 多端口演示可通过不同 listener/route/filter 配置分别测试不同动态模块
- Access 日志通过挂载目录输出,便于本地分析和调试
4. 启动实验环境(Envoy+Echo)
docker compose up -d
- 会启动两大服务:
- Envoy:自动加载集成的动态模块
- Echo Server:HTTP 后端响应服务,便于验证流量和 filter 效果
- Envoy 会监听 1062、1063、1064 等端口,分别加载不同模块(详见
envoy.yaml
配置)
5. 发送请求,体验动态模块实际效果
以 access_logger
模块为例(监听 1062):
curl localhost:1062/uuid
你应收到:
Hello from echo server
同理,也可以对 1063/1064 端口进行请求,体验其他模块(如 header_mutation
、passthrough
等)
6. 查看结构化日志与 header 变更
访问日志可用于分析模块行为(比如 access_logger
),header_mutation
可直接在响应头看到注入内容。
cat integration/access_logs/access_log_0.log
access_logger
会结构化输出每次请求和响应的所有 headers。header_mutation
会在响应头插入如foo: bar
、foo2: bar2
等自定义字段。- 你可以通过对比日志和响应,验证动态模块链路的真实效果。
7. 关闭和清理实验环境
docker compose down
- 会自动停止并清理所有容器与挂载
- 若需反复测试,可重新修改源码、重编模块,再
up -d
启动
注意事项
编译
.so
时要保证和运行 Envoy 版本完全一致(同一 Git SHA/Release Tag),否则 ABI 不兼容,这也意味着每次升级 Envoy 时,务必同步重编所有动态模块配置文件中通过
dynamic_module_config.name
对应指定模块名支持 Rust 官方 SDK 开发,Go 处于实验阶段,C/C++ 兼容性取决于 ABI 实现
只允许加载企业内受控、可信的动态模块
总结
Dynamic Module 填补了 C++/Wasm/Lua/外部服务等扩展方式的局限,兼顾高性能与开发友好性。推荐企业在自控场景下优先评估使用,后续如需源码/链路细节探讨,欢迎留言交流。