Envoy 网格基础概念
Envoy 是什么
众所周知,Nginx 有两层功能:
WebServer
基于模块扩展的反向代理:根据模块载入的不同或者根据后期二次开发的增强,模块会不断增加
用到最为典型的两个功能分别是 HTTP Proxy 和 TCP Proxy。后者是基于 TCP 协议工作的,应用层服务需要借助此反向代理机制完成反向代理功能,比如 MySQL。
而 Envoy 也是这么一类工具,能实现两大基本功能:
七层反向代理:HTTP/HTTPS、Zookeeper、MySQL、MongoDB…
四层反向代理:TCP、UDP。虽然这并非是 Envoy 的主流特性,在微服务场景中,依赖于某个独立的存储服务时,如果上述的主流协议都无法覆盖时则可基于 TCP 或 UDP 的方式去承载也是常见的
Envoy 本是是反向代理服务器,本身不支持作为 Web 服务使用。它的核心作用在于充当现代服务网格的通信总线,设计用于大型、现代化的服务导向的架构。它的设计有它自身适配的目标,而它又晚于 Nginx,因此它在特性和性能上有原生支持现代化和云计算这种环境下的动态配置的特性。Envoy 对此有着得天独厚的优势。
所以,这个项目本身主要是面向两种假设而设计的:
网络给应用提供透明代理,应用程序无所感知
网络和应用发生网络通信问题时可提供一定程度局部故障处理机制,这是服务网格本身应该具备的能力,如超时、重试、熔断
因此,较之 nginx,Envoy 更适应于现代的,智能的,面向服务导向的设计。
Envoy 本身是使用 C++开发,一个反向代理和负载均衡器,可以将其和特定应用打包在同一个 Pod 当中,仅代理该 pod 所有的进出流量,它称为网格内部代理。随后,它还能够工作在网格或者服务的边缘处的 Edge proxy,比如三个 Pod 是特定应用程序的三个不同实例,可以为同一个服务的多个不同实例作统一的代理出口,此时 Envoy 称为 Front-envoy;
关键特性
Out of process architecture**:** 进程外的架构
Envoy 适用于任何应用程序语言。单个 Envoy 部署可以在 Java、C++、Go、PHP、Python 等之间形成网格。面向服务的架构使用多种应用程序框架和语言变得越来越普遍。Envoy 透明地弥合了差距。
Envoy 可以透明地跨整个基础设施快速部署和升级。
L3/L4 filter architecture**:L3/L4 过滤器** TCP proxy, UDP proxy, HTTP proxy, TLS client certificate authentication, Redis, MongoDB, Postgres, etc.
HTTP L7 filter architecture:HTTP L7 层过滤器 HTTP、HTTPS
First class HTTP/2 support :原生支持 http2 协议,也支持 http3(alpha 阶段)
HTTP/3 support (currently in alpha) :支持 HTTP/3
HTTP L7 routing :HTTP L7 路由
使用的最多的功能,http 7 层路由功能。服务网格当中大部分流量治理功能指的就是 http L7 routing 的相关功能。
比如 istio 中借助于 virtualService 资源的定义,去修改,定义和配置服务网格当中服务支持哪些类型的访问的特性,virtualService 本身配置的就是 L7 routing 的相关功能。
在 istio 中配置的是使用 CRD,而此处是使用相关配置的语法,直接配置以了解路由功能如何实现。
gRPC support : 支持 gRPC:较之 http 协议承载的 RPC 而言,它更加高效面向服务间通信的协议。Envoy 原生支持 grpc。
Service discovery and dynamic configuration : 服务发现和动态配置
服务发现:以 Nginx 对比。在 Nginx 语境中,当反向代理时,需要在前端配置一个虚拟主机,监听套接字以接收客户端请求。而后端使用 upstream server,配置上游服务器组,代理到上游的服务器中。根据 Nginx 的 Http 流量的处理机制将某些类型的请求反向代理给服务器组,而转发出去。不过,这里的上游服务器组应该包含哪些主机?Nnginx 直接在配置文件指定,为了便于实现兼具某种程度动态的效果,可以在指定上游主机时不使用 IP 地址,而是 DNS 名称,只要 DNS 本身能解析到对应的 上游主机地址。不过,这又进入了复杂性,所以动态是有代价的。
而对于 Envoy 而言,这里的服务发现有一部分功能就是实现上述描述:基于 DNS 的服务发现。但是,它比 Nginx 的功能要强大。首先,它不仅支持 DNS 发现,配置上游端点的时候直接指定 DNS 主机名而非 ip 地址,基于解析找到上游主机是谁。而 envoy 还能基于 DNS 的 A 记录结果,如果不止一条,能把每条记录自动生成上游的可被代理的服务器主机端点。Envoy 也支持在大的架构当中,如果解析的结果有非常多,支持仅取出海量结果当中的第一个作为上游端点。
动态配置:配置的发现和变动无需重启 envoy,无需 reload。所有的发现和运行过程的变动以接近实时的方式反映到 envoy 内部配置上。这种动态能力,才是真正意义上的动态能力。而 Nginx 仅仅是平滑迁移,因为需要手动 reload。而适配到现代云原生应用的 Nginx 实际上也做各种改进了,但打补丁式的改进和从设计之初就适配到这种应用场景 Envoy 而言,二者的特性在这方面的比较肯定是不可同日而语的。
Health checking : 健康状态检查
对上游服务器做健康状态检测,Nginx 只能实现主动健康状态检测(过去是这样,除非主动增加探测机制)
Envoy 支持两种健康检测功能:
主动健康状态检测:代理者向被代理端主动发起探测包,如果对方能响应的或者是期望结果就是健康的;
被动健康状态检测:对任何流量不做额外任何流量的增加,而只是根据正常通信结果中报文的响应码分析来判定,也称作异常值探测;
Advanced load balancing : 高级负载均衡机制
Envoy 除了能支持 Nginx 中的算法的负载均衡:rr、wrr、ls、wls 外,还支持基于地域位置的调度,基于优先级调度的全局负载均衡逻辑。
Front/edge proxy support :前端/边缘 代理支持
基于前端边缘代理,基于网格内部代理。Kubernetes 的 Ingress controller 仅管理进出集群的流量,这种称为边缘代理或前端代理。而在整个集群内部仅负责代理某个服务的请求,称为网格内部的代理。
Best in class observability :可观测性
基于前端边缘代理,基于网格内部代理。Envoy 原生内部支持,内嵌支撑现代化云原生要求的监控机制,方便打印内部的访问日志以便于 elk 这样的工具采集和存储。
在内部直接暴露 Prometheus 格式的指标,通过 admin 接口获取原生 prometheus 的 metrics。并且内嵌支持多种分布式跟踪系统,包含 skywalking。
zone aware, priority/locality load balancing :区域感知路由,基于优先级的路由机制、负载均衡机制、基于位置负载均衡机制
circuit breaking :中断、断路器,熔断
outlier detection :异常探测,被动健康状态检测当中的异常值检测
retries, retry policies :重试,重试策略,局部故障处理逻辑
timeout (including budgets) :超时,局部故障处理逻辑
traffic shadowing :流量镜像,直接把生产流量复制一份打到预发环境测试的一种常用手段,测试机制;
rate limiting:限速,可以限流
access logging, statistics collection :访问日志,统计数据收集:通过内生接口暴露
request racing :请求竞速,哪些能够被引入的流量取决于本身到达的速度
工作逻辑
理解 Envoy 作为反向代理的工作特性,我们以 nginx 为例进行说明。一个 Nginx 完全可实现监听多个虚拟主机(虚拟服务),而且 virtualService 还可以使用不同的 IP 地址和套接字。虚拟主机通常三种类型:
基于端口:监听多个套接字
基于 IP 地址:监听多个套接字,仅 IP 不同
基于 FQDN,主机头:同一个套接字之上{IP:Port},基于请求报文当中的主机名 Host,来判定目标虚拟机主机
在 Envoy 中,前二者通过 Listener 定义,后者为 virtualService。
可以拼接三者方式来实现虚拟主机,比如使用同一个 IP 地址,不同端口提供多个 Listener 套接字,并各自提供多个虚拟机主机。
通过监听套接字的不同,分别为多个不同请求代理流量请求。Envoy 内部还有类似 Nginx 那样,通过串联多个不同模块,来实现不同的功能。这允许用户在配置文件中指定用户使用的模块,提供什么样的流量处理机制。在 Envoy 内部,这样的模块称为 Filter,或者扩展 Extexstion。因此可按需拼接,串联多个过滤器,实现对流量的不同功能的处理。
比如,串联过滤器,将四层流量解封到应用层对 Http 协议进行代理,意味着这是七层代理。而后再创建 Route,就可以实现七层路由机制。
在 Envoy 上,这个过程称为定义过滤器链的过程。只有定义完成后,这一类特定流量该如何被处理,处理完成后指定代理给上游的端点:Cluster。Cluster 如同 Nginx 中的 Upstream servers,作为真正处理代理流量的端点。
当流量来临时,由上游哪个端点接受并处理?此时,应该定义负载均衡算法,并对上游端点做健康状态检测。或者是否需要连接池,这些都是需要定义在集群中。
总的来说,前端为路由管理,后端为集群管理部分,由集群管理器去管理。
Envoy 显著特性
性能、可扩展性及动态可配置性
性能:除了大量功能外,envoy 还提供极高的吞吐量和低尾差异延迟,同时消耗相对较少的 CPU 和 RAM;
可扩展性:envoy 在 L4 和 L7 上提供了丰富的可插拔过滤器功能,允许用户轻松添加新功能;
API 可配置性:envoy 提供了一组可由控制平面服务实现的管理 API,也成为 xDS API;
若控制平面实现了这所有的 API,则可以使用通用引导配置在整个基础架构中允许 envoy;
所有进一步的配置更改都可通过管理服务器无缝地进行动态传递,使得 envoy 永远不需要从新启动,这使得 envoy 称为一个通用数据平面,当与足够复杂的控制平面相结合时,可打打降低整体操作复杂性;
在修改 Envoy 配置方面,既可以直接修改 envoy 的配置文件,重启 envoy 让其加载配置这种传统的配置方式,Envoy 又可以支持通过 Api:把配置功能当做接口把自己的各种封装的过滤器和各种功能,直接暴露出去。它可接受 Api 信息,只要对方遵循 Api 的接口规范即可。这意味着它可通过配置服务器 config server 接收配置信息,在 server 上提供配置信息,envoy 就可以对接到 server 建立连接,一旦有自己的配置信息生成,则加载到本地并生效。
如果改变配置服务器的配置信息,envoy 会发现这些变更。它可通过轮训方式确定是否发生变更,比如 1 分钟,或者建立始终在线双线的 grpc 连接,一旦有新配置会主动推送给 envoy,envoy 动态生效。
配置服务器如果能够统一管理多个 Envoy 时,它就是控制平面了。
envoy xDS API 存在 v1、v2、v3 三个版本
v1 API 仅使用 json/REST,本质上是轮询;
v2 API 是 v1 的演进,它是 v1 功能的超集,新的 API 模式使用 proto3 指定,并同时以 grpc 和 REST+json/YAML 端点实现;
v3 API 当前支持的版本,支持 start_tls、拒绝传入的 tcp 连接、4096 位的 tls 秘钥、skywalking 和 wasm 等;
envoy 已成为现代服务网格和边缘网关的通用数据平面 API,Istio、Ambassador 和 Gloo 等项目均是为此数据平面代理提供的控制平面;
很多 Ingress Controller 内部使用的代理服务器都是基于 envoy 的二次封装
目前 Kubernetes 的 Ingress Controller 有三类:nginx,envoy,traefix
Envoy 的拓扑结构
整体的拓扑结构如上图所示,核心组件实现连接下游(客户端)和上游(被代理服务器端),为了能够接受客户端的请求流量,需要 Listener 接入。Listener 支持功能增强和配置,基于 istner filter:专用于扩展 Listener 功能的过滤器实现。
将流量接入后,可将其交给由各种 Filter 串联起来的预处理,处理完毕后可路由给上游的端点。
每个服务器在 Envoy 的语境中称为端点,每个 endPoint 本身可按照指定方式被发现并定义为不同集群,比如 ClusterA、ClusterB。他们使用的端点允许交叉,不管是哪一种 fiter 串联起来的 filter 链,最终由 route 功能,route 过滤器路由给选定的某个上游集群。
只是不同的的代理协议可能路由给上游集群的方式不同,比如支持应用层协议代表中 http 的代理,就是 HTTP 的路由器;如果是 Mongodb 的代理,就是 Mongodb 的路由器,tcp 代理就是 tcp 路由器,他们支持的路由功能各不相同。而 envoy 核心设计面向 http 协议的,特性最为丰富就是 http 协议的路由器。
其实,现代服务网格中的微服务的通信,都应该基于 http 或 grpc 协议来实现的。
Envoy 基础组件
Envoy 支持 xds API,从各种配置服务器中接收不同类型的配置信息。为了便于管理,domain 将内部可配置的配置信息通过分成多个不同子类,每个子类分别使用不同的配置服务器配置。为了区别他们,每个类别的配置会有专用的 API。
Envoy 基于 API 的可配置性
Envoy 将配置信息划分成了多个类别,每个类别有其专用 API,这些 API 的功能主要在于发现配置,通常称为发现 API。这些 API 称为 DS API,根据类别的不同,将其归为:
发现 Listener:专用于配置新的 Listener,或删除 Listener 等配置信息,通过发现 Listener 的 API 实现,LDS
发现 Route:RDS
发现集群:CDS
集群端点的发现:EDS
secret 信息的发现:SDS,保存在 envoy 内部的 secret 资源
这些 DS api 由子 api 打包起来的,称为 xDS:超级 DS。xDS 指的就是 API 组合起来的统称,Envoy 可通过多个不同的 xds server 发现不同的配置信息。
为了管理方便,也可以用统一的服务将 xDS 打包在一起,同时提供多种 discovery service。这些配置 API 既可以通过不同的专用 Configuration Server 获取配置,也可以由一个统一的 Configuration Server 配置,他们之间可能存在依赖关系。比如 RDS 定义在 LDS(LDS→RDS),端点配置在集群之上(CDS→EDS)。如果发现了端点但集群没有定义,那也无意义。因此配置之间存在依赖关系,从这个意义上讲,路由只是侦听器的子集,端点只是集群的子集。
因此,可以在发现配置时,通过手段加以确保他们的依赖关系。
一个请求从客户端发出,到达 Envoy 的侦听器,侦听器通过一系列的 filter chain 处理后,路由把请求路由到集群,从一组端点中挑一个去响应客户端报文。
侦听器可硬编码在配置文件中,也可以通过 LDS 动态配置。路由信息,集群,端点信息也是如此。
配置信息加载方式
以静态方式定义,放置在配置文件中
通过 xds API 动态加载。
动态加载时如何确实服务器的信息?因此还应该提供一个配置文件给 Envoy,只不过它仅包含配置服务器的地址。
此处配置文件中通常既包含了指明要动态加载的配置,也包含配置服务器的地址。配置文件有一个专门的称呼:bootstrap config file,以至于在 Envoy 启动时直接加载的配置文件都称为 bootstrap config file。
通常,动态配置与静态配置混合使用,比如路由信息是动态加载的,侦听器是静态定义的,集群是静态定义,端点动态加载。无论纯动态还是混合使用,都应该指明配置服务器的位置。
envoy 常用术语
主机(Host):一个具有网络通信能力的端点,例如服务器、移动智能设备等
集群(Cluster):集群是 Envoy 连接到的一组逻辑上相似的端点;在 v2 中, RDS 通过路由指向集群, CDS 提供集群配置,而 Envoy 通过 EDS 发现集群成员,即端点;
下游(Downstream):下游主机连接到 Envoy,发送请求并接收响应,它们是 Envoy 的客户端;
上游(Upstream):上游主机接收来自 Envoy 的连接和请求并返回响应,它们是 Envoy 代理的后端服务器;
端点(Endpoint):端点即上游主机,是一个或多个集群的成员,可通过 EDS 发现;
侦听器(Listener):侦听器是能够由下游客户端连接的命名网络位置,例如端口或 unix 域套接字等;
位置(Locality):上游端点运行的区域拓扑,包括地域、区域和子区域等;
管理服务器(Management Server):实现 v3 API 的服务器,它支持复制和分片,并且能够在不同的物理机器上实现针对不同 xDS API 的 API 服务;
地域(Region):区域所属地理位置;
区域(Zone): AWS 中的可用区(AZ)或 GCP 中的区域等;
子区域: Envoy 实例或端点运行的区域内的位置,用于支持区域内的多个负载均衡目标;
xDS: CDS 、 EDS、 HDS 、 LDS、 RLS(Rate Limit)、 RDS 、 SDS、 VHDS 和 RTDS 等 API 的统称;
评论