0. 简介
官网地址: www.envoyproxy.io/
Envoy 是一款 CNCF 旗下的开源项目. Envoy 采用 C++ 实现,是面向 Service Mesh 的高性能网络代理服务. Envoy 代理可以提供以下功能:
- 负载均衡:envoy 代理可以根据不同的算法和策略,将流量分配到上游的服务或应用.
- 服务发现:envoy 代理可以动态地发现上游的服务或应用,无需手动配置.
- 路由:envoy 代理可以根据请求的属性,如路径、头部、参数等,将流量路由到不同的目标.
- 观察性:envoy 代理可以收集和报告各种指标,如延迟、吞吐量、错误率等,以及生成访问日志和追踪信息.
- 安全性:envoy 代理可以支持多种协议和机制,如 TLS、mTLS、JWT、OAuth2 等,来保证流量的安全性和身份验证.
Envoy 代理可以以 sidecar 的模式部署在每个服务或应用的旁边,形成一个透明的通信网格,也可以以边缘代理或 API 网关的模式部署在系统的边界,处理入站或出站的流量. envoy 代理可以通过动态(XDS)配置来适应不同的场景和需求,而无需重启或修改.
1. 快速开始
1.1 重要概念
-
代理(Proxy):Envoy 是一个代理服务器,位于客户端和后端服务之间,用于处理网络流量的转发和管理。它可以作为边缘代理、透明代理或双向代理,通过拦截请求和响应来提供一些功能,如负载均衡、流量控制、故障恢复和安全性。
-
过滤器(Filter):Envoy 的核心功能是通过过滤器链处理请求和响应。每个过滤器可以执行特定的任务,如鉴权、路由、日志记录、监控等。过滤器链中的过滤器按照顺序依次处理网络流量,并且每个过滤器可以修改请求或响应的内容。
-
上游和下游(Upstream and Downstream):在 Envoy 中,上游(Upstream)表示客户端或请求的来源,而下游(Downstream)表示后端服务或响应的目标。Envoy 代理作为客户端与上游服务进行通信,同时作为服务器与下游服务进行通信。
-
集群(Cluster):Envoy 将多个后端服务组织为一个逻辑集群。集群中的每个成员都具有相同的服务接口,Envoy 可以根据负载均衡策略将请求分发给集群中的成员。
-
路由(Routing):Envoy 提供了灵活的路由配置,可以根据请求的特征将流量转发到不同的后端服务。路由规则可以基于请求的路径、主机、头部信息等进行匹配和过滤,以确定请求应该转发到哪个集群或服务。
-
端点(Endpoint) 是指后端服务的网络地址和端口。每个后端服务都被视为一个 Endpoint。
-
动态配置(XDS):Envoy 可以动态地更新路由规则和负载均衡策略,
-
健康检查(Health Checking):Envoy 可以定期对后端服务进行健康检查,以确定服务的可用性。健康检查可以使用不同的协议和方法,如 TCP、HTTP、gRPC,以及自定义的检查逻辑。根据健康检查结果,Envoy 可以自动排除不可用的服务节点。
-
TLS(Transport Layer Security):Envoy 提供了对传输层安全性的支持,可以使用 TLS 加密和认证来保护网络通信。它可以配置双向认证(即客户端和服务端都要验证对方身份)以及传输层加密,确保通信的机密性和完整性
1.2 运行瞧瞧
-
启动 envoy 我们需要先准备一个测试服务, 这里选用 nginx, 接下来都会使用 docker 进行部署envoy.
# docker run --name ngxv1 -d nginx:1.18-alpine
-
由于我们需要使用 envoy 进行代理流量转发到 nginx 上, 所以需要知道 nginx 的 ip.
# docker inspect ngxv1 # 获取 ip 地址
-
编写 envoy.yaml, 配置后续会再做详细解释, 先跑起来再说;
admin: # 管理后台的监听地址, 这是一个简易的ui界面 address: socket_address: { address: 0.0.0.0, port_value: 9901 } static_resources: listeners: # 启动一个监听器 - name: listener_0 # 监听地址 0.0.0.0:8080 address: socket_address: { address: 0.0.0.0, port_value: 8080 } # 过滤链, 这是 envoy 的重中之重 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager # typed_config 是一个插件调用的关键字 typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO # 由 HttpConnectionManager 插件提供的路由配置 route_config: name: shadow_route # 配置虚拟主机 virtual_hosts: - name: myhost # 匹配的主机头 domains: ["*"] routes: # 路由匹配 - match: {prefix: "/"} # 路由到上游集群, 即下方配置的 cluster route: {cluster: shadow_cluster_config} # 启用http链 http_filters: # 启用路由链 - name: envoy.filters.http.router clusters: # 配置静态集群 - name: shadow_cluster_config connect_timeout: 1s type: Static dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: shadow_cluster endpoints: - lb_endpoints: # 配置端点 - endpoint: address: socket_address: # 需要修改为 ngxv1 的 ip address: 172.17.0.5 # 代理端口 port_value: 80
-
选用 docker 快速安装, 先进行镜像下载
# docker pull envoyproxy/envoy-alpine:v1.21.0
-
启动 docker 镜像, 把配置映射进去方便后续对 envoy.yaml 进行调整
# docker run --name=envoy -d -p 9901:9901 -p 8081:8080 -v /opt/envoy/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy-alpine:v1.21.0
-
从主机上进行测试, 测试通过证明你已经是通过 envoy 反向代理了你部署的 nginx.
# curl 127.0.0.1:8081 # 测试是否能访问 nginx # curl 127.0.0.1:9901 # 测试是否能访问 admin
2. 授人以渔
2.1 原理简介
- 从上图可以看出请求的流量通过监听器进入, 然后会经过一系列的 Filter 的处理, 而在每种 Filter 中其实会支持不同的插件配置 (在配置中体现为 typed_config.@type). 从 Filter 中出来后便会路由到集群(Cluster), 最后到达 Endpoint (图中的 Service).
2.2 文档导读
- 在第一章节的描述中,我们初步了解了 Envoy 的一些重要概念。从上述的 envoy.yaml 配置中,我们可以感受到 Envoy 的配置相对较为复杂。同时,Envoy 被广泛认为是一项具有挑战性的代理任务之一,其学习曲线也相应地陡峭。此外,初学者可能会发现 Envoy 的文档索引令人困惑,难以准确找到所需的信息并进行正确的配置。因此,在本章节中,我们将通过配置2个案例来帮助您更好地查询文档并正确进行配置。
2.2.1 Accesslog 配置
-
本示例展示如何配置 access_log, 以及如何查找相应的示例和参数; 从下方配置可以看到, 我们需要先配置 http_connection_manager 过滤器, 然后再往下配置 access_log 字段, 继续往下配置envoy.access_loggers.stdout 扩展插件, 插件指出把日志输出到终端上.
-
从下方配置你可以看到 filters 其实可以理解为 4层 (TCP、UDP等) 配置, 而 http_filters 则是 http_connection_manager 下表示配置 7 层的 http 相关功能, 在 http_filters 中配置了 envoy.filters.http.router 插件;
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 codec_type: AUTO stat_prefix: ingress_http access_log: - name: envoy.access_loggers.stdout typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog route_config: name: local_route virtual_hosts: - name: services domains: - "*" routes: - match: prefix: "/" route: cluster: frontend_service http_filters: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: frontend_service type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: frontend_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: frontend-service port_value: 8080
-
接下来,我们将介绍如何从文档中找出这些参数并进行正确配置。首先,让我们先来了解一下配置概览页面。您可以参考下面的概览页面. 你可以再下图表示 [1] ,该示意图展示了一些基础配置的概览,有助于您进行学习的开展。
-
在上述配置我们看到了3种扩展插件(以typed_config.@type来表示的), 名字分别是 envoy.filters.network.http_connection_manager、envoy.filters.http.router 和 envoy.access_loggers.stdout. 接下来我们将一一在文档中找出这些配置并且从中大致了解如何寻找配置.
-
envoy.filters.network.http_connection_manager, 截图内只截取了我们上述配置的关键配置.
-
我们开始寻找这个 envoy.access_loggers.stdout 扩展, 衔接上图的标识 [5].
-
接下来寻找最后一个扩展 envoy.filters.http.router, 需要衔接 Http connection manager 图的标识 [4] 描述的 http_filters.
-
在 envoy.filters.http.router 扩展中我们并没有配置任何的字段, 为什么呢? 在上图中有个 configuration overview
官方文档:
2.2.2 Cors 插件配置
-
经过上一章节的配置, 应该对 envoy 文档如何进行查阅的技巧有所明白. 接下来再用一个跨域插件配置的例子进一步的巩固一下如何进行查阅.
-
跨域配置在 envoy 有好几种方法, 我这里配置跨域配置插件只是纯粹为了演示, 不一定再配置跨域时按着我的配置.
-
众所周知 cors 的配置属于http 范畴, 所以我们应该可以想到直接寻找 http_filters.
-
先看看配置
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 codec_type: AUTO stat_prefix: ingress_http access_log: - name: envoy.access_loggers.stdout typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog route_config: name: local_route virtual_hosts: - name: www domains: - "*" typed_per_filter_config: # 配置全局跨域 envoy.filters.http.cors: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy allow_origin_string_match: - safe_regex: regex: \* allow_methods: "GET" filter_enabled: default_value: numerator: 100 denominator: HUNDRED runtime_key: cors.www.enabled shadow_enabled: default_value: numerator: 0 denominator: HUNDRED runtime_key: cors.www.shadow_enabled routes: - match: prefix: "/cors/open" route: cluster: backend_service - match: prefix: "/cors/disabled" route: cluster: backend_service typed_per_filter_config: # 单一匹配后进行跨域控制 envoy.filters.http.cors: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy filter_enabled: default_value: numerator: 0 denominator: HUNDRED - match: prefix: "/cors/restricted" route: cluster: backend_service typed_per_filter_config: # 单一匹配后进行跨域控制 envoy.filters.http.cors: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy allow_origin_string_match: - safe_regex: regex: .*\.envoyproxy\.io allow_methods: "GET" - match: prefix: "/" route: cluster: backend_service http_filters: # 启用跨域配置插件 - name: envoy.filters.http.cors typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: backend_service type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: backend_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: backend-service port_value: 8080
-
开始寻找文档中如何配置
官方文档: envoy.filters.http
3. 参考
4. 写在最后
-
通过今天的文章, 你应该大致了解 envoy 的配置以及一些基础概念; 后续我还会继续编写 envoy 相关的文章形成相对完整的入门系列:
- Envoy 作为 Sidecar 接管 Pod 的流量 [2]
- Envoy XDS 入门 (Golang) [3]
- Envoy wasm 插件入门 (Golang) [4]
-
Envoy 的配置确实挺复杂, 特别刚开始接触文档会非常头晕被绕来绕去的. envoy 的复杂度其实大概来自两点, 一是来自envoy的强大灵活性, 二是我们自身对各类传输协议的了解不足;
-
点赞👍、关注➕、收藏🌟、留言💬. 下篇文章见