news 2026/1/12 1:48:45

很多人用 Envoy,却从没真正理解过 xDS(我也是,直到手搓了一遍)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
很多人用 Envoy,却从没真正理解过 xDS(我也是,直到手搓了一遍)

前言

上一篇内容,我们详细讨论了envoy做服务发现,并且详细讨论了静态配置与使用dns做服务发现,并且通过consul的详细配置阐述了dns做服务发现的工作原理,但是也遗留了一个问题,一旦想要修改endpoint的配置

clusters: - name: app_service connect_timeout: 1s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: app_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: "backend-service" port_value: 10000

比如我想改address: "backend-service",envoy并不会自动感知,还是需要重启

envoy xDS简介

xDS 不是一个单一的模块,而是一组与 Envoy 服务发现相关、解耦的 API 接口集合

  • CDS (Cluster Discovery Service): 集群发现。获取上游集群的定义,即 Envoy 可以将流量路由到的一组逻辑上相似的上游主机
  • EDS (Endpoint Discovery Service): 端点发现。这是最核心的服务发现模块。它为每个集群提供具体的、健康的网络端点(如 IP:Port)列表。Envoy 支持通过 EDS 进行增量更新,从而实现高效、实时的服务实例变更
  • LDS (Listener Discovery Service): 监听器发现。获取 Envoy 应该监听的网络地址、端口和过滤器链配置
  • RDS (Route Discovery Service): 路由发现。获取虚拟主机和路由规则配置,用于将流量定向到正确的集群
  • SDS (Secret Discovery Service): 密钥发现。安全地获取 TLS 证书和私钥
  • ADS (Aggregated Discovery Service): 聚合发现服务。一个特殊的 gRPC 端点,它将所有 xDS API 聚合到单个流中。这确保了配置更新的一致性和顺序性,避免配置不一致导致的流量中断

是不是看得脑袋嗡嗡的,没关系,我们从最核心的入手,那就是EDS

envoy EDS

所谓EDS服务:

  • 就是在envoy之外,有一个配置中心,之前直接配置在envoy的静态配置,搬迁到配置中心来,新增和维护新规则都在配置中心维护
  • 一旦配置有变更,配置中心会主动推送到envoy,让其及时变更流量转发配置
创建eds服务端

手搓一个最简单的eds_server用来演示:

eds服务

该脚本启动18000端口,接收gRPC请求,并且响应EDS,只要envoy来连接18000,就会下发endpoint到envoy

修改envoy配置

再修改一下envoy的配置:

... clusters: - name: backend_cluster type: EDS connect_timeout: 0.25s lb_policy: ROUND_ROBIN eds_cluster_config: eds_config: api_config_source: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: eds_server - name: eds_server connect_timeout: 1s type: STATIC http2_protocol_options: {} load_assignment: cluster_name: eds_server endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 10.22.12.178 port_value: 18000 ...
  • type: EDS说明了使用EDS作为服务发现,而EDS的相关信息在cluster_name: eds_server这里定义
  • eds_server是静态的配置,访问10.22.12.178:10000就能够获取获取eds配置
验证

配置完之后,首先启动eds_server

▶ go run eds.go 2025/12/23 18:15:36 EDS server listening on :18000

修改envoy配置之后重启,检查eds_server的输出:

▶ go run eds.go 2025/12/23 18:15:36 EDS server listening on :18000 2025/12/23 18:17:33 EDS stream connected 2025/12/23 18:17:33 >>> Sending EDS response version=1766484936064308385, nonce=1766485053421230413 2025/12/23 18:17:33 DiscoveryRequest resources=[backend_cluster] version="" nonce="" 2025/12/23 18:17:33 DiscoveryRequest resources=[backend_cluster] version="1766484936064308385" nonce="1766485053421230413"

成功了,启动的envoy之后,envoy与eds_server建立连接,并且eds_server推送相关配置给envoy,再访问一下试试curl 10.22.12.178:30785/test

[2025-12-23T10:20:44.892Z] "GET /test HTTP/1.0" 200 40 1 c40a5dd3-29b7-4d1b-b73d-e93b31b5f6e3 "curl/7.81.0" "-" 10.244.0.111:10000 backend_cluster - [2025-12-23T10:20:46.003Z] "GET /test HTTP/1.0" 200 40 1 1656452c-4571-469b-b2b7-3d43bd703c6d "curl/7.81.0" "-" 10.244.0.114:10000 backend_cluster -
EDS小结

手搓了一个能够响应eds的服务,并且将envoy指向该服务,envoy也能够获取后端endpoint的地址,成功转发的请求

演示中的脚本,是将配置写死在代码中的

s.endpoints = []*endpointpb.LbEndpoint{ newEndpoint("10.244.0.111", 10000), newEndpoint("10.244.0.114", 10000), }

只需要将这部分改造一下。如果在k8s里面,那就watch k8s的endpoint,动态获取就行。如果是在k8s集群之外,可以封装一个web 容器,在页面上管理后端endpoint也行。总之,后端服务的配置,完全由eds接管,不管ip:port怎 么变化,只需要在eds服务中配置,就会推送至envoy,完成endpoint服务发现

envoy RDS

现在已经拥有了EDS服务,能够动态获取endpoint,但是http的路由配置依然是直接在配置文件里面的

... static_resources: listeners: - name: ingress_listener 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_http http_protocol_options: accept_http_10: true common_http_protocol_options: idle_timeout: 300s codec_type: AUTO route_config: name: local_route virtual_hosts: - name: app domains: ["*"] routes: - match: { prefix: "/" } route: cluster: backend_cluster ...

比如想要修改match: { prefix: "/" },envoy并不会感知,还是需要重启。所以引入RDS服务,与EDS服务类似,自动发现HTTP路由配置

创建rds服务端

手搓一个简单的rds_server

rds服务

该脚本启动18001端口,接收gRPC请求,并且响应RDS,只要envoy来连接18001,就会下发http route到envoy

修改envoy配置
static_resources: listeners: - name: ingress_listener 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_http http_protocol_options: accept_http_10: true codec_type: AUTO rds: route_config_name: local_route config_source: api_config_source: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: rds_server ... clusters: ... - name: rds_server connect_timeout: 1s type: STATIC http2_protocol_options: {} load_assignment: cluster_name: rds_server endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 10.22.12.178 port_value: 18001
验证

配置完之后,首先启动rds_server

▶ go run rds.go 2025/12/24 17:02:34 RDS server listening on :18001

修改envoy配置之后重启,检查rds_server的输出:

▶ go run rds.go 2025/12/24 17:02:34 RDS server listening on :18001 2025/12/24 17:02:55 RDS stream connected 2025/12/24 17:02:55 >>> Sending RDS response version=1766566954686151045, nonce=1766566975846610006 2025/12/24 17:02:55 DiscoveryRequest resources=[local_route] version="1766561174225337826" nonce="" 2025/12/24 17:02:55 DiscoveryRequest resources=[local_route] version="1766566954686151045" nonce="1766566975846610006"

成功了,启动的envoy之后,envoy与rds_server建立连接,并且rds_server推送相关配置给envoy,再访问一下试试curl 10.22.12.178:30785/test

[2025-12-24T09:03:16.252Z] "GET /test HTTP/1.0" 200 40 1 bea0ccf1-0621-4be1-919f-3dbb24e93ff5 "curl/7.81.0" "-" 10.244.0.114:10000 backend_cluster - [2025-12-24T09:03:16.916Z] "GET /test HTTP/1.0" 200 40 1 f22c01e4-8120-4cb1-837e-a6c0b27f7410 "curl/7.81.0" "-" 10.244.0.111:10000 backend_cluster -
RDS小结

演示中的脚本,是将配置写死在代码中的

Match: &routepb.RouteMatch{ PathSpecifier: &routepb.RouteMatch_Prefix{ Prefix: "/test", }, },

只需要将这部分改造一下。如果在k8s里面,那就watch k8s的ingress,动态获取就行。如果是在k8s集群之外,可以封装一个web 容器,在页面上管理后端http router也行

envoy ADS

目前我们完成了EDS、RDS,可以自动发现对应的endpoint、http router资源,但是他们都是gRPC协议,能不能整合在一起呢?并且xDS还有其他的资源,什么CDS、LDS等等,每个种类都监听一次接口,管理难度也太冗余了。于是ADS就应运而生了,它是一个聚合发现服务,一个特殊的 gRPC 端点,将所有 xDS API 聚合在一起

创建ads服务端

ads服务

该脚本启动18000端口,接收gRPC请求,并且响应聚合请求ADS,再根据不同的查询类型(EDS、RDS等),响应不同的资源,并且下发到envoy

修改envoy的配置

这里修改较为复杂,直接给出配置文件即可

node: id: envoy-1 cluster: demo-proxy dynamic_resources: ads_config: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: ads_server static_resources: listeners: - name: ingress_listener 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_http http_protocol_options: accept_http_10: true codec_type: AUTO rds: route_config_name: local_route config_source: ads: {} http_filters: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router access_log: - name: envoy.access_loggers.stdout typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog log_format: text_format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %BYTES_SENT% %DURATION% %REQ(X-REQUEST-ID)% \"%REQ(USER-AGENT)%\" \"%REQ(X-FORWARDED-FOR)%\" %UPSTREAM_HOST% %UPSTREAM_CLUSTER% %RESPONSE_FLAGS%\n" clusters: - name: ads_server connect_timeout: 1s type: STATIC http2_protocol_options: {} load_assignment: cluster_name: ads_server endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 10.22.12.178 port_value: 18000 - name: backend_cluster type: EDS connect_timeout: 0.25s lb_policy: ROUND_ROBIN eds_cluster_config: eds_config: ads: {}
验证

首先启动ADS服务,再修改envoy配置,最后重启envoy服务。验证部分同EDS、ADS,这里就不赘述

ADS小结

至此,通过ADS聚合服务,可以接受不同类型的xDS请求,在文中我们实现了EDS与RDS,当然如果有需求,可以持续的把LDS、CDS等全部加上

小结

“修改配置之后如何自动生效”,本文通过这一切入点,详细探讨了envoy的另外一种服务发现策略xDS,并且手搓了诸如EDS、RDS等服务,成功响应了envoy的需求,完成了配置生效。并且最终使用ADS,将EDS与RDS聚合在一起,形成了一个统一且管理型强的服务入口

后记

有位老哥说了,这不就是istio嘛?没错,istio的数据面就是使用envoy

所谓服务治理,也是从解决最基本的问题而诞生的,本系列从“记录后端真实pod ip”为切入口,通过常见的场景需求,不断的解决需求,发现问题,解决问题,最终将这些功能全部聚合一起,就是服务治理的基本框架

而问题的提出、解决问题的过程以及需求的满足,不光是服务治理,也是所有软件诞生的基本思想

联系我

  • 联系我,做深入的交流


至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教…

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/29 16:04:57

COMSOL在载荷作用下的多层复合材料蠕变特性深度解析

comsol,载荷作用下,多层复合材料的蠕变分析。COMSOL是进行多物理场仿真分析的强大工具,尤其是在分析复杂材料行为时,它能帮助我们更好地理解材料在载荷作用下的响应。今天,我们就来聊一聊如何使用COMSOL对多层复合材料…

作者头像 李华
网站建设 2025/12/29 16:04:31

孤能子视角:“孤能子视角“的阶段性小结

(这里让知乎AI回答。同时澄清概念误解。)我的问题:1.孤能子视角。(注:看AI是否了解理论)2.它最深刻的部分是它的自我批判,没见过有理论是这样虐待自己的。(注:逼AI找问题)3.你说的那些我倒是理解的,理论说了,理论框架的概念与传统概念不一样(…

作者头像 李华
网站建设 2026/1/8 12:48:52

【2025年度创作总结】从笔记到实践,从思考到生活融合

2025 年度技术成长与创作盘点:从笔记到实践,从思考到生活融合 2025 年,对我来说是技术深耕与自我加速的一年。一年来,我围绕 算法体系、C 工程实战、Linux 系统编程、Java 企业级开发、辅助技术工具(如 Git、Protobuf&…

作者头像 李华
网站建设 2025/12/29 15:59:47

OCR+大模型!信息抽取效率狂飙,这对CP太顶了!

OCR 大模型!信息抽取效率狂飙,这对 CP 太顶了! 打工人快看!还在手动扒文档、熬夜做报表?OCR 和大模型这对 “智能 CP”,直接让信息抽取效率起飞! 以前,想从合同、发票里捞关键信息&…

作者头像 李华