Envoy 的定位:边缘、前端代理与服务网格模式
同一个代理,三种不同的部署方式 —— 以及为什么"轻量到可以每个实例都运行一个"使这三种方式成为可能。
无论部署在哪里,Envoy 的配置模型都不会改变,但部署位置的不同,会改变它所解决的问题。本指南介绍常见的部署模式,以及 Envoy 的一个特性 —— 足够便宜,可以每个应用运行一个实例 —— 为何使其中最有意思的一种成为可能。
边缘代理:网络的前门
部署在边缘时,Envoy 位于外部世界与你的内部服务之间,接受来自外部客户端的入站流量,在另一个方向上,处理你自己的服务发出的出站流量。这是经典的 API 网关或负载均衡器位置:一个(或一小组冗余的)Envoy 实例,为所有穿越网络边界的流量(无论哪个方向)做 TLS 终止、路由和限流。
前端代理:共享的内部负载均衡器
一种更简单的内部模式:单个 Envoy(或一小组 Envoy)作为所有内部服务发送请求的对象,再由它分发到正确的后端。这看起来像是一个传统的内部负载均衡器,功能上它也确实是,只不过是用 Envoy 的路由和健康检查来实现,而不是用专用的 LB 设备。
Sidecar:每个服务实例一个 Envoy
这正是 Envoy 区别于大多数其他代理的模式:不是由一个共享实例处理许多服务的流量,而是在每个服务的每个实例旁边都运行一个 Envoy 实例,以 Kubernetes 的说法,就是同一个 Pod 中的第二个容器。每个服务的入站和出站流量都流经它自己专属的代理实例,而不是共享的那个。
这种紧邻带来了两点直接的结果:
- 应用不再需要了解网络。 重试、超时、TLS 和负载均衡逻辑从应用代码中移出,进入紧挨着它的代理,这正是 Envoy 的设计所要实现的关注点分离。
- 排障变得更快。 当 Envoy 是网络上别处的共享组件时,问题可能出在应用、代理,或它们之间的网络路径上。当 Envoy 就运行在它所服务的那个应用实例旁边时,这种模糊性大多消失了:如果 sidecar 报告到应用的连接健康,但请求仍然失败,那么搜索范围就小得多了。
每个实例运行一个 Envoy 之所以可行,只是因为 Envoy 被刻意设计得很轻量;如果一个代理的单实例占用很重,在这种密度下部署它,就会变成再运行一遍应用的资源成本。
服务网格:协同工作的 sidecar
服务网格就是将 sidecar 模式一致地应用于整个应用:每个服务实例都有自己的 Envoy,所有这些代理作为一个协调的系统被配置和观测,而不是作为独立、各自单独管理的实例。Envoy 的流量管理特性 —— 重试、熔断、双向 TLS、细粒度路由 —— 在这里统一应用于网格中的每个服务,而无需任何单个服务的代码去实现它们。
手动配置数百个独立的 Envoy 实例并不现实,这正是服务网格作为 Envoy 之上独立一层存在的原因,而不是”只是部署了很多的 Envoy”。Istio 是最突出的例子:它的数据平面具体就是 Envoy,而 Istio 自己的控制平面 istiod 会在服务和策略变化时自动生成并推送每个 sidecar 的配置。如果你对一个已被 Istio 注入 sidecar 的 Pod 执行 kubectl exec 并查看其日志,实际运行的是一个真正的 Envoy 进程,只不过由 Istio 而非手工配置。
混合模式是常态
生产拓扑通常会组合多种模式:边缘的 Envoy 处理整个集群的入站和出站,而 Envoy sidecar 处理集群内部的服务到服务流量。Envoy 并不强制单一模式;选择其中一种或组合,取决于哪些流量需要哪种控制,而不是代理本身的限制。