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; AListeners --> BRoutes B --> CClusters C --> DEndpoints

但这个构建模块太过抽象,不太好理解,我将更加具体的三层四区抽象放在下面:
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. 一些学习需要用到的工具

相关推荐
张忠琳4 小时前
【client-go v0.36.1】(store Part 3)Store 超深度分析 — 集成模式、完整数据流、不变量、与 DeltaFIFO 协作
云原生·kubernetes·informer·store·client-go
赵渝强老师7 小时前
【赵渝强老师】Kubernetes(K8s)中的金丝雀升级
linux·docker·云原生·容器·kubernetes
鹤落晴春7 小时前
【K8s】配置存储卷
云原生·容器·kubernetes
张忠琳9 小时前
【client-go v0.36.1】(DeltaFIFO Part 1)DeltaFIFO 超深度分析 — 模块定位、类结构、接口层次、构造与初始化
云原生·kubernetes·deltafifo·informer·client-go
阿里云云原生10 小时前
实战揭秘:如何通过 AI Agent Skill 让 K8s 应用自动接入云监控?
云原生
张忠琳12 小时前
【client-go v0.36.1】tools/cache 深度分析(下篇)— RealFIFO 深度、集成架构、生命周期、设计模式总结
云原生·kubernetes·cache·informer·client-go
张忠琳13 小时前
【client-go v0.36.1】(store Part 2)Store 超深度分析 — threadSafeMap 核心、索引体系、RV追踪、事务机制
云原生·kubernetes·informer·store·client-go
sbjdhjd14 小时前
04(上)| k8s中的微服务
微服务·云原生·kubernetes·开源·云计算·excel·kubelet
这个DBA有点耶16 小时前
时序数据库深度对比:2026 年主流 TSDB 架构演进与选型指南
数据库·sql·云原生·架构·运维开发·时序数据库
小哈里17 小时前
【K8S】OCI标准下的企业级镜像治理:Harbor+Skopeo+Trivy 最佳实践
云原生·容器·kubernetes·harbor·镜像·skopen