在 Istio 架构中,Pilot 属于最核心的组件,Pilot 的一个主要职责就是将服务信息和配置数据转换为 xDS 接口的标准数据结构,通过 Grpc stream 下发到数据平面的 Envoy。Polit 的输入来源主要有两个:
- 服务数据:来源于各个服务注册表(Service Registry),如 Kubernetes 和 Consul。
- 配置规则:各种配置规则,包括路由规则和流量管理等规则。
Istio Pilot 代码分为 pilot-agent 和 pilot-discovery,其中 pilot-agent 主要在数据平面负责管理 Envoy 的生命周期,pilot-discovery 是控制平面组件,本节主要分析 pilot-discovery。
Pilot-Discovery 代码结构
下图是 pilot-discovery 的主要代码结构,Pilot-Discovery 的入口函数为 pilot/cmd/pilot-discovery/main.go 中的 main 方法。main 方法中创建了 Discovery Server,Discovery Server 主要包括三个部分:
- Config Controller
- Service Controller
- Xds Server
Config Controller
Config Controller用于管理各种配置数据,包括用户创建的流量管理规则和策略。Config Controller 包括:
- Kubernetes:对接 Kubernetes CRD,从 kube-apiserver 中 watch VirtualService、DestinationRules 等配置信息,有则推送至 XdsServer。
- MCP(Mesh Configuration Protocol):使用 Kubernetes 来存储数据会导致 Istio 和 Kubernetes 的耦合,限制 Istio 在非 Kubernetes 环境下的使用。所以,Istio 提出了 MCP,MCP 定义了一个向 istio 控制平面下发配置数据的标准协议,istio pilot 作为 MCP Client,任何实现了 MCP 协议的 Server 都可以通过 MCP 协议向 istio 下发配置,从而解除了 istio 和 kubernetes 的耦合。
- Memory:内存实现,主要用于测试。
istio 中的配置包括:VirtualService、DestinationRule、Gateway、ServiceEntry 等。
Service Controller
Service Controller 用于管理各种注册中心,包括:
- Kubernetes:对接 Kubernetes,可以将 Kubernetes 定义的服务采集到 istio 中。
- Consul:使用 Consul 作为注册中心。
- ServiceEntryStore:描述的是集群外部的服务信息(ServiceEntry),将集群外部的服务信息同步到 istio 中,纳入网格统一进行流量控制和路由。
- MCP:与具体的注册中心解耦,从 MCP Server 中获取服务注册信息。
- Mock:用于测试。
Discovery Service
Discovery Service 中主要包含下述逻辑:
- 启动 Grpc Server 并接收来自 Envoy 端的连接请求。
- 接收 Envoy 端的 xDS 请求,从 Config Controller 和 Service Controller 中获取配置和服务信息,生成响应消息发送给 Envoy。
- 监听来自 Config Controller 的配置变化消息和来自 Service Controller 的服务变化消息,并将配置和服务变化内容通过 xDS 接口推送到 Envoy。
Pilot-Discovery 业务流程
pilot-discovery主要包括以下业务流程:
初始化组件
在 main 方法中,创建了 Pilot Server,Server 做了一些初始化的工作:
创建并初始化 Config Controller。
创建并初始化 Service Controller。
创建并初始化 Discovery Server,pilot 创建了 Envoy v3 版本的 API 的 Grpc Discovery Server。
将 Discovery Server 注册为 Config Controller 和 Service Controller 的 Event Handler,监听配置和服务变化消息。
创建Grpc Server 并接收 Envoy 连接请求
pilot 创建了一个 Grpc Server,用于监听和接受来自 Envoy 的 xDS 请求。ploit/pkg/xds/ads.go 中的 DiscoveryServer.StreamAggregatedResources 方法被注册为 Grpc Server 的服务处理方法。
当 Grpc Server 收到 Envoy 的连接时:
- 会调用 DiscoveryServer.StreamAggregatedResources 方法,在该方法中创建一个 xds.Connection 对象,并开启一个 goroutine 从该连接中接收客户端的 xDS 请求并处理。
- 如果控制面的配置/服务注册信息发生变化,pilot 也会通过该 connection 把配置变化主动推送到 Envoy 端。
配置变化后向 Envoy 推送更新
这是 pilot 中最为复杂的部分,因为采用了多个 channel 和 queue 对变化消息进行合并和转发。流程如下:
- Config Controller 和 Service Controller 在配置或者服务发生变化时通过回调方法通知 Discovery Server,Discovery Server 将变化的消息推至 Push Channel 中。
- Discovery Server 通过一个 goroutine 从 Push Channel 中接收变化消息,将一段时间内连续发生的变化消息进行合并(debounce)。如果超过指定时间没有新的变化消息,则将合并后的消息加入到一个队列 Push Queue 中。
- 另一个 goroutine 从 Push Queue 中取出变化消息,生成 XdsEvent,发送到每个客户端连接的 Push Channel 中。
- (每一个 Grpc 客户端连接)在 DiscoveryServer.StreamAggregatedResources 方法中从 Push Channel 中取出 XdsEvent,然后根据上下文生成符合 xDS 接口规范的 DiscoveryResponse,通过 GRPC 推送给 Envoy 。
响应 Envoy 主动发起的 xDS 请求
pilot 和 envoy 之间建立的是双向 stream grpc 连接,所以 pilot 可以在配置变化时向 envoy 推送,envoy 也可以主动发起 xDS 请求获取配置信息。Envoy 主动发起 xDS 请求的流程如下:
- Envoy 通过建立好的 Rrpc 连接发送一个 DiscoveryRequest。
- Discovery Server 通过一个 goroutine 从 xds.Connection 中接收来自 Envoy 的 DiscoveryRequest,并将请求发送到 ReqChannel 中。
- Discovery Server 通过另一个 goroutine 从 ReqChannel 中接收 DiscoveryRequest,根据上下文生成符合 xDS 规范的 DiscoveryResponse,返回给 envoy。