Envoy 学习笔记(一)

该系列学习笔记是作者为记录云原生基础架构学习过程而写。若想要详细学习 envoy,大家可以去 tetrate 官网和官方文档中学习。

如果没有任何接触云原生的经验,可以在学习相关概念前,去了解以下概念:

  • kubernetes(这个是云原生的基础)
  • 微服务架构是什么(微服务,RPC,服务发现等等)
  • 服务网格(Service Mesh)是什么

0. 什么是 Envoy?

如果是云原生相关的 devops 工程师应该多多少少都会了解 envoy 这个东西。

在行业大量使用 微服务架构 和 云原生解决方案 的今天,一切向"云"靠拢,那么对于云上的流量治理网络调试就是一件复杂但必要的事情。

各个微服务可能使用不一致的流量跟踪和记录机制,对于开发人员来说,这方面调试的复杂性大大增加。对于这种类似多对多映射的状况,我们就很难确定问题发生在哪里,以及如何解决。如果你是一个业务开发,而调试网络问题不是你所擅长的话,那就更是如此。

将网络问题从应用程序堆栈中抽离出来,由另一个组件来处理网络部分,让调试网络问题变得更容易。这就是 Envoy 所做的事情。

也就是说,你写的微服务不用再关注一系列网络与流量问题相关的程序逻辑(如接口速率限制,鉴权验证,超时重试等等)这些全部抽离出来,在envoy中配置好帮你完成。同时,envoy 会做好流量跟踪记录相关的工作,在多个envoy联合工作时,就可以通过一个统一的控制面去收集这些流量信息,治理效果就很显著了。

1. Envoy 的两种模式

envoy 是 云原生时代 代理服务器应用的一种,和nginx类似,但它比 nginx 这种传统代理服务器更胜一筹。

它有两种部署方式,一种是很符合它本质工作的部署方式:边缘代理,这种情况下,envoy部署用于构建 API 网关,和正常网关的功能差不多。

另一种部署方式就是:边车部署(sidecar) ,这种情况下就是前文我们说的,将网络层面的工作从应用程序中"抽离"出来,单独运行。sidecar方式部署时,envoy是和应用服务一起部署的。Envoy 和应用程序形成一个原子实体,但仍然是独立的进程。应用程序处理业务逻辑,而 Envoy 则处理网络问题。(最常见的例子在 k8s 里面,就是一个pod里面放两个容器,一个跑业务代码,另一个就是envoy,在pod内部业务代码容器的流量都会先经过envoy容器,再由envoy传到pod外)

sidecar部署的方式用来组织当前云原生中的服务治理大杀器------服务网格。但当前不会提及,后面学习也是先学习边缘代理模式。

2. Envoy的高级特性

2.1 进程外架构

这里要说的是 envoy 的sidecar部署方式的好处。tetrate 学习资料中对这部分介绍写得比较清晰,我放一下原文(有些模糊的地方我会做注释,用斜体标出):

Envoy 是一个独立的进程,旨在与每个应用程序一起运行 ------ 也就是我们前面提到的 Sidecar 部署模式。集中配置的 Envoy 的集合形成了一个透明的服务网格。

路由和其他网络功能的责任被推给了 Envoy。应用程序**(进行DNS服务发现时)** 向一个虚拟地址(localhost)而不是真实地址(如公共 IP 地址或主机名)发送请求,不知道网络拓扑结构。应用程序不再承担路由的责任,因为该任务被委托给一个外部进程。应用程序的流量会先到达envoy监听的端口。原理是envoy会在 L4 层修改 iptables 规则,进行流量劫持,将出站和入站流量重定向到 Envoy 监听的端口

与其让应用程序管理其网络配置,不如在 Envoy 层面上独立于应用程序管理网络配置。在一个组织中,这可以使应用程序开发人员解放出来,专注于应用程序的业务逻辑。

Envoy 适用于任何编程语言。你可以用 Go、Java、C++ 或其他任何语言编写你的应用程序,而 Envoy 可以在它们之间架起桥梁。Envoy 的行为是相同的,无论应用程序的编程语言或它们运行的操作系统是什么。

Envoy 还可以在整个基础设施中透明地进行部署和升级。这与为每个单独的应用程序部署库升级相比,后者可能是非常痛苦和耗时的。

进程外架构是有益的,因为它使我们在不同的编程语言 / 应用堆栈中保持一致,我们可以免费获得独立的应用生命周期和所有的 Envoy 网络功能,而不必在每个应用中单独解决这些问题。

"集中配置的 Envoy 的集合形成了一个透明的服务网格"。服务网格的核心目标是解决微服务架构中服务间通信的复杂性,而多语言环境下的互通问题正是其重要应用场景之一。envoy很好地契合了这一任务。

2.2 L3/L4 过滤器结构

Envoy 是一个 L3/L4 网络代理,根据 IP 地址和 TCP 或 UDP 端口进行决策。它具有一个可插拔的过滤器链(就像linux上的管道),可以编写你的过滤器来执行不同的 TCP/UDP 任务。

Envoy 可以通过堆叠所需的过滤器来构建逻辑和行为,形成一个过滤器链。当前已有很多做好了的过滤器,支持诸如原始 TCP 代理、UDP 代理、HTTP 代理、TLS 客户端认证等任务。Envoy 也是可扩展的,我们可以编写我们的过滤器。(特别是当下结合 wasm 技术)

2.3 L7 过滤器结构

Envoy 支持一个额外的 HTTP L7 过滤器层。我们可以在 **HTTP 连接管理子系统(HCM)**中插入 HTTP 过滤器,执行不同的任务,如 cache、速率限制、路由 / 转发等等。

2.4 HTTP 相关支持

HTTP2 支持

Envoy 同时支持 HTTP/1.1 和 HTTP/2,并且可以作为一个透明的 HTTP/1.1 到 HTTP/2 的双向代理进行操作。这意味着任何 HTTP/1.1 和 HTTP/2 客户端和目标服务器的组合都可以被桥接起来。即使你的传统应用没有通过 HTTP/2 进行通信,如果你把它们部署在 Envoy 代理旁边,它们最终也会通过 HTTP/2 进行通信。

推荐在所有的服务间配置的 Envoy 使用 HTTP/2,以创建一个持久连接的网格,请求和响应可以在上面复用。

HTTP 路由

当以 HTTP 模式操作并使用 REST 时,Envoy 支持路由子系统HCM,能够根据 路径、权限、内容类型 和 运行时间值 等规则来路由和重定向请求。

在将 Envoy 作为构建 API 网关的前台 / 边缘代理时,这一功能非常有用,在构建服务网格(sidecar 部署模式)时,也可以利用这一功能。

2.5 gRPC 支持

Envoy 支持作为 gRPC 请求和响应的路由和负载均衡底层所需的所有 HTTP/2 功能。

gRPC 是一个开源的远程过程调用(RPC)系统,它使用 HTTP/2 进行传输,并将协议缓冲区作为接口描述语言(IDL),它提供的功能包括认证、双向流和流量控制、阻塞 / 非阻塞绑定,以及取消和超时。

2.6 服务发现和动态配置

我们可以使用静态配置文件来配置 Envoy,这些配置文件描述了服务间通信方式,后面我们会有相关学习。

对于静态配置 Envoy 不现实的高级场景,Envoy 支持动态配置,在运行时自动重新加载配置。

一组名为 xDS 的发现服务可以用来通过网络动态配置 Envoy,并为 Envoy 提供关于主机、集群 HTTP 路由、监听套接字和加密信息。

但这个是sidecar部署以及服务网格相关的内容了,简单来说,就是有一个控制平面 动态收集当前微服务集群中的信息,然后将这些信息转换成各种各样的 config.yaml ,再统一下发到各个 sidecar envoy 中。

2.7 健康检查

负载均衡器(LB)有一个特点,那就是只将流量路由到健康和可用的上游服务。

Envoy 支持健康检查子系统(Health Checking),对上游服务集群进行主动健康检查。健康检查包括很多种类型,有HTTP,TCP,gRPC等,采用类似于定期发送心跳包的方式。

(但需要后端实现对应心跳包响应逻辑,像HTTP主动心跳检查,后端应该实现一个 /health 的接口来响应200状态。)

(但如果 Envoy 完全依赖 服务网格 EDS机制(通过监听 Kubernetes 的 Endpoints 状态),并且 Kubernetes 的 readinessProbe 已能准确反映应用的健康状态,那么 Envoy 基本无需额外配置主动健康检查。后面会继续学习。)

然后,Envoy 使用 服务发现 和 健康检查信息 的组合来确定健康的负载均衡目标。

Envoy 还可以通过 异常点检测子系统(Outlier Detection) 支持被动健康检查.

有一个简单的总结表,暂时看不懂也没关系:

场景 是否需要 Envoy 主动检查 理由
纯 Kubernetes + EDS Kubernetes 探针已足够,Envoy 依赖 EDS 状态路由
需要深度健康检查 叠加 Envoy 检查以补充 Kubernetes 探针的不足
服务网格(如 Istio) 依赖 Kubernetes 探针 + 被动异常检测,控制平面保障 EDS 数据一致性
混合环境(K8s + 非 K8s 服务) 部分需要 非 Kubernetes 服务需通过 Envoy 主动检查

2.8 高级负载均衡

Envoy 支持自动重试、断路、全局速率限制(使用外部速率限制服务)、shadow请求(或流量镜像)、异常点检测和请求对冲。

2.10 TLS 终止

应用程序和代理的解耦使网格部署模型中所有服务之间的 TLS 终止(双向 TLS)成为可能。

Envoy 可以作为代理,负责处理 TLS 加密连接的建立、解密和验证,而无需后端应用程序直接参与 TLS 握手或加密通信。这样业务代码只需要处理明文流量就好,也无需业务开发在应用中管理证书和密钥。

在服务网格(如 Istio)中,Envoy 作为 Sidecar 代理,集中管理 TLS 加密和身份认证

2.11 可观测性

为了便于观察,Envoy 会生成日志、指标和追踪。Envoy 目前支持 statsd(和兼容的提供者)作为所有子系统的统计。得益于可扩展性,我们也可以在需要时插入不同的统计提供商。

在服务网格如istio中,envoy收集的信息通过经典的 Prometheus + Grafana 的组合进行可视化。

2.12 HTTP/3 (Alpha)

Envoy 1.19.0 支持 HTTP/3 的上行和下行,并在 HTTP/1.1、HTTP/2 和 HTTP/3 之间进行双向转义。

3. Envoy 架构图

下面给出大致的 envoy 架构(偏向 sidecar 部署,其中 控制平面 是服务网格的东西)

具体对过滤器结构的介绍见下一章的【请求流作用模块】。
flowchart TB subgraph "Envoy Proxy" direction TB admin["Admin Interface"] subgraph "数据平面(Data Plane)" listeners["Listeners"] subgraph "过滤器结构" fc["过滤器链(Filter Chain)"] nf["Network Filters"] hcm["HTTP Connection Manager"] hf["HTTP Filters"] router["Router Filter"] end clusters["Clusters"] endpoints["Endpoints"] lb["Load Balancers"] hc["Health Checkers"] end subgraph "可观测性(Observability)" stats["统计(Statistics)"] logging["日志(Logging)"] tracing["追踪(Tracing)"] end end subgraph "控制平面(Control Plane)" xds["xDS API (Dynamic Configuration)"] end client[("客户端(Client)")] --> listeners listeners --> fc fc --> nf nf --> hcm hcm --> hf hf --> router router --> clusters clusters --> lb --> endpoints clusters --> hc --> endpoints endpoints --> backend[("后端服务(Backend)")] xds <-.-> admin xds <-.-> listeners xds <-.-> clusters xds <-.-> endpoints nf <-.-> stats & logging & tracing hf <-.-> stats & logging & tracing clusters <-.-> stats & logging & tracing

4. Envoy 的构建模块

在tetrate官网上, Envoy 的基本构建模块包括四部分:
graph TD; A[Listeners] --> B[Routes] B --> C[Clusters] C --> D[Endpoints]

但这个构建模块太过抽象,不太好理解,我将更加具体的三层四区抽象放在下面:
flowchart TB client(("客户端")) --> L subgraph "1. 监听与接收层" L["监听器 (Listener)"] LF["监听器过滤器"] FC["过滤器链 (Filter Chain)"] NF["网络过滤器 (Network Filters)"] end subgraph "2. 处理与路由层" HCM["HTTP 连接管理器(HCM)"] HF["HTTP 过滤器链"] RF["路由过滤器 (Router)"] end subgraph "3. 上游服务层" CL["集群 (Cluster)"] LB["负载均衡器"] EP["端点 (Endpoint)"] end subgraph "4. 控制与观测区" XDS["动态配置 (xDS API)"] OBS["可观测性组件"] end L --> LF --> FC --> NF --> | HCM 是一种 NetworkFilter | HCM HCM --> HF --> |RouterFilter 是一种 HTTPFilter, 也是最后一个| RF --> CL CL --> LB --> EP --> backend(("后端服务")) XDS -.-> L & HCM & CL & EP OBS -.-> NF & HF & CL & LB

以一个典型的 HTTP 请求为例:

  1. 客户端发起 HTTP 请求到 Envoy 监听的端口
  2. 监听与接收层:监听器接受连接并选择过滤器链;网络过滤器识别 HTTP 协议
  3. 处理与路由层:HTTP连接管理器(HCM)解析请求,HTTP过滤器链处理请求(认证、限流等),路由过滤器确定目标集群
  4. 上游服务层:集群中的负载均衡器选择一个健康的端点,请求被转发到该端点
  5. 后端服务处理请求并返回响应,响应沿原路返回客户端
  6. 控制与观测区:整个过程中,配置可能动态更新,请求被记录、统计和追踪

我们主要关注三个层,主要关注静态资源,在后面再介绍学习如何配置动态资源。

那么现在我们先从 Listener 开始介绍各个构建模块,思路还是跟着官网的学习路线走,对三层四区的抽象只是更加方便理解。

4.1 Listener 监听器

Envoy 暴露的监听器是命名的网络位置,可以是一个 IP 地址和一个端口,也可以是一个 Unix 域套接字路径。Envoy 通过监听器接收连接和请求。考虑一下下面的 Envoy 配置。

yaml 复制代码
 static_resources:
   listeners:
   - name: listener_0
     address:
       socket_address:
         address: 0.0.0.0
         port_value: 10000
     filter_chains: [{}]

通过上面的 Envoy 配置,我们在 0.0.0.0 地址的 10000 端口上声明了一个名为 listener_0 的监听器。这意味着 Envoy 正在监听 0.0.0.0:10000 的传入请求。

每个监听器都有不同的部分需要配置。然而,唯一需要的设置是地址。上述配置是有效的,你可以用它来运行 Envoy------ 尽管它没有用,因为所有的连接都会被关闭。因为我们让网络过滤器链 filter_chains 字段为空,因为在接收数据包后不需要额外的操作。

为了进入下一个构件(路由),我们需要创建一个或多个filter_chains,至少要有一个过滤器。

4.2 FilterChain 过滤器链机制

每个通过监听器进来的请求可以流经多个过滤器。我们可以写一个配置,根据传入的请求或连接属性选择不同的过滤器链。过滤器链可以对进入和离开 Envoy 的请求和响应进行处理(注意,有来有回,过滤器链不只是处理请求)。

我们先来全面介绍一下 Envoy 中的监听器类别,记得结合三层四区图来看!

Envoy 定义了三类过滤器:监听器过滤器、网络过滤器 和 HTTP 过滤器。注意,三类过滤器构成了一个层次化的请求处理管道,而不是平行组件。

  • 监听器过滤器 在收到数据包后立即启动,通常对数据包的头信息进行操作。监听器过滤器包括:
    • 代理监听器过滤器(提取 PROXY 协议头)
    • TLS 检查器监听器过滤器(检查流量是否为 TLS,如果是,则从 TLS 握手中提取数据)。
  • 网络过滤器(NetworkFilter) 通常对数据包的有效载荷进行操作,查看有效载荷并对其进行解析。例如,PostgreSQL 网络过滤器解析数据包的主体,检查数据库操作的种类或其携带的结果。
    • 一个特殊的、内置的网络过滤器被称为 HTTP 连接管理器 过滤器(HTTP Connection Manager Filter)或 HCM。HCM 过滤器能够将原始字节转换为 HTTP 级别的消息。它可以处理访问日志,生成请求 ID,操作头信息,管理路由表,并收集统计数据。我们将在以后的学习中对 HCM 进行更详细的介绍。
  • HTTP过滤器 ,就像我们可以为每个监听器定义多个网络过滤器(其中一个是 HCM)一样,Envoy 也支持在 HCM 过滤器中定义多个 HTTP 级过滤器。我们可以在名为 http_filters 的字段下定义这些 HTTP 过滤器。

总的来说是下面这样:

  1. Listener 有自己的监听器过滤器,除此之外,一个 Listener 包含一个或多个 FilterChain 。
  2. 一个FilterChain 包含一个或多个 NetworkFilter 。
  3. HCM 是其中一种 NetworkFilter ,HTTPFilter 组成了 HCM 中的 HTTPFilterChain
  4. RouterFilter 是其中一种 HTTPFilter,放在 HTTPFilterChain 的最后执行。

注意,我们在 4.1 中的配置文件中,只定义了一个监听器(listener_0) 和 一个空的过滤器链 filter_chains: [{}],但这是网络过滤器链(Network Filter Chain)的部分,而不是监听器过滤器。监听器过滤器需要在配置中使用 listener_filters 字段定义。

4.3 Router

HTTP 过滤器链中的最后一个过滤器必须是路由器过滤器(envoy.filters.HTTP.router)。路由器过滤器负责执行路由任务。这最终把我们带到了第二个构件 ------ 路由

域名匹配

我们在 HCM 过滤器route_config 字段下定义路由配置。在路由配置中,我们可以通过查看元数据(URI、Header 等)来匹配传入的请求,并在此基础上,定义流量的发送位置。

路由配置中的顶级元素是虚拟主机 virtual_hosts 。每个虚拟主机元素都有一个属于自己的name,在发布统计数据时使用(不用于路由),还有一组被路由到它的域名domain

让我们考虑下面的路由配置和域名的集合。

yaml 复制代码
 route_config:
   name: my_route_config
   virtual_hosts:
   - name: cnblogs_hosts
     domains: ["cnblogs.com"]
     routes:
     ...
   - name: test_hosts
     domains: ["test.cnblogs.com", "qa.cnblogs.com"]
     routes:
     ...

如果传入请求的目的地是 cnblogs.com(即HTTP请求中 Host/Authority 标头被设置为其中一个值),则该请求会走向cnblogs_hosts 虚拟主机中定义的路由。

同样,如果 Host/Authority 标头包含 test.cnblogs.comqa.cnblogs.com,则该请求会走向test_hosts 虚拟主机下的路由。这样我们就可以用一个监听器(0.0.0.0:10000)来处理多个顶级域名。

如果你在数组中指定多个域,搜索顺序如下:

  1. 精确的域名(例如:cnblogs.com)。
  2. 后缀域名通配符(如 *.cnblogs.com)
  3. 前缀域名通配符(例如:cnblogs.*)。
  4. 匹配任何域的特殊通配符(*)。

路由匹配

在 Envoy 匹配域名后,是时候处理所选虚拟主机中的 routes 字段了。我们用该字段,指定如何匹配一个请求,以及接下来如何处理该请求(例如,重定向、转发、重写、发送直接响应等)。

我们来看看一个例子。

yaml 复制代码
 static_resources:
   listeners:
   - name: listener_0
     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: hello_world_service
           http_filters:
           - name: envoy.filters.http.router
           	 typed_config:
             	"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
           route_config:
             name: my_first_route
             virtual_hosts:
             - name: direct_response_service
               domains: ["*"]
               routes:
               - match:
                   prefix: "/"
                 direct_response:
                   status: 200
                   body:
                     inline_string: "envoy yay"

配置的顶部部分与我们之前看到的一样。我们已经添加了 HCM 过滤器、统计前缀(hello_world_service)、单个 HTTP 过滤器(路由器)和路由配置。

我们把route_config的部分拿出来:

yaml 复制代码
route_config:
	name: my_first_route
    virtual_hosts:
    - name: direct_response_service
      domains: ["*"]
      routes:
      - match:
    		prefix: "/"
        direct_response:
    		status: 200
    		body:
    			inline_string: "envoy yay"

在虚拟主机内,我们要匹配任何域名(通配符 * 指定)。在 routes 下,我们匹配前缀(/),然后我们可以发送一个响应。

当涉及到匹配请求时,我们有多种选择。

路由匹配 描述 示例
prefix 前缀必须与:path 头的开头相符。 /hellohello.com/hellohello.com/helloworldhello.com/hello/v1 匹配。
path 路径必须与:path 头完全匹配。 /hello匹配 hello.com/hello,但不匹配 hello.com/helloworldhello.com/hello/v1
safe_regex 所提供的正则表达式必须与:path 头匹配。 /\{3} 匹配任何以 / 开头的三位数。例如,与 hello.com/123 匹配,但不能匹配 hello.com/hellohello.com/54321。
connect_matcher 匹配器只匹配 CONNECT 请求。

一旦 Envoy 将请求与路由相匹配,我们就可以对其进行路由、重定向或返回一个直接响应。在这个例子中,我们通过 direct_response 配置字段使用直接响应

在tetrate的教学过程中,他们使用了一个名为 func-e 的命令行工具。func-e 允许我们选择和使用不同的 Envoy 版本。

安装 func-e CLI 的方式见func-e官网。

当然,也可以直接下载envoy的bin文件来跑。

更加折腾的方式是自己下载envoy源码进行编译。但是编译的环境要求非常高(建议用编译环境镜像做这个事情),并且编译时间会非常非常非常长......

现在把上述配置保存到 envoy-direct-response.yaml 中后,我们可以用这个配置运行 Envoy。

sh 复制代码
func-e run -c envoy-direct-response.yaml

如果是自己拉下来的二进制 envoy,则:

sh 复制代码
envoy-1.27.0-linux-x86_64 -c conf.yaml

一旦 Envoy 启动,我们就可以向 localhost:10000 发送一个请求,以获得我们配置的直接响应。

复制代码
$ curl localhost:10000
envoy yay

同样,如果我们添加一个不同的主机头(例如 -H "Host: hello.com")将得到相同的响应,因为 hello.com 主机与虚拟主机中定义的域相匹配。

4.4 Cluster 和 Endpoint

在大多数情况下,从配置中直接发送响应是一个很好的功能,但我们会有一组端点或主机,我们将流量路由到这些端点或主机。在 Envoy 中做到这一点的方法是通过定义集群

集群(Cluster)是一组 接受流量的上游类似主机。这可以是你的服务所监听的主机或 IP 地址的列表(类似 k8s 中 Service 与 EndpointSlice 的关系)

例如,假设我们有一个 hello world 服务监听 127.0.0.0:8000 。然后,我们可以用一个单一的端点创建一个集群,像这样:

复制代码
static_resources:
  clusters:
    - name: hello_world_service
      connect_timeout: 0.25s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      hosts:
        - socket_address:
            address: 127.0.0.1
            port_value: 8000

集群的定义与监听器的定义在同一级别 ,使用 clusters 字段。我们在路由配置中引用集群时,以及在导出统计数据时,都会使用集群这个字段部分。该集群名称在所有集群中必须是唯一的。

我们还可以定义集群内部端点的负载均衡

复制代码
 clusters:
 - name: hello_world_service
   load_assignment:
     cluster_name: hello_world_service
     endpoints:
     - lb_endpoints:
       - endpoint:
           address:
             socket_address:
               address: 127.0.0.1
               port_value: 8000

load_assignment 字段下,我们可以定义要进行负载均衡的端点列表,以及负载均衡策略设置。每个 lb_endpoints 包含一个或多个端点,对于 endpoint 部分见 4.5 介绍。

Envoy 支持多种负载均衡算法(round-robin、Maglev、least-request、random),这些算法是通过一系列设定共同配置的,这些相关设定有:静态引导配置、DNS、动态 xDS(CDS 和 EDS 服务)以及 主动 / 被动健康检查。如果我们没有通过 lb_policy 字段明确地设置负载均衡算法,它默认为 round-robin。

(当然这里的envoy负载均衡只是一个了解,后面会有单独的笔记深入学习。)

我们可以先来了解一下 endpoint 部分负载均衡支持的一些特性。通常情况下,负载均衡器对所有端点一视同仁,但集群定义允许在端点内建立一个层次结构。

例如,端点可以有一个 权重(weight) 属性,这将指示负载均衡器与其他端点相比,向这些端点发送更多 / 更少的流量。

另一种层次结构类型是基于**地域(locality)**属性,通常用于定义故障转移架构,在容灾处理和平常的业务优化都很有用。这种层次结构允许我们定义地理上比较接近的 "首选" 端点,以及在 "首选" 端点变得不健康的情况下应该使用的 "备份" 端点。

我们还可以在 Cluster 中配置以下可选功能:

  • 主动健康检查(health_checks
  • 断路器 (circuit_breakers)
  • 异常点检测(outlier_detection
  • 在处理上游的 HTTP 请求时有额外的协议选项
  • 一组可选的网络过滤器,应用于所有出站连接等

和监听器的地址一样,端点地址可以是一个套接字地址,也可以是一个 Unix 域套接字。

4.5 示例结合 yaml

让我们看看这些配置是如何结合起来的。

复制代码
 static_resources:
   listeners:
   - name: listener_0
     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: hello_world_service
           http_filters:
           - name: envoy.filters.http.router
           route_config:
             name: my_first_route
             virtual_hosts:
             - name: direct_response_service
               domains: ["*"]
               routes:
               - match:
                   prefix: "/"
                 route:
                   cluster: hello_world_service
   clusters:
   - name: hello_world_service
     connect_timeout: 5s
     load_assignment:
       cluster_name: hello_world_service
       endpoints:
       - lb_endpoints:
         - endpoint:
             address:
               socket_address:
                 address: 127.0.0.1
                 port_value: 8000

与前面的yaml不同,我们添加了集群配置,没有使用 direct_response,而是使用 routes 字段并指定集群名称。

我们在本地跑一个 golang 的 hello-world 服务来验证这个配置,监听 8000 端口:

golang 复制代码
package main

import (
    "fmt"
    "net/http"
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

func main() {
    http.HandleFunc("/", helloWorld)
    fmt.Println("Starting server at port 8000")
    if err := http.ListenAndServe(":8000", nil); err != nil {
        fmt.Println(err)
    }
}

然后我们可以向 127.0.0.1:8000 发送一个请求,以检查我们是否得到 "Hello World" 的响应。

接下来,让我们把上述保存了的 Envoy 配置送到 Envoy 启动。

复制代码
envoy-1.27.0-linux-x86_64 -c envoy-cluster.yaml

当 Envoy 代理启动时,向 0.0.0.0:10000 发送一个请求,让 Envoy 代理请求到 hello world 端点。

sh 复制代码
$ curl -v 0.0.0.0:10000
*   Trying 0.0.0.0:10000...
* Connected to 0.0.0.0 (0.0.0.0) port 10000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 0.0.0.0:10000
> User-Agent: curl/8.12.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
< date: Tue, 01 Apr 2025 12:23:00 GMT
< content-length: 13
< content-type: text/plain; charset=utf-8
< x-envoy-upstream-service-time: 0
< server: envoy
< 
* Connection #0 to host 0.0.0.0 left intact
Hello, World!

从冗长的输出中,我们会注意到由 Envoy 代理设置的响应头 x-envoy-upstream-service-timeserver: envoy

5. 一些学习需要用到的工具

相关推荐
阿里云云原生8 小时前
Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现
云原生
阿里云云原生8 小时前
当实时消费遇到 SPL:让数据处理更高效、简单
云原生
阿里云云原生10 小时前
大模型 Token 的消耗可能是一笔糊涂账
云原生
企鹅侠客13 小时前
Prometheus operator怎么添加targets和告警规则
运维·云原生·kubernetes·prometheus·pod
KubeSphere 云原生15 小时前
云原生周刊:Kubernetes v1.33 要来了
云原生·容器·kubernetes
孔令飞16 小时前
关于 LLMOPS 的一些粗浅思考
人工智能·云原生·go
镜舟科技17 小时前
迈向云原生:理想汽车 OLAP 引擎变革之路
大数据·数据库·云原生
聚搜云—服务器分享17 小时前
阿里云国际站代理商:传统IOE架构向云原生迁移的关键挑战有哪些?
阿里云·云原生·架构