Envoy 原生扩展开发指南:深入 API 与实践
为什么需要 Envoy 扩展?
Envoy 扩展类型
Envoy 扩展开发:HTTP Filter 示例
1. 开发环境准备
2. 创建 Filter 源码
3. 编译 Filter
4. 配置 Envoy 使用 Filter
5. 运行 Envoy
6. 测试 Filter
Envoy 扩展开发:其他注意事项
总结
Envoy 作为一款高性能、可扩展的代理,被广泛应用于服务网格和边缘代理场景。其强大的扩展性,允许开发者根据自身需求定制功能,满足各种复杂的应用场景。本文将深入探讨 Envoy 的原生扩展机制,带你了解如何利用 Envoy 提供的 API 进行自定义扩展开发,打造属于你自己的专属功能。
为什么需要 Envoy 扩展?
虽然 Envoy 本身已经提供了丰富的功能,但在实际应用中,你可能会遇到以下场景,需要通过扩展来满足需求:
- 协议定制: 需要支持 Envoy 尚未原生支持的协议,例如一些私有协议或行业特定协议。
- 自定义过滤: 在请求处理流程中添加自定义逻辑,例如实现特殊的认证、授权、限流、监控等。
- 服务发现集成: 集成自定义的服务发现机制,例如 Consul、Etcd 或其他内部系统。
- 访问日志定制: 以特定格式记录访问日志,或将日志发送到自定义的日志系统。
- 指标定制: 收集自定义的性能指标,并将其暴露给监控系统。
- Tracing 集成: 集成不同的 Tracing 系统,例如 Jaeger、Zipkin 或其他定制系统。
Envoy 扩展类型
Envoy 提供了多种扩展点,允许你在不同层面进行定制。主要扩展类型包括:
- Filters (过滤器): 这是最常用的扩展类型。Filters 位于 Envoy 的请求处理流水线中,可以对请求和响应进行各种操作,例如修改头部、修改 Body、进行路由决策、执行健康检查等。Filters 分为以下几种:
- Listener Filters (监听器过滤器): 在 Listener 接收到连接后,但在创建网络 Filter 链之前执行。可用于实现 TLS 终结、连接重定向等。
- Network Filters (网络过滤器): 在 Listener 接收到连接后,对连接上的原始数据流进行处理。可用于实现协议解析、流量整形等。
- HTTP Filters (HTTP 过滤器): 在 Network Filter 处理完数据后,如果数据是 HTTP 协议,则会进入 HTTP Filter 链。HTTP Filters 可以对 HTTP 请求和响应进行更细粒度的操作。这是最常用,也是最强大的过滤器。
- Access Loggers (访问日志记录器): 用于记录请求的详细信息,例如请求时间、来源 IP、目标地址、请求头、响应码等。你可以自定义日志格式,或将日志发送到不同的后端系统。
- Tracers (追踪器): 用于实现分布式追踪,帮助你了解请求在整个系统中的调用链路。Envoy 支持集成多种 Tracing 系统。
- Health Checkers (健康检查器): 用于主动探测后端服务的健康状态。Envoy 支持多种健康检查方式,例如 HTTP、TCP 等。你可以自定义健康检查逻辑。
- Resource Monitors (资源监控器): 用于监控 Envoy 自身的资源使用情况,例如内存、CPU 等。当资源使用超过阈值时,可以触发告警或执行其他操作。
- Clusters (集群): 定义了一组提供相同服务的上游主机。Envoy 支持多种负载均衡算法和健康检查机制。你可以自定义集群的发现方式。
- Endpoints (端点): 集群中的单个上游主机实例。
- Listeners (监听器): 监听特定的端口,接收来自客户端的连接。你可以自定义监听器的配置,例如绑定的 IP 地址、端口、使用的协议等。
- Secrets Discovery Service (SDS): 用于动态获取 TLS 证书和其他机密信息。你可以集成自定义的 SDS 实现。
- **Runtime Configuration(运行时配置):**提供了一个动态修改Envoy配置的机制。
Envoy 扩展开发:HTTP Filter 示例
下面以 HTTP Filter 为例,详细介绍如何进行 Envoy 扩展开发。HTTP Filter 是最常用也是功能最强大的扩展类型。我们将创建一个简单的 HTTP Filter,用于在请求头中添加一个自定义字段 X-My-Custom-Header
。
1. 开发环境准备
- 安装 Bazel: Envoy 使用 Bazel 作为构建系统。你需要安装 Bazel。
- 获取 Envoy 源码: 从 GitHub 克隆 Envoy 源码。
- C++编译器: 确保已安装支持C++14或者更高版本的编译器。
2. 创建 Filter 源码
在 Envoy 源码目录下创建一个新的目录,例如 source/extensions/filters/http/my_custom_filter
。
在该目录下创建三个文件:
- my_custom_filter.h: 定义 Filter 的类。
#ifndef EXTENSIONS_FILTERS_HTTP_MY_CUSTOM_FILTER_MY_CUSTOM_FILTER_H_ #define EXTENSIONS_FILTERS_HTTP_MY_CUSTOM_FILTER_MY_CUSTOM_FILTER_H_ #include "envoy/http/filter.h" namespace Envoy { namespace Extensions { namespace HttpFilters { namespace MyCustomFilter { class MyCustomFilter : public Http::PassThroughFilter { public: MyCustomFilter() = default; Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) override; }; } // namespace MyCustomFilter } // namespace HttpFilters } // namespace Extensions } // namespace Envoy #endif // EXTENSIONS_FILTERS_HTTP_MY_CUSTOM_FILTER_MY_CUSTOM_FILTER_H_
- my_custom_filter.cc: 实现 Filter 的逻辑。
#include "source/extensions/filters/http/my_custom_filter/my_custom_filter.h" #include "envoy/http/header_map.h" namespace Envoy { namespace Extensions { namespace HttpFilters { namespace MyCustomFilter { Http::FilterHeadersStatus MyCustomFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool /*end_stream*/) { headers.addCopy("x-my-custom-header", "My Custom Value"); return Http::FilterHeadersStatus::Continue; } } // namespace MyCustomFilter } // namespace HttpFilters } // namespace Extensions } // namespace Envoy
- BUILD: Bazel 构建文件。
load("@envoy//bazel:envoy_build_system.bzl", "envoy_cc_extension", "envoy_extension_config") envoy_cc_extension( name = "my_custom_filter_lib", srcs = [ "my_custom_filter.cc", ], hdrs = [ "my_custom_filter.h", ], deps = [ "//include/envoy/http:filter_interface", "//source/common/common:minimal_logger_lib", ], ) envoy_extension_config( name = "my_custom_filter", category = "envoy.filters.http", type_urls = ["type.googleapis.com/envoy.extensions.filters.http.my_custom_filter.v3.MyCustomFilter"], config_discovery = False, config_inline = True, deps = [":my_custom_filter_lib"], )
3. 编译 Filter
在 Envoy 源码根目录下执行以下命令:
bazel build //source/extensions/filters/http/my_custom_filter:my_custom_filter_lib
编译成功后,会生成一个 .so
文件,这就是我们的 Filter 库。
4. 配置 Envoy 使用 Filter
修改 Envoy 的配置文件,添加 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 route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: cluster: service_google http_filters: - name: envoy.extensions.filters.http.my_custom_filter typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.my_custom_filter.v3.MyCustomFilter - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service_google connect_timeout: 0.25s type: LOGICAL_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_google endpoints: - lb_endpoints: - endpoint: address: socket_address: address: www.google.com port_value: 80
在 http_filters
部分,添加了 envoy.extensions.filters.http.my_custom_filter
,并指定了 typed_config
。
5. 运行 Envoy
使用修改后的配置文件启动 Envoy。
6. 测试 Filter
向 Envoy 发送请求,你会发现响应头中包含了 x-my-custom-header: My Custom Value
。
Envoy 扩展开发:其他注意事项
- 线程模型: Envoy 是多线程的,Filter 代码必须是线程安全的。
- 内存管理: Envoy 使用引用计数来管理内存,你需要正确地处理对象的生命周期,避免内存泄漏。
- 性能优化: Filter 的性能对 Envoy 的整体性能有很大影响。在开发 Filter 时,要注意性能优化,避免不必要的开销。
- API版本控制: Envoy API 使用 protobuf 进行版本控制。 确保使用与 Envoy 版本兼容的 API。
- 测试: 编写单元测试和集成测试, 确保Filter功能的正确性和稳定性。
总结
Envoy 的扩展机制为开发者提供了强大的定制能力。通过原生扩展,你可以实现各种自定义功能,满足复杂的应用场景需求。本文以 HTTP Filter 为例,介绍了 Envoy 扩展开发的基本流程。希望本文能够帮助你入门 Envoy 扩展开发,打造出更强大、更灵活的代理解决方案。 除了HTTP Filter, 理解其他类型的Filter, 以及 Filter Chain 的工作原理也至关重要。 深入学习 Envoy 官方文档和示例代码, 是掌握 Envoy 扩展开发的最佳途径。