博客 深入理解 Envoy Dynamic Module:原理、开发与全流程实战

全面解析 Envoy Proxy Dynamic Module(动态模块)的原理、配置、开发与 Demo 实战,结合官方 Rust SDK 与 Go 多语言能力,手把手带你实现高性能原生扩展。

版权声明
除非另有说明,本网站上的内容根据 CC BY-NC-SA 4.0 许可协议提供。
查看本文大纲

在上一篇《理解 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 的实验性实现。

Dynamic Module 加载机制要点
  • 通过 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 的扩展能力:

  1. access_logger
    • 结构化采集所有请求/响应 header,日志输出 JSON,适合观测与调试
  2. header_mutation
    • 支持 header 增删、策略注入,适合灰度、A/B 测试和策略统一下发场景
  3. passthrough(Rust/Go 双实现)
    • 演示多语言兼容性及动态模块链式加载能力(不做任何操作)

1. 克隆源码仓库

git clone https://github.com/envoyproxy/dynamic-modules-examples.git
cd dynamic-modules-examples
git checkout 74e9dbf80c0ea056e0ee5fc1b8f1c35d3a4c9a98
说明
选定的 commit 可确保与官方文档和实验结果一致,避免因主干变更带来兼容性问题。

2. 使用 Docker 构建所有动态模块与 Envoy 镜像

docker build -t envoy-with-dynamic-modules:latest .
  • 该命令会自动:
    • 编译 Rust/Go 动态模块,生成 .so 共享库
    • 构建定制的 Envoy 主程序
    • 打包成可直接运行的 Docker 镜像
  • 默认仅支持本机架构(如 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_mutationpassthrough 等)

6. 查看结构化日志与 header 变更

访问日志可用于分析模块行为(比如 access_logger),header_mutation 可直接在响应头看到注入内容。

cat integration/access_logs/access_log_0.log
  • access_logger 会结构化输出每次请求和响应的所有 headers。
  • header_mutation 会在响应头插入如 foo: barfoo2: 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/外部服务等扩展方式的局限,兼顾高性能与开发友好性。推荐企业在自控场景下优先评估使用,后续如需源码/链路细节探讨,欢迎留言交流。

参考与延伸阅读