第15章 Envoy:xDS 动态配置与 filter chain,为何是云原生数据面

学习目标

  • 掌握 Envoy 的核心模型:Listener → Filter Chain → Route → Cluster → Endpoint
  • 理解 xDS 如何让配置"秒级热推、永不 reload"——这是 Envoy 区别于 Nginx 的根本
  • 搞懂 filter chain 责任链如何插入鉴权、限流、故障注入、Wasm
  • 明白为什么 Istio/Service Mesh 选择 Envoy 作数据面

前置知识


原理

定位:为动态配置而生的数据面

Envoy 由 Lyft 开源,生来就是为云原生设计的代理。它和 Nginx/HAProxy 最根本的不同:

Nginx/HAProxy 的配置是静态文件,改了要 reload;Envoy 的配置可以由控制面通过 API 动态下发,秒级热生效、无需重启。 这一条,决定了它成为 Service Mesh 数据面的标准。

在一个有成千上万微服务、实例不断扩缩容的环境里,"改配置要 reload"是不可接受的——这正是 Envoy 的主场。

核心模型:一个请求在 Envoy 里的旅程

                    Envoy 配置层级
  ┌──────────────────────────────────────────────────────┐
  │ Listener   监听地址:端口(如 0.0.0.0:15001)           │
  │   └─ Filter Chain  网络过滤器链                         │
  │        └─ HTTP Connection Manager(L7 入口)           │
  │             └─ HTTP Filters  http 过滤器链             │
  │                  router / ext_authz / ratelimit / ...  │
  │             └─ Route Config  虚拟主机 + 路由规则        │
  │                  match(域名/路径/头) → Cluster          │
  │ Cluster    后端服务(逻辑名)+ 负载均衡 + 健康检查      │
  │   └─ Endpoints  实际后端实例 IP:Port                    │
  └──────────────────────────────────────────────────────┘

  请求流:Listener 收包 → Filter Chain → HTTP filters 逐个处理
        → Route 匹配选 Cluster → 负载均衡选 Endpoint → 转发

记住这条链:Listener(在哪听)→ Route(怎么选服务)→ Cluster(哪个服务)→ Endpoint(哪个实例)。xDS 的每个 D 就对应其中一层。

filter chain:责任链式可扩展

Envoy 的能力来自过滤器链——请求像流水线一样穿过一串 filter,每个 filter 做一件事:

  • Network filters(L4):tcp_proxy(L4 代理)、http_connection_manager(L7 入口)
  • HTTP filters(L7):router(必备,最后一个)、ext_authz(外部鉴权)、ratelimit(限流)、fault(故障注入)、jwt_authn(JWT 校验)、lua、wasm(第30章)

要加能力,就插一个 filter——这种可组合性是 Envoy 灵活的来源。

xDS:动态配置的发现协议

xDS = "x Discovery Service",是一族让控制面动态下发配置的 gRPC/REST API:

缩写全称下发什么
LDSListener Discovery Service监听器
RDSRoute Discovery Service路由规则
CDSCluster Discovery Service后端集群
EDSEndpoint Discovery Service集群里的实例 IP(扩缩容主要靠它)
SDSSecret Discovery Service证书/密钥(mTLS 用)
ADSAggregated Discovery Service把上面聚合到一条流,保证下发顺序一致
  控制面(Istiod 等)          数据面(Envoy)
        │   gRPC 长连接(ADS)        │
        │ ──── CDS:有这些集群 ────▶│
        │ ──── EDS:集群X有这些IP ─▶│  实例扩容 → 控制面推新 EDS → Envoy 秒级生效
        │ ──── LDS/RDS:路由更新 ──▶│  无需 reload、不断连

EDS 的价值:Pod 扩缩容时,控制面只需推一条 EDS 更新,Envoy 立即知道新实例——这在 Nginx 里得改 upstream + reload。

静态配置 vs 动态 xDS

Envoy 既能用静态 YAML(学习/简单场景),也能全动态(生产/Mesh):

  • 静态:所有 Listener/Cluster 写死在 bootstrap 配置里(下面实验一)
  • 动态:bootstrap 里只配"去哪找控制面",其余全走 xDS(Istio 模式)

为什么 Service Mesh 选 Envoy

需求Envoy 的答案
配置秒级热推、海量实例xDS 动态下发,无 reload
gRPC/h2 原生一等公民(第07章)
可观测原生 metrics/access log/分布式追踪
可扩展filter chain + Wasm,不改二进制加能力
mTLSSDS 下发证书,自动双向 TLS(第05章)
平滑升级hot restart(新老进程交接,不丢连接)

这套组合拳让 Istio、Consul、AWS App Mesh、Gateway API 实现纷纷选它作数据面(第29章)。代价是配置复杂、上手陡——直接手写 Envoy YAML 远比 Nginx 繁琐,所以现实中多由控制面生成。


️ 实现 / 命令

实验一:最小静态配置(Listener → Route → Cluster)

# envoy.yaml —— 一个最小 L7 反向代理
static_resources:
  listeners:
  - name: main
    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: ingress
          route_config:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: service_backend }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: service_backend
    type: STRICT_DNS
    load_assignment:
      cluster_name: service_backend
      endpoints:
      - lb_endpoints:
        - endpoint: { address: { socket_address: { address: 10.0.0.11, port_value: 8080 }}}
envoy -c envoy.yaml &
curl -s http://localhost:10000/ -o /dev/null -w "%{http_code}\n"   # 200

实验二:用 admin 接口调试(Envoy 的可观测利器)

# Envoy 自带 admin 端口(配 admin: { address: ... port_value: 9901 })
curl -s localhost:9901/clusters     | head    # 各集群的后端、健康状态
curl -s localhost:9901/config_dump  | jq .     # 当前完整生效配置(含 xDS 下发的)
curl -s localhost:9901/stats | grep upstream_rq # 上游请求统计
curl -s localhost:9901/server_info             # 版本/状态

config_dump 在排查"xDS 到底下发了什么"时不可替代。

实验三:插一个故障注入 filter(混沌测试)

http_filters:
- name: envoy.filters.http.fault
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault
    delay:                                   # 给 50% 请求注入 5s 延迟
      percentage: { numerator: 50 }
      fixed_delay: 5s
- name: envoy.filters.http.router          # router 必须在最后
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

加一个 filter 就有了故障注入——这是 filter chain 可组合性的体现(也是 Istio 故障注入的底层)。


排错

现象根因解决
503 UH(no healthy upstream)Cluster 没有健康 endpoint查 /clusters、EDS 下发、健康检查
配置改了不生效改了静态文件但走的是 xDS看 config_dump 确认实际生效源
xDS 不同步控制面连接断、版本不一致查 Envoy↔控制面 gRPC 连接、/stats 的 xDS 指标
gRPC 调用失败没按 h2 配 ClusterCluster 加 http2_protocol_options(第07章)
filter 不执行filter 顺序错,router 不在最后router 必须是 HTTP filter 链最后一个
手写 YAML 太痛苦Envoy 配置本就繁琐生产用控制面(Istio)生成,别手写

本章小结

  • Envoy 的模型:Listener → Filter Chain → Route → Cluster → Endpoint,xDS 的每个 D 对应一层。
  • xDS(LDS/RDS/CDS/EDS/SDS/ADS) 让控制面动态热推配置,秒级生效、无 reload——这是它区别于 Nginx 的根本,也是 Mesh 选它的核心原因。
  • filter chain 责任链让能力可插拔(鉴权/限流/故障注入/Wasm)。
  • 强在动态、gRPC、可观测、mTLS、热重启;弱在配置复杂,故生产多由控制面生成。

下一章 第16章 Traefik / Caddy,看另一类现代代理如何走"自动化优先"路线:从容器标签自动发现路由、自动申请 HTTPS 证书,用便利换极致控制。