第4章:Envoy 代理深入:核心概念与 xDS 协议
好,咱们今天来啃一块硬骨头——Envoy 的核心概念。说实话,很多人在刚接触服务网格时,都被 Envoy 那一堆术语搞懵了。Listener、Cluster、Endpoint,还有那个听起来很唬人的 xDS 协议。别急,我一个一个给你拆开讲。
4.1 三大核心概念:Listener、Cluster、Endpoint
我个人习惯把 Envoy 理解成一个「智能邮局」。你想想看,邮局要正常工作,需要什么?首先得有个收信窗口(Listener),然后要知道信该送到哪个片区(Cluster),最后得知道具体送到哪栋楼哪个房间(Endpoint)。
4.1.1 Listener(监听器)
Listener 就是 Envoy 的「耳朵」。它负责监听某个端口,等待流量进来。我在项目中遇到过一个问题:有个团队把 Listener 配成了监听 0.0.0.0:80,结果所有流量都涌进来了,根本没法做精细化控制。
核心要点:Listener 定义了 Envoy 如何接收流量。你可以配置多个 Listener,每个监听不同的端口或 IP。
# 一个简单的 Listener 配置示例
listeners:
- name: my_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8080
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
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: my_cluster
http_filters:
- name: envoy.filters.http.router
嗯,这里要注意:Listener 的配置里,filter_chains 是个关键。它决定了流量进来后怎么处理。说白了,就是给流量「过安检」。
4.1.2 Cluster(集群)
Cluster 是 Envoy 的「通讯录」。它告诉 Envoy:你要把流量发到哪一组后端服务去。每个 Cluster 可以包含多个后端实例,也就是 Endpoint。
我的经验:Cluster 的负载均衡策略一定要根据业务场景选。我见过有人所有服务都用 ROUND_ROBIN,结果某个服务响应时间差异很大,导致部分请求超时。后来改成 LEAST_REQUEST 就好多了。
clusters:
- name: my_cluster
connect_timeout: 0.25s
type: STRICT_DNS
dns_lookup_family: V4_ONLY
lb_policy: LEAST_REQUEST
load_assignment:
cluster_name: my_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 8080
- endpoint:
address:
socket_address:
address: 10.0.0.2
port_value: 8080
Cluster 的类型有几种:STATIC、STRICT_DNS、LOGICAL_DNS、EDS。我个人最常用的是 EDS(Endpoint Discovery Service),因为它能动态感知后端变化。你想想看,如果后端扩容了,EDS 能自动把新实例加进来,多省心。
4.1.3 Endpoint(端点)
Endpoint 就是具体的后端实例。它包含 IP 地址和端口。在 Kubernetes 环境下,Endpoint 通常对应一个 Pod 的 IP。
我曾经踩过的坑:有一次生产环境出了故障,排查了半天才发现是 Endpoint 的健康检查配置有问题。Envoy 默认的健康检查是主动探测,但如果后端服务响应慢,就会误判为不健康。后来我改成了被动健康检查(outlier detection),问题就解决了。
Endpoint 的配置其实很简单,但它的健康状态管理却很重要。Envoy 支持两种健康检查方式:
- 主动健康检查:Envoy 定期向后端发送探测请求
- 被动健康检查:根据实际请求的成功率来判断
我建议你把两者结合起来用。主动检查保证及时发现问题,被动检查防止误杀。
4.2 xDS 协议:Envoy 的「神经中枢」
好,核心概念讲完了。现在咱们聊聊 xDS。这个 xDS 到底是什么?说白了,它就是 Envoy 和控制面之间的通信协议。x 代表多种发现服务,比如 LDS(Listener Discovery Service)、CDS(Cluster Discovery Service)、EDS(Endpoint Discovery Service)等等。
为什么会需要 xDS?你想想看,如果每个 Envoy 实例都要手动配置,那几百个实例得配到猴年马月去。xDS 让控制面可以动态下发配置,Envoy 只需要「听话」就行。
4.2.1 xDS 的家族成员
| 缩写 | 全称 | 作用 |
|---|---|---|
| LDS | Listener Discovery Service | 动态下发 Listener 配置 |
| CDS | Cluster Discovery Service | 动态下发 Cluster 配置 |
| EDS | Endpoint Discovery Service | 动态下发 Endpoint 列表 |
| RDS | Route Discovery Service | 动态下发路由规则 |
| SDS | Secret Discovery Service | 动态下发证书和密钥 |
我记得刚开始用 Istio 时,看到这些缩写头都大了。后来发现,其实它们的工作流程很简单:控制面通过 gRPC 流把配置推送给 Envoy,Envoy 收到后立即生效。
4.2.2 xDS 的工作流程
咱们用个例子来说明。假设你更新了 Istio 的 VirtualService,想改变流量路由。整个过程是这样的:
- 控制面(比如 Istiod)检测到配置变化
- 它通过 RDS 把新的路由规则推送给 Envoy
- Envoy 收到后,更新本地路由表
- 新的请求就按照新规则走了
整个过程是实时的,不需要重启 Envoy。这就是 xDS 的威力所在。
关键点:xDS 使用 gRPC 双向流进行通信。这意味着控制面可以主动推送,Envoy 也可以主动请求。这种设计保证了配置的实时性和一致性。
4.2.3 实际项目中的 xDS 配置
我在一个项目中遇到过这样的场景:需要动态调整某个服务的流量比例。当时用的是 Istio 的 VirtualService,底层其实就是通过 RDS 实现的。
# 一个简单的 VirtualService 示例
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-service
spec:
hosts:
- my-service
http:
- match:
- headers:
version:
exact: v2
route:
- destination:
host: my-service
subset: v2
- route:
- destination:
host: my-service
subset: v1
这个配置下发后,Envoy 会通过 RDS 获取到新的路由规则。带有 version: v2 头的请求会走 v2 版本,其他请求走 v1 版本。
我的建议:在生产环境中,一定要给 xDS 配置超时和重试机制。我曾经遇到过控制面短暂不可用,导致 Envoy 拿不到配置的情况。后来加了重试和缓存,问题就解决了。
4.3 总结与避坑指南
好,咱们来捋一捋今天的内容。Envoy 的三大核心概念——Listener、Cluster、Endpoint,说白了就是「收流量、找目标、发请求」。xDS 协议则是让这一切变得动态和自动化。
最后,分享几个我踩过的坑:
- Listener 端口冲突:多个 Listener 不要监听同一个端口,否则会报错
- Cluster 类型选错:如果后端 IP 会变,别用 STATIC,用 EDS 或 STRICT_DNS
- 健康检查配置不当:主动检查和被动检查要配合使用,别只依赖一种
- xDS 超时设置:控制面和 Envoy 之间的 gRPC 连接要设置合理的超时
嗯,今天就到这儿。下一章咱们会深入聊聊 Envoy 的过滤器链,那才是真正好玩的东西。