第15章 Envoy:xDS 动态配置与 filter chain,为何是云原生数据面
学习目标
- 掌握 Envoy 的核心模型:Listener → Filter Chain → Route → Cluster → Endpoint
- 理解 xDS 如何让配置"秒级热推、永不 reload"——这是 Envoy 区别于 Nginx 的根本
- 搞懂 filter chain 责任链如何插入鉴权、限流、故障注入、Wasm
- 明白为什么 Istio/Service Mesh 选择 Envoy 作数据面
前置知识
- 第07章 HTTP2/gRPC 代理、第10章 L7、第11章 Sidecar 劫持
- 第13章 Nginx(对照:静态 vs 动态配置)、apiGateway · 服务网格
原理
定位:为动态配置而生的数据面
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:
| 缩写 | 全称 | 下发什么 |
|---|---|---|
| LDS | Listener Discovery Service | 监听器 |
| RDS | Route Discovery Service | 路由规则 |
| CDS | Cluster Discovery Service | 后端集群 |
| EDS | Endpoint Discovery Service | 集群里的实例 IP(扩缩容主要靠它) |
| SDS | Secret Discovery Service | 证书/密钥(mTLS 用) |
| ADS | Aggregated 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,不改二进制加能力 |
| mTLS | SDS 下发证书,自动双向 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 配 Cluster | Cluster 加 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 证书,用便利换极致控制。