目录
- Envoy 网络拓扑及请求流程
- 1. 术语
- 2. 网络拓扑
- 3. 配置
- 4. 更高层的架构
- 5. 请求流程
- 1. Listener TCP 接收
- 2. 侦听器过滤器链和网络过滤器链匹配
- 3.TLS传输套接字解密
- 4.网络过滤器链处理
- 5. HTTP/2 codec 解码
- 6. HTTP filter 链处理
- 7. 负载均衡
- 8. HTTP/2 codec 编码
- 9. TLS transport socket 加密
- 10. 响应路径和HTTP生命周期
- 11.Post-request 处理
Envoy 网络拓扑及请求流程
首先描述Envoy如何适合请求的请求路径,然后描述内部请求从下游到达Envoy代理之后发生的内部事件
1. 术语
Envoy在其代码库和文档中使用以下术语:
- Cluster/(集群):,集群是指 Envoy 连接到的逻辑上相同的一组上游主机。 Envoy 通过服务发现 来发现集群的成员。可以通过 主动健康检查来确定集群成员的健康状态。 Envoy 通过负载均衡策略 决定将请求路由到哪个集群成员
- Downstream/(下游): 下游主机连接到Envoy, 发送请求并接收响应。可能是本地应用程序(在Sidecar模型中)或网络节点 ,在非Sidecar模型中,这是一个远程客户端, 对于Envoy(服务端),相当于client .
- Endpoints(端点):实现逻辑服务的网络节点。它们在集群中被分组。集群中的端点位于Envoy代理的上游。
- Filter(过滤器):连接或请求处理管道中的模块,用于提供某些请求处理时的连接或请求处理的管道模块。 类似与Unix系统中的管道符。
- Filter chain: 类似 过滤器
- Listeners/(监听器): Envoy模块,负责绑定到
IP /端口
, 可以被下游客户端连接。Envoy 暴露一个或者多个监听器给下游主机连接。 - Upstream/(上游):上游主机接收来自 Envoy 的连接和请求,并返回响应。这可能是本地应用程序(在Sidecar模型中)或网络节点。在非Sidecar模型中,这对应于Envoy来说,相当于服务端(server)。
2. 网络拓扑
请求如何流经网络(包括Envoy)中的组件取决于网络的拓扑。Envoy可用于多种网络拓扑中。
-
Envoy最初是作为Service Mesh Sidecar代理,从应用程序中排除了负载平衡,路由,可观察性,安全性和发现服务。在Service Mesh模型中,请求数据流经Envoy作为网络的网关。请求通过入口或出口侦听器到达Envoy:
-
Ingress listeners
从Service Mesh中的其他节点获取请求,并将其转发到本地应用程序。本地应用程序的响应通过Envoy流回到下游。 -
Egress listeners
从本地应用程序获取请求,并将其转发到网络中的其他节点。这些接收节点通常还将运行Envoy,并通过其入口侦听器接受请求。
-
-
除Service mesh外,Envoy还用于各种配置中。例如,它还可以充当内部负载平衡器:
-
作为网络边缘上的入口/出口(ingress/egress proxy)代理:
-
在实际应用中,会混合使用上面的方法, 其中Envoy在service mesh(服务网格)中, 在边缘网络上作为 内部负载均衡 使用。 请求路径可能会遍历多个Envoy
-
Envoy可以在多层拓扑中进行配置以实现可伸缩性和可靠性,其中请求首先通过边缘Envoy,然后再通过第二个Envoy层:
在上述所有的网络拓扑模型中,请求将从下游通过TCP
,UDP
或 Unix域套接字
到达特定的Envoy。 Envoy将通过TCP
,UDP
或Unix域套接字
向上游转发请求。
下面重点介绍一个Envoy代理模式
3. 配置
Envoy是扩展性很强的平台。 这导致可能的请求路径组合很多,具体取决于:
- L3 / 4层协议例如TCP,UDP,Unix域套接字
- L7层协议,例如HTTP / 1,HTTP / 2,HTTP / 3,gRPC,Thrift,Dubbo,Kafka,Redis和各种数据库。
- 传输socket套接字,例如纯文本,TLS,ALTS。
- 连接路由,例如PROXY协议,原始目的地,动态转发。
- 身份验证和授权
- 断路器和异常值检测配置以及激活状态。
- 许多其他配置,如用于网络,HTTP,侦听器,访问日志记录,运行状况检查,跟踪和统计信息扩展等配置
以下示例主要使用到如下配置相关
- 一次具有TLS协议的连接上游和下游的HTTP/2的请求
HTTP连接管理器
作为唯一的网络过滤器
。- 假定一个客户自定义过滤器和一个路由作为 HTTP过滤器
- 文件系统访问日志记录
- 具有静态端点的单个群集
- 统计接收器
为了简单起见,假定使用静态引导程序配置文件:example.yml
# 静态资源
static_resources:
listeners:
# 简单的监听器,绑定端口443
- name: listener_https
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 443
# A single listener filter exists for TLS inspector.
listener_filters:
- name: "envoy.filters.listener.tls_inspector"
typed_config: {}
# On the listener, there is a single filter chain that matches SNI for acme.com.
filter_chains:
- filter_chain_match:
# This will match the SNI extracted by the TLS Inspector filter.
server_names: ["acme.com"]
# 下游 TLS 配置.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.useso.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: { filename: "certs/servercert.pem" }
private_key: { filename: "certs/serverkey.pem" }
filters:
# HTTP连接管理器
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.useso.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
use_remote_address: true
http2_protocol_options:
max_concurrent_streams: 100
# 基于系统文件的 日志
access_log:
- name: envoy.access_loggers.file
typed_config:
"@type": type.useso.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
path: "/var/log/envoy/access.log"
# 路由表,匹配本地服务的 /foo请求
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["acme.com"]
routes:
- match:
path: "/foo"
route:
cluster: some_service
# CustomFilter and the HTTP router filter are the HTTP filter chain.
http_filters:
# - name: some.customer.filter
- name: envoy.filters.http.router
clusters:
- name: some_service
connect_timeout: 5s
# 上游 TLS 认证.
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.useso.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
load_assignment:
cluster_name: some_service
# 静态端点分配(逻辑主机节点)
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.1.2.10
port_value: 10002
- endpoint:
address:
socket_address:
address: 10.1.2.11
port_value: 10002
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.useso.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options:
max_concurrent_streams: 100
- name: some_statsd_sink
connect_timeout: 5s
# The rest of the configuration for statsd sink cluster.
# statsd sink.
stats_sinks:
- name: envoy.stat_sinks.statsd
typed_config:
"@type": type.useso.com/envoy.config.metrics.v3.StatsdSink
tcp_cluster_name: some_statsd_sink
4. 更高层的架构
Envoy中的请求处理路径包括两个主要部分:
- Listener子系统,用于处理下游请求。它还负责管理下游请求生命周期以及到客户端的响应路径。下游的HTTP/2请求位于此模块中
- Cluster子系统,负责选择和配置与端点的上游连接。可以从此处了解群集和端点健康状况,负载均衡和连接池的地方。上游HTTP / 2请求处理位于此模块
这两个子系统与HTTP路由器过滤器桥接,该过滤器将HTTP请求从下游转发到上游。
Envoy还具有基于事件的线程模型。主线程负责服务器的生命周期,配置处理,状态等,并且一些工作线程负责处理请求。所有线程都围绕事件循环运行,当监听器接收到一个连接请求时,该连接将其生命周期绑定到一个单独的 worker 线程。worker 线程则负责执行监听、过滤和转发。每个工作线程都维护自己的与上游端点的TCP连接池。UDP过滤器状态为给定的工作线程共享,过滤器负责根据需要提供会话语义。
worker线程很少共享状态,并且很少并行运行。该线程模型可以扩展到核心数量非常多的CPU。
5. 请求流程
使用上面的示例配置简要概述了请求和响应的生命周期:
- workder线程上运行的Envoy Listener接受来自下游的一个TCP连接。
- 监听过滤器创建并运行,将会提供 SNI 和其他 TLS欲配置项。一旦完成后,侦听器将匹配网络过滤器链。每个侦听器可能具有多个过滤器链,这些过滤器链在目标IP CIDR范围,SNI,ALPN,源端口等的某种组合上匹配。传输套接字(在我们的示例中为TLS传输套接字)与此过滤器链相关联。
- 在网络读取时,TLS传输套接字将从TCP连接读取的数据流并进行解密,以进行进一步处理。
- 网络过滤器链已创建并运行。 HTTP最重要的过滤器是HTTP连接管理器,它是链中的最后一个网络过滤器。
- **HTTP connection manager **中的HTTP / 2编解码器 从TLS连接获取到多个独立的数据流进行解密。每个流只处理一个请求和响应。
- 对于每个HTTP流,会有一个HTTP过滤器创建和运行。该请求首先通过CustomFilter(可以读取和修改该请求),最重要的HTTP过滤器是位于HTTP过滤器尾部的路由器过滤器。在路由器过滤器上调用解码时,将选择路由并选择集群。数据中请求头将转发到该群集中的上游端点。路由器过滤器从群集管理器中为匹配的群集获取HTTP连接池,以执行此操作。
- 集群中具体的负载均衡 被执行用以查找一个端点。群集的断路器被检查,以确定是否允许新的数据流。如果端点的连接池为空或容量不足,一个新的连接(端点)被创建。
- 上游端点连接器HTTP/2的解码器将请求的流与通过单个TCP连接流向上游的任何其他流进行多路复用和帧化。
- 上游端点连接的TLS传输套接字加密这些字节并将它们写入上游连接的TCP套接字。
- 包含请求头,请求正文和尾部组成的 Request 在上游被代理,Response在下游被代理。响应以与请求相反的顺序通过HTTP过滤器,从路由器过滤器开始并通过CustomFilter,然后再发送到下游。
1. Listener TCP 接收
如图
ListenerManager负责获取侦听器的配置,并实例化绑定到其各自IP /端口的多个侦听器实例上。侦听器可能处于以下三种状态之一:
- Warming: 侦听器正在等待配置依赖项(如外部配置,动态机密)。侦听器尚未准备好接受TCP连接。
- Active: 侦听器已绑定到其IP /端口并接受TCP连接。
- Draining: 监听已存在的 TCP链接持续一段时间, 不再接受新的TCP链接
每个工作线程为每个配置的侦听器维护自己的侦听器实例。每个侦听器都可以通过SO_REUSEPORT绑定到同一端口,或者共享一个绑定到该端口的套接字。当新的TCP连接到达时,内核将决定哪个工作线程将接受该连接,并且该工作线程的侦听器将调用其Server :: ConnectionHandlerImpl :: ActiveTcpListener :: onAccept()回调。
2. 侦听器过滤器链和网络过滤器链匹配
然后,工作线程的侦听器将创建并运行侦听器过滤器链。过滤器链是通过应用每个过滤器的过滤器工厂而创建的。工厂知道过滤器的配置,并为每个连接或流创建一个新的过滤器实例。
3.TLS传输套接字解密
Envoy通过TransportSocket扩展接口提供可插拔的运输插座。传输套接字遵循TCP连接的生命周期事件,并读写网络缓冲区。运输套接字必须实现的一些关键方法是:
virtual void onConnected() PURE;
virtual IoResult doRead(Buffer::Instance& buffer) PURE;
virtual IoResult doWrite(Buffer::Instance& buffer, bool end_stream) PURE;
virtual void closeSocket(Network::ConnectionEvent event) PURE;
当TCP连接上有可用数据时,Network :: ConnectionImpl :: onReadReady()会通过SslSocket :: doRead()调用TLS传输套接字。然后,传输套接字在TCP连接上执行TLS握手。握手完成后,SslSocket :: doRead()将解密的字节流提供给Network :: FilterManagerImpl的实例,该实例负责管理网络过滤器链。
需要特别注意的是,无论是TLS握手还是过滤器管道暂停,都无法阻止任何操作。由于Envoy是基于事件的,因此任何需要额外数据处理的情况都将导致事件的尽早完成,并使CPU产生另一个事件。当网络使更多数据可供读取时,读取事件将触发TLS握手的恢复。
4.网络过滤器链处理
与侦听器过滤器链一样,Envoy将通过Network :: FilterManagerImpl实例化其过滤器工厂中的一系列网络过滤器。该实例对于每个新连接都是新鲜的。网络过滤器(如传输套接字)跟随TCP生命周期事件,并随着数据从传输套接字可用而被调用。
网络过滤器是作为管道组成的,与每个连接一个的传输套接字不同。网络过滤器分为三种:
- ReadFilter 实现onData(),当连接中有数据可用时(由于某些请求)而调用。
- **WriteFilter **实现onWrite(),在即将将数据写入连接时(由于某些响应)而调用。
- Filter 同时实现ReadFilter和WriteFilter。
5. HTTP/2 codec 解码
Envoy中的HTTP / 2编解码器基于nghttp2。
6. HTTP filter 链处理
对于每个HTTP流,HCM都按照上面为侦听器和网络过滤器链建立的模式实例化HTTP过滤器链。
HTTP过滤器接口共有三种:
- StreamDecoderFilter 带有用于请求处理的回调。
- StreamEncoderFilter 带有用于响应处理的回调。
- StreamFilter 同时实现 StreamDecoderFilter 和StreamEncoderFilter.
请求路径将如下所示:
响应路径如下所示:
调用路由器过滤器时,路由将最终确定。所选路由的配置将指向上游群集名称。然后,路由器过滤器向ClusterManager询问群集的HTTP连接池。
7. 负载均衡
负载平衡是一种在单个上游群集内的多个主机之间分配流量以有效利用可用资源的方法。有许多不同的方法可以完成此任务,因此Envoy提供了几种不同的负载平衡策略。从高层次上讲,我们可以将这些策略分为两类:全局负载平衡和分布式负载平衡。
分布式负载平衡是指 让Envoy自己基于了解上游主机的位置来确定应如何将负载分配到端点。
例子:
- Active health checking: 通过对上游主机进行状况检查,Envoy可以调整优先级和位置的权重以解决不可用主机的问题。
- Zone aware routing: 可使Envoy选择使用更近的端点,而不必在控制平面中显式配置优先级。
- Load balancing algorithms:Envoy可以使用几种不同的算法来使用提供的权重来确定要选择的主机
每个集群都有一个负载均衡器,当新请求到达时,该负载均衡器会选择一个端点。Envoy支持多种负载平衡算法,例如加权轮循,磁悬浮列车,负荷最小,随机。
负载平衡器从静态引导程序配置,DNS,动态xDS(CDS和EDS发现服务)以及主动/被动运行状况检查的组合中获得有效分配。
选择端点后,将使用该端点的连接池来查找用于转发请求的连接。如果不存在与主机的连接,或者所有连接均处于其最大并发流限制,则除非连接集群最大连接的断路器跳闸,否则将建立新连接并将其放置在连接池中。如果配置并达到了连接的最大生存期流限制,则会在池中分配一个新的连接,并且耗尽受影响的HTTP / 2连接。
8. HTTP/2 codec 编码
所选连接的HTTP / 2编解器将请求流与单个TCP连接流向同一上游的任何其他流进行多路复用。这与HTTP / 2编解码器解码相反。
与下游HTTP / 2解码器一样,上游编解码器负责获取Envoy对HTTP的标准抽象,即在单个连接上与请求/响应标头/正文/尾部复用的多个流,并通过生成一系列HTTP / 2帧将其映射到HTTP / 2
9. TLS transport socket 加密
上游端点连接的TLS传输套接字对HTTP / 2编解码器输出中的字节进行加密,并将其写入用于上游连接的TCP套接字。与TLS传输套接字解密一样,示例中,群集配置了提供TLS传输安全性的传输套接字。上游和下游传输套接字扩展存在相同的接口。
10. 响应路径和HTTP生命周期
Reuest 在上游被代理,Response在下游被代理。响应以相反的顺序通过HTTP和网络过滤器。从请求。
解码器/编码器请求生命周期事件的各种回调将在HTTP过滤器中调用,例如当响应预告片被转发或请求主体被流式传输时。同样,当请求期间数据继续在两个方向上流动时,读/写网络过滤器也将调用其各自的回调。
请求有可能提前终止。这可能是由于(但不限于):
- 请求超时。
- 上游端点重启。
- HTTP筛选器流重置。
- 熔断
- 上游资源不可用,例如缺少路线的群集。
- 没有健康的端点
- DoS保护。
- HTTP协议违规。
- 来自HCM或HTTP过滤器的本地回复。例如。速率限制HTTP过滤器返回429响应。
如果发生这些情况中的任何一个,Envoy内部可能会发送生成的响应(如果尚未发送上游响应头),或者将流重置(如果响应头已经转发至下游)。
11.Post-request 处理
请求完成后,数据流将被销毁。还会发生以下情况:
- 请求后统计信息将进行更新(例如,计时,活动请求,升级,运行状况检查)。但是,在请求处理期间,某些统计信息会更早更新。此时,统计信息尚未写入统计信息接收器,它们由主线程定期进行批处理和写入。在我们的示例中,这是一个statsd接收器。
- 访问日志将写入访问日志接收器。示例中,这是一个文件访问日志。