PAYATHON 2026

Consul 与 Envoy 如何集成并管理负载均衡、配置和 sidecar?

技术老齐

Consul 与 Envoy 集成后,通常由 Consul 维护服务发现、访问策略和流量配置,Envoy 接收配置,执行请求转发、负载均衡和过滤。多个 Envoy 可以同时提供服务,无须在它们之间选举 leader。

xDS 是配置发现 API 的统称,Envoy 是处理业务流量的数据平面组件。xDS 本身不属于数据平面,也不负责存储配置。Consul 的服务网格功能可以作为 Envoy 的控制平面,通过 xDS 下发配置。Envoy xDS 文档

以下以 Consul Service Mesh 集成为前提。如果独立 Envoy 只是查询 Consul DNS,Consul 就只参与服务发现,Envoy 的路由和 filter 仍需另行管理。

1. 多实例服务由谁执行负载均衡?

发起上游连接的 Envoy 负责选择目标实例。例如,MA 调用有三个实例的 MB,典型流量路径是:

MA → MA 的 Envoy → 某个 MB 实例的 Envoy → 对应的 MB 实例

Consul 根据服务注册、健康状态和流量策略生成配置,向 MA 的 Envoy 提供可用的 MB 目标。MA 的 Envoy 按配置的负载均衡策略选择实例。接收请求的 MB sidecar 通常只把入站流量转给它所代理的那个 MB 实例。

Consul 可以通过 service-resolver 等配置条目声明负载均衡策略,再由 Envoy 执行。具体能配置哪些策略,取决于 Consul 版本,因此不能笼统地说 Consul 无法配置复杂负载均衡。Service resolver 配置参考

单独使用 Consul DNS 时,Consul 可以随机打乱返回地址的顺序,但最终采用哪个地址、缓存多久、是否复用连接,都由客户端或代理决定,无法保证每次业务请求都严格轮询。Consul DNS 参数参考

负载均衡的粒度还取决于协议:TCP 通常按连接选择目标;HTTP 可以按请求选择目标,但已经建立的长连接或流式请求不会被拆分到多个实例。

2. Envoy 的配置存在哪里,如何分发?

Envoy 不需要配套部署一个“Envoy Server”数据库。它的配置通常分为两部分:

配置类型内容及来源
Bootstrap 配置启动时读取的本地文件,包含代理身份、xDS 连接地址及必要的静态资源
动态配置从控制平面接收的 listener、route、cluster、endpoint 等资源,供 Envoy 在运行时使用

Envoy 支持组合使用静态配置和动态配置。多个 Envoy 实例各自连接控制平面获取配置,不需要彼此复制配置文件。Envoy 配置示例

在 Consul 集成中,Consul 控制平面维护服务目录、配置条目和访问策略,Consul Server 通过 Raft 维护一致性。控制平面根据每个代理的身份和依赖关系生成所需的 xDS 资源,因此不同 Envoy 收到的配置不一定相同。

Envoy 连接控制平面的方式随部署方式而异:

  • 使用 Consul client agent 时,Envoy 通常连接本地 agent 暴露的 gRPC/xDS 接口。
  • 在采用 Consul Dataplane 的 Kubernetes 等部署中,consul-dataplane 管理 Envoy 进程,并将其 xDS 请求代理到 Consul Server,无须每个节点都运行 client agent。

部署方式的差异,也是不同年代的部署文档看起来不一致的原因之一。Consul Envoy 配置参考、Consul Dataplane 架构

3. 多个入口 Envoy 如何协作,是否需要 leader?

多个入口 Envoy 通常同时承接流量:

                    ┌→ EF-1 ─┐
客户端 → 入口负载均衡器          ├→ 后端服务
                    └→ EF-2 ─┘

每个 EF 独立接收配置、维护连接并选择上游。前面的负载均衡器将客户端连接分配给各个 EF,入口也可以继续使用已有的 HAProxy。

这里涉及的几个概念含义不同:

  • Envoy 配置中的 Cluster 是一组上游目标,例如 MB 的若干实例。
  • 多个入口 Envoy 副本组成一个代理部署,副本之间不会因此选举 leader。
  • Consul Server 集群需要通过 Raft 选举 leader,这是控制平面的内部机制。

各个 EF 的连接池和本地负载统计通常相互独立,即使配置相同,也不会因此协调出一个全局严格轮询序列。

Consul 的 sidecar 配置和入口网关配置用途也不同。普通 Envoy 连接到 Consul 后,不会自动获得面向整个服务目录的入口路由。入口应使用适合部署环境的网关集成,或由单独的配置系统管理。例如,Consul 的 Kubernetes API Gateway 使用 Gateway、HTTPRoute 等资源声明入口和路由。Gateway 配置参考

4. sidecar 注入具体是怎么回事?

sidecar 是一种部署关系,通常一个代理对应一个应用实例。Consul agent 可以服务于同一节点上的多个应用实例,不必与 sidecar 一一对应。

虚拟机或裸机:注册之后,还要启动代理

例如,将下面的配置保存为 mb.hcl:

service {
  name = "mb"
  id   = "mb-1"
  port = 8080

  meta = {
    version = "v2"
  }

  connect {
    sidecar_service {}
  }
}

注册服务,再启动对应的 Envoy:

consul services register mb.hcl
consul connect envoy -sidecar-for=mb-1

sidecar_service {} 会让 Consul 同时生成关联的代理服务注册。consul connect envoy 生成 Envoy 所需的 bootstrap 配置并启动进程。仅注册服务不会自动在机器上启动 Envoy。Sidecar 部署说明

该示例假设本地 Consul agent 已启用服务网格及所需的 gRPC 接口,Envoy 已安装,ACL、TLS 和访问权限也已配置。生产环境还需要为应用添加健康检查,并由 systemd 或编排系统管理代理进程。

Kubernetes:在创建 Pod 时修改 Pod 定义

Kubernetes 中,Consul connect injector 通过 mutating admission webhook,在 Pod 创建时加入代理及必要的初始化配置,实现自动注入。

启用 injector 的 Helm values 包括:

connectInject:
  enabled: true

对于按注解选择注入的部署,将注解放在 Pod 模板上:

spec:
  template:
    metadata:
      annotations:
        consul.hashicorp.com/connect-inject: "true"

已有 Pod 通常需要重新创建,才能经过注入流程。采用 Consul Dataplane 时,注入的代理容器通过 consul-dataplane 管理 Envoy。Consul 注入机制、Consul Dataplane 说明

完成注入后,还需要让流量经过代理。显式 upstream 模式要求应用连接本地代理监听端口;透明代理模式需要相应的流量重定向配置。

5. 如何按 URL 路径转发到指定实例?

路径匹配通常用 HTTP 路由规则即可。通过 Consul 管理时,可以用 service-router 匹配请求,再用 service-resolver 选择服务子集。

假设 MB 已有正常运行的 v1 和 v2 实例,需要按以下规则转发:

  • /special/ 开头的请求进入 v2;
  • 其他请求进入 v1。

先配置 mb-defaults.hcl,将 MB 声明为 HTTP 服务:

Kind     = "service-defaults"
Name     = "mb"
Protocol = "http"

启用 HTTP 路由前,需要声明 L7 协议。如果代理只能看到无法解密的应用 TLS 流量,就无法读取 URL 路径。Service defaults 配置参考

然后配置 mb-resolver.hcl,根据实例注册时的 metadata 划分子集:

Kind          = "service-resolver"
Name          = "mb"
DefaultSubset = "v1"

Subsets = {
  v1 = {
    Filter      = "Service.Meta.version == v1"
    OnlyPassing = true
  }

  v2 = {
    Filter      = "Service.Meta.version == v2"
    OnlyPassing = true
  }
}

这里的 Filter 是 Consul 的实例筛选表达式,与 Envoy HTTP filter 是不同概念。Service resolver 子集配置

最后配置 mb-router.hcl:

Kind = "service-router"
Name = "mb"

Routes = [
  {
    Match {
      HTTP {
        PathPrefix = "/special/"
      }
    }

    Destination {
      Service       = "mb"
      ServiceSubset = "v2"
    }
  }
]

如果要求路径任意位置包含一个完整的 special 路径段,可以将 PathPrefix 替换为 PathRegex = ".*/special(/.*)?",两者不要同时配置。前缀示例只匹配 /special/ 及其后续路径,不匹配单独的 /special。Service router 配置参考

按依赖顺序写入:

consul config write mb-defaults.hcl
consul config write mb-resolver.hcl
consul config write mb-router.hcl

这些规则用于调用方访问 mb 时的服务发现链。调用必须经过配置了 mb upstream 的网格代理;直接请求 MB 应用端口会绕过这套路由。如果由 EF 承接外部请求,还需要配置对应的入口路由。

指定子集后,请求会在该子集内继续执行负载均衡。只有子集中仅有一个符合条件的健康实例时,请求才会全部进入它。如果业务要求绑定某个实例,可以使用唯一 metadata 值筛选,同时明确该实例不可用时如何处理。没有配置 fallback,就不能期待请求自动退回其他子集。

如果需要运行 Lua、调用外部鉴权服务等自定义处理逻辑,应检查对应版本支持的 EnvoyExtensions。Consul 也提供部分底层配置覆盖方式,但可配置范围和适用代理类型需要逐项确认。Consul Envoy extensions

6. 如何访问未由 Consul 管理的机器和服务?

接入方式取决于目标是否在 Consul 中声明。远端机器可以不运行 Consul,但仍在 Consul 中注册服务;也可以完全不将目标地址纳入 Consul 配置。

远端不安装 agent,但允许在 Consul 中声明服务

可以将目标作为外部服务接入。常见方案是注册外部服务,并配置 terminating gateway:

MA → MA 的 Envoy → Terminating Gateway → 外部服务

远端机器可以不安装 agent 或 sidecar。网关终止来自网格的 mTLS 连接,再连接外部目标。网关到目标之间是否使用 TLS,需要单独配置,因此不能把这两段连接视为同一条端到端网格 mTLS 连接。

接入时通常需要声明目标、配置网关关联和访问权限,并安排健康检查。仅向 Catalog 写入一个地址,不会自动产生远端健康探测。外部服务与 terminating gateway 接入说明

目标地址完全不进入 Consul 配置

可以通过独立入口 Envoy 的静态配置,或另一套控制平面管理该目标。

如果必须复用 Consul 管理的 Envoy,可以使用 envoy_extra_static_clusters_json、envoy_extra_static_listeners_json 等 bootstrap 扩展选项。新增 cluster 只声明了上游,还需要 listener 和路由引用它,才能转发流量。Envoy bootstrap 扩展参考

使用这些扩展时,需要注意:

  • 静态资源名称和监听端口应避免与 Consul 生成的资源冲突。
  • Bootstrap 扩展属于启动配置,修改后需要重新生成配置并重启代理。
  • 自定义出站路径的 TLS、鉴权和健康检查需要自行配置,不能默认继承网格保护。
  • 不要让两个控制平面同时修改同一组动态资源,也不要通过直接修改 Envoy 中由 Consul 生成的配置来保存长期规则。

如果已有同名 Consul 配置条目,应先合并现有设置再写入。排查时,要同时检查配置是否写入成功、Envoy 是否接受 xDS 更新,以及请求是否经过目标代理。控制平面中已有规则,实际流量也可能尚未采用。

备注:内容仅供参考。