Pilot 作为 Istio 控制面流量管理的核心组件,为整个服务网格提供了标准的独立与各种平台的服务模型,支持从 Kubernetes、Consul 等多种平台获取服务发现功能,支持用户通过 VirtualServiceGateway等 API 定制服务间的流量管理规则,并将这些配置信息转化为统一的服务发现和流量控制模型,以 xDS 方式下发到数据面。

在组件拓扑中, Pod istio-pilot包括 istio-proxy(sidecar)和 discovery2 个容器, pilot 核心能力由容器 discovery中执行的命令 pilot-discovery discovery提供.

图中实线连线表示控制流,虚线连线表示数据流。带 [pilot] 的组件表示为 Pilot组件,图中关键的组件如下:

  • Discovery service:即 pilot-discovery,主要功能是从 Service provider(如 kubernetes 或者 consul )中获取服务信息,从 Kubernetes API Server 中获取流量规则(Kubernetes CRDResource),并将服务信息和流量规则转化为数据面可以理解的格式,通过标准的数据面 API 下发到网格中的各个 sidecar中。
  • agent:即 pilot-agent 组件,该进程根据 Kubernetes API Server 中的配置信息生成 Envoy的配置文件,负责启动、监控 sidecar进程。
  • proxy:既 sidecarproxy,是所有服务的流量代理,直接连接 pilot-discovery ,间接地从 Kubernetes 等服务注册中心获取集群中微服务的注册情况。
  • service A/B:使用了 istio的应用,如 Service A/B,的进出网络流量会被 proxy 接管。

下面介绍下 Pilot相关的组件 pilot-agent、pilot-discovery 的关键实现。

pilot-agent

pilot-agent 负责的主要工作如下:

  • 生成 sidecar的配置
  • sidecar 的启动与监控

pilot-discovery

pilot-discovery 扮演服务注册中心、istio控制平面到 sidecar之间的桥梁作用,负责将服务信息和配置数据转换为 xDS 接口的标准数据结构,通过 gRPC 下发到数据面的 Envoy。

目前 Pilot 的输入包括两部分数据来源:

  • 服务数据: 来源于各个服务注册表(Service Registry),例如 Kubernetes 中注册的 Service,Consul Catalog 中的服务等。Pilot 监控服务注册中心(如 Kubernetes)的服务注册情况。在 Kubernetes 环境下,会监控 serviceendpointpodnode 等资源信息。
  • 配置规则: 各种配置规则,包括路由规则及流量管理规则等,通过 Kubernetes CRD(Custom Resources Definition)形式定义并存储在 Kubernetes 中。Pilot 会监控 istio控制面信息变化,在 Kubernetes 环境下,会监控包括 RouteRuleVirtualServiceGatewayEgressRuleServiceEntry 等以 Kubernetes CRD形式存在的 istio控制面配置信息。

Pilot 的输出为符合 xDS 接口的数据面配置数据,并通过 gRPC Streaming 接口将配置数据推送到数据面的 Envoy 中。

代码、配置、架构一体化视角 深入解读 Service Mesh 背后的技术细节

从协议视角看 pilot-discovery

Config Controller

Config Controller 用于管理各种配置数据,包括用户创建的流量管理规则和策略。Istio 目前支持三种类型的 Config Controller:

  • Kubernetes:使用 Kubernetes 来作为配置数据的存储,该方式直接依附于 Kubernetes 强大的 CRD 机制来存储配置数据,简单方便,是 Istio 最开始使用的配置存储方案。
  • MCP (Mesh Configuration Protocol):使用 Kubernetes 来存储配置数据导致了 Istio 和 Kubernetes 的耦合,限制了 Istio 在非 Kubernetes 环境下的运用。为了解决该耦合,Istio 社区提出了 MCP,MCP 定义了一个向 Istio 控制面下发配置数据的标准协议,Istio Pilot 作为 MCP Client,任何实现了 MCP 协议的 Server 都可以通过 MCP 协议向 Pilot 下发配置,从而解除了 Istio 和 Kubernetes 的耦合。如果想要了解更多关于 MCP 的内容,请参考文后的链接。
  • Memory:一个在内存中的 Config Controller 实现,主要用于测试。

目前 Istio 的配置包括:

  • Virtual Service: 定义流量路由规则。
  • Destination Rule: 定义和一个服务或者 subset 相关的流量处理规则,包括负载均衡策略,连接池大小,断路器设置,subset 定义等等。
  • Gateway: 定义入口网关上对外暴露的服务。
  • Service Entry: 通过定义一个 Service Entry 可以将一个外部服务手动添加到服务网格中。
  • Envoy Filter: 通过 Pilot 在 Envoy 的配置中添加一个自定义的 Filter。

Service Controller

Service Controller 用于管理各种 Service Registry,提出服务发现数据,目前 Istio 支持的 Service Registry 包括:

  • Kubernetes:对接 Kubernetes Registry,可以将 Kubernetes 中定义的 Service 和 Instance 采集到 Istio 中。
  • Consul: 对接 Consul Catalog,将 Consul 中定义的 Service 采集到 Istio 中。
  • MCP: 和 MCP config controller 类似,从 MCP Server 中获取 Service 和 Service Instance。
  • Memory: 一个内存中的 Service Controller 实现,主要用于测试。

Discovery Service

Discovery Service 中主要包含下述逻辑:

  • 启动 gRPC Server 并接收来自 Envoy 端的连接请求。
  • 接收 Envoy 端的 xDS 请求,从 Config Controller 和 Service Controller 中获取配置和服务信息,生成响应消息发送给 Envoy。
  • 监听来自 Config Controller 的配置变化消息和来自 Service Controller 的服务变化消息,并将配置和服务变化内容通过 xDS 接口推送到 Envoy。(备注:目前 Pilot 未实现增量变化推送,每次变化推送的是全量配置,在网格中服务较多的情况下可能会有性能问题)。

pilot-discovery

流程分析

Pilot-Disocvery 包括以下主要的几个业务流程:

初始化 Pilot-Discovery 的各个主要组件

Pilot-Discovery 命令的入口为 pilot/cmd/pilot-discovery/main.go 中的 main 方法,在该方法中创建 Pilot Server,Server 代码位于文件 pilot/pkg/bootstrap/server.go 中。Server 主要做了下面一些初始化工作:

  • 创建并初始化 Config Controller。
  • 创建并初始化 Service Controller。
  • 创建并初始化 Discovery Server,Pilot 中创建了基于 Envoy V1 API 的 HTTP Discovery Server 和基于 Envoy V2 API 的 GPRC Discovery Server。由于 V1 已经被废弃,本文将主要分析 V2 API 的 gRPC Discovery Server。
  • 将 Discovery Server 注册为 Config Controller 和 Service Controller 的 Event Handler,监听配置和服务变化消息。

创建 gRPC Server 并接收 Envoy 的连接请求

Pilot Server 创建了一个 gRPC Server,用于监听和接收来自 Envoy 的 xDS 请求。pilot/pkg/proxy/envoy/v2/ads.go 中的 DiscoveryServer.StreamAggregatedResources 方法被注册为 gRPC Server 的服务处理方法。

当 gRPC Server 收到来自 Envoy 的连接时,会调用 DiscoveryServer.StreamAggregatedResources 方法,在该方法中创建一个 XdsConnection 对象,并开启一个 goroutine 从该 connection 中接收客户端的 xDS 请求并进行处理;如果控制面的配置发生变化,Pilot 也会通过该 connection 把配置变化主动推送到 Envoy 端。

配置变化后向 Envoy 推送更新

这是 Pilot 中最复杂的一个业务流程,主要是因为代码中采用了多个 channel 和 queue 对变化消息进行合并和转发。该业务流程如下:

  1. Config Controller 或者 Service Controller 在配置或服务发生变化时通过回调方法通知 Discovery Server,Discovery Server 将变化消息放入到 Push Channel 中。
  2. Discovery Server 通过一个 goroutine 从 Push Channel 中接收变化消息,将一段时间内连续发生的变化消息进行合并。如果超过指定时间没有新的变化消息,则将合并后的消息加入到一个队列 Push Queue 中。
  3. 另一个 goroutine 从 Push Queue 中取出变化消息,生成 XdsEvent,发送到每个客户端连接的 Push Channel 中。
  4. 在 DiscoveryServer.StreamAggregatedResources 方法中从 Push Channel 中取出 XdsEvent,然后根据上下文生成符合 xDS 接口规范的 DiscoveryResponse,通过 gRPC 推送给 Envoy 端。(gRPC 会为每个 client 连接单独分配一个 goroutine 来进行处理,因此不同客户端连接的 StreamAggregatedResources 处理方法是在不同 goroutine 中处理的)

响应 Envoy 主动发起的 xDS 请求

Pilot 和 Envoy 之间建立的是一个双向的 Streaming gRPC 服务调用,因此 Pilot 可以在配置变化时向 Envoy 推送,Envoy 也可以主动发起 xDS 调用请求获取配置。Envoy 主动发起 xDS 请求的流程如下:

  1. Envoy 通过创建好的 gRPC 连接发送一个 DiscoveryRequest
  2. Discovery Server 通过一个 goroutine 从 XdsConnection 中接收来自 Envoy 的 DiscoveryRequest,并将请求发送到 ReqChannel 中
  3. Discovery Server 的另一个 goroutine 从 ReqChannel 中接收 DiscoveryRequest,根据上下文生成符合 xDS 接口规范的 DiscoveryResponse,然后返回给 Envoy。

参考