bigywater.tech
微服务 · 84

Envoy 的 xDS:LDS、RDS、CDS、EDS

Envoy 是怎么从控制平面拿配置的?LDS、RDS、CDS、EDS 各是什么、什么顺序?ACK 和 NACK 是什么?更新推到流量切换要多久?控制平面挂了数据平面会怎样?这篇用自己写的最小 ADS 服务和真实的 Envoy 逐项观察。

CDS → EDS → LDS → RDS
Envoy 启动后各类资源第一次被请求的顺序
1.4 ms
控制平面推送 EDS 更新到客户端第一次收到新端点响应的中位数(RDS 更新是 0.7 ms)
102 / 102
控制平面停了 6 s 期间通过 Envoy 的请求成功数

请求顺序与 ACK

实测 · 本机执行输出
  t(s) | 方向  | 类型  | version_info | 资源名 | nonce   
  -----+-----+-----+--------------+-----+---------
  0.06 | 流打开 |     |              |     |         
  0.07 | 请求  | CDS | (空)          | (空) | (空)     
  0.07 | 发出  | CDS | 1            | c_a | 2e481018
  0.07 | 请求  | EDS | (空)          | c_a | (空)     
  0.07 | 发出  | EDS | 1            | c_a | b405840b
  0.07 | 请求  | CDS | 1            | (空) | 2e481018
  0.07 | 请求  | LDS | (空)          | (空) | (空)     
  0.07 | 发出  | LDS | 1            | l0  | 98c4f889
  0.07 | 请求  | EDS | 1            | c_a | b405840b
  0.07 | 请求  | RDS | (空)          | r0  | (空)     
  0.07 | 发出  | RDS | 1            | r0  | 7f7e155d
  0.07 | 请求  | LDS | 1            | (空) | 98c4f889
  0.07 | 请求  | RDS | 1            | r0  | 7f7e155d
  Envoy 起来到第一个请求能成功用了 0.08 s。表里各资源类型第一次被请求的先后是 CDS、EDS、LDS、RDS:最先是 CDS(不带资源名,订阅全部集群);收到 CDS 之后请求其中集群的 EDS(带上集群名 c_a);再请求 LDS(订阅全部监听器);收到 LDS 之后请求监听器引用的 RDS(带上路由配置名 r0)。先集群、后监听器、最后路由,避免监听器已经开始接流量而它引用的集群还没准备好(这个顺序的理由是我对 xDS 文档的了解,表里的先后是实测)。每个资源类型收到之后 Envoy 回一条带着刚收到的 version_info 和 nonce 的请求,就是 ACK

Envoy 按 CDS、EDS、LDS、RDS 的顺序订阅,每收到一类资源回一条带 version_info 和 nonce 的请求作为 ACK。

NACK

实测 · 本机执行输出
  t(s) | 方向   | version_info | 内容 / error_detail                                                                                                                                                                   
  -----+------+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  0.10 | 发出   | 2            | l0                                                                                                                                                                                  
  0.10 | NACK | 1            | Error adding/updating listener(s) l0: Error: terminal filter named envoy.filters.http.nonexistent of type envoy.filters.http.router must be the last filter in a http filter chain.

  1.61 | 发出   | 3            | l0                                                                                                                                                                                  
  1.61 | 请求   | 3            | (空)                                                                                                                                                                                 
  推送坏配置期间(NACK 之后)通过 Envoy 的 10 个请求:Counter({'A': 10});推送有效更新之后的 5 个请求:Counter({'A': 5})。坏配置被 NACK(error_detail 里写着原因),version_info 仍然是上一个被接受的版本,Envoy 继续用旧监听器,流量不受影响;控制平面推了有效版本之后 Envoy 回 ACK(version_info 变成新版本)

不接受的配置被 NACK,version_info 保持上一个被接受的版本,数据平面继续用旧配置。

推送延迟

实测 · 本机执行输出
  更新类型           | 到流量切换 中位数(ms) | 最小(ms) | 最大(ms)
  ---------------+---------------+--------+-------
  (a)EDS 更新      | 1.4           | 1.0    | 9.9   
  (b)RDS 更新(换路由) | 0.7           | 0.6    | 4.7   
  (c)新集群没有端点:路由改到 c_c 之后的前 1 s(EDS 还没推)里的请求结果 {'HTTP 503': 67};EDS 在第 1007 ms 推出,第一次收到 B 的响应在 1014 ms。路由指向了一个还没有端点的集群,在端点到位之前请求得不到成功的响应

同一台机器上的推送到切换是毫秒级;路由指向还没有端点的新集群会出现 503 窗口。

控制平面断开

实测 · 本机执行输出
  控制平面停了 6 s 期间的 102 个请求:Counter({'B': 102})(流量不受控制平面断开的影响,Envoy 继续用最后一份配置);控制平面重启之后 Envoy 重新建立 ADS 流用了 11.6 s
  重新连上之后 Envoy 重新发来的请求:[('CDS', '3', '(空)'), ('EDS', '33', 'c_c,c_b,c_a'), ('LDS', '3', '(空)'), ('RDS', '32', 'r0'), ('CDS', '3', '(空)'), ('EDS', '33', 'c_c,c_b,c_a'), ('LDS', '3', '(空)'), ('RDS', '32', 'r0')];重连后推送更新(c_a 的端点换成 B)之后的 5 个请求:Counter({'B': 5})
  数据平面与控制平面解耦:控制平面不可用时已经下发的配置照常工作,只是无法更新;Envoy 在控制平面恢复之后 11.6 s 重新连上(更长时间的断开下重连间隔会不会变长,我没有测),重连后重新订阅并拿到当前版本
ERRORS {}

已下发的配置照常工作,只是不能更新;控制平面恢复后 Envoy 重新连上并重新订阅。

分步演示

Envoy 的 xDS
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Envoy 1.39.3,控制平面是我用 Python(grpcio、xds-protos,文件 xdsl.py,约 70 行)写的 State-of-the-World 的 ADS,只实现了 StreamAggregatedResources,没有 Delta xDS、没有 ECDS / SDS / VHDS;资源是用 json_format 从与 Envoy YAML 同构的字典转成 protobuf 的;全部在同一台机器上,所以毫秒级的推送延迟不代表真实网络;控制平面重连时间有很大波动(1.3 s 和 11.6 s),只观察了两次
    • 没有验证:Delta xDS(增量);大量资源时的推送开销和版本管理;Istio / Consul / Envoy Gateway 等真实控制平面的行为;ADS 之外各个资源类型单独一条流的情形与顺序问题(make-before-break);控制平面多副本的一致性;SDS(证书下发)见 mTLS 一篇
    京ICP备2026067502号-1京公网安备11010502063487号