+-
Pilot MCP协议介绍

背景介绍

Istio是目前主流的Service Mesh组件。Istio基于Service Mesh的理念,承担着服务发现、服务通信、负载均衡、限流熔断、监控等等功能。让应用不用关心这些底层逻辑,专注于业务的开发和访问即可。软件行业的趋势一定是让业务的开发越来越轻松和简便,所以从这个角度看以Istio为代表的Service Mesh一定会越来越流行。至于目前存在的一些包括性能和设计复杂的问题,一定可以通过技术手段解决。

总体架构

MCP协议推出的背后,是Pilot意图重构以解决之前架构带来的代码维护、测试、扩展及稳定性等多方面的问题。在之前的架构中,Pilot内嵌了与K8S的交互逻辑以及其他扩展的注册中心和配置中心的逻辑。这样导致Pilot内部逻辑比较复杂、承担职责不够清晰。同时与各个组件的交互协议也不统一,导致Pilot难以维护这些代码,对于后面的扩展组件接入,也望而却步。在Pilot的重构设计文档中,对于Pilot目前存在的问题是这么总结的[1]:

Pilot is more difficult to test than is necessary. It requires a dependency on K8S Apiserver in order to write more complex unit or simple integration tests. We have smeared logic for dealing with the vagaries of K8S CRDs across Pilot, Mixer & Citadel. Common concerns like K8S integration, resource validation and API versioning do not share a common implementation. The proposal to resurrect **Galley **is primarily motivated by an obvious need to improve our user-facing API experience. Consuming the K8S API client in Pilot is a source of bloat and represents a production risk. Moving that risk to Galley while isolating the network control plane from it should be a performance and stability win. The contract between Pilot, Kubernetes and other platforms is under-specified. We implicitly consume the totality of the K8S API surface even if we don’t need it. Writing extensions to Pilot requires developers to consume our build process and this has proven to be a barrier to adoption for partners. This is analogous to the recent architectural shift in Mixer to use out-of-process adapters for the same reasons.

Istio在Pilot重构设计文档中,展示了基于MCP协议的内部组件架构。在这个架构中,Pilot与Envoy还是使用Envoy定义的ADS协议,Pilot与Galley、外部的注册中心、配置中心以及其他的Pilot之间,都是通过MCP协议进行通讯:

...

这样的设计比较好的定义了Pilot的职责,就是整个Service Mesh的控制面,但是不感知任何Istio以外的组件,即使是K8S也是如此。所有与外部组件包括注册中心和配置中心的交互逻辑,都移到了Pilot进程以外,通过运行特定的MCP Server来与Pilot进行通讯。Galley的职责,我认为是Pilot中默认的配置来源的MCP Server,目前是实现了与K8S数据的对接。这个文档中还定义了Pilot与Pilot之间的数据访问也是通过MCA(基于MCP协议定义的API),但是文档里没有提供更多的细节,会不会是Pilot未来会支持数据的分片以提高Pilot整体的数据容量?

协议定义

看完了Pilot的整体演进架构,我们来看一下MCP协议的设计[2],MCP协议在设计上参考了与Envoy的交互协议xDS。

...

在MCP协议中,定义了两个身份:Sink和Source。Sink指代的是数据的接收端,Source指代的是数据的发送端即数据源。MCP协议基于gRPC双向流协议进行定义,Sink端和Source端都可以随时往对端发送数据,相互不会阻塞。从上图的协议交互也可以看出,Sink端可以主动发送RequestResources请求来向Source端要求数据,同时Source端也可以主动将Resources数据发送给Sink端。Sink端在收到数据后会返回一个ACK确认。在RequestResources请求和ACK响应中,主要有两个字段:

collection:表示此次请求需要的数据类型,目前Istio定义了17种类型(测试得出,实际数据以Istio官方说法为准),其中包含了serviceentries作为服务发现的数据等。 nonce:类似于请求的ID,用来与响应或者请求进行匹配。

而在返回的数据中,还有一个字段就是resources字段,里面包含真正的数据,具体的数据格式和collection的类型有关。

同时MCP协议还定义了增量推送的能力,如下图所示。可以在RequestResouces请求中增加incremental=true字段,这样Sink在收到数据后,会根据增量的形式进行数据的更新。协议的文档中还指出,必须在RequestResouces请求中包含incremental=true的情况下,才能返回给Sink增量的数据,否则Sink端对于该数据的处理将是未知的。目前社区的Pilot还没有支持增量的MCP数据推送,从下一节的源码分析中可以看到,Pilot对于每一个Source(Pilot可以配置多个Source,每个Source之间的数据是隔离的)发送的数据,都是整体替换更新的。目前社区的一个进展是正在进行serviceentries类型数据的增量更新的支持,而且是endpoints粒度的增量更新[3]。

...

Pilot Sink端启动流程

Pilot在启动的时候,会根据ConfigMap里的配置,进行MCP Server的初始化。主要的逻辑可以参考下图:

...

Pilot启动时,会调用server.NewServer来新建Pilot Server。在此过程中,会去读取ConfigSource配置里面的address。由于可能会配置多个address,那么Pilot在这里会循环去创建多个Sink客户端①,每一个Sink客户端拥有独立的数据。在这里有一点要注意的是,这个循环中会尝试与每个MCP Server创建gRPC连接,而如果其中一个MCP Server连接不上的话,整个循环就会推出,并返回error。也就是说如果Pilot在启动过程中无法连上每一个MCP Server,那么Pilot就会处于不健康状态,而在运行时没有这个限制②。当Sink客户端都新建完以后,就会再次循环异步调用每个Sink客户端的run方法,进行数据的收发了。

Sink端的run方法主要是调用EstablishResouceStream方法来创建双向流连接。在连接创建后,会调用creatIntialRquests请求来创建一次初始请求③,也就是说Pilot不会在启动时阻塞的去从每个MCP Server拉取数据,更新完后再提供服务,而是异步的进行数据的请求。事实上,Pilot也只会主动发这一次数据的请求④,接下来的数据更新都依赖于MCP Server的主动推送。在收到数据后,则进行handleResponse方法进行数据的更新⑤。

Pilot Sink端处理流程

下面介绍的是Pilot的Sink端在接收到数据的处理逻辑。从下图中可以看出,在接收到数据后,会先判断是否返回了错误,如果返回错误则直接结束数据的更新。如果没有错误,则进入Apply(change)方法。在这个方法中,会先新建一个innerStore的Map来保存更新到的数据。然后会将原来的configStore里对应collection类型的数据直接由这个innerStore进行替换。因此可以看出Pilot目前还不支持增量的MCP数据推送。在更新完数据后,则调用serviceEntryEvents或者ClearDiscoveryServerCache来通知Envoy等组件进行数据的更新。

...

总结与展望

目前Source端可以参考的代码有Galley,以及Nacos开发的独立MCP Server,用于进行MCP功能的测试和压测[4],以及和Nacos Server一起部署的Java版本的MCP Server[5]。本文不做详细介绍,有兴趣的同学可以自行阅读相关源代码。

整体来看目前Pilot MCP协议还处于快速的更新和迭代阶段,未来可能还会有很多的变化。但是Istio基于MCP协议进行重构的大方向应该是不会跑了。Nacos团队目前也在密切关注着Istio及其他云原生组件的发展方向,同时将会陆续发布相关的功能,敬请期待。

原文

[1] Pilot重构设计文档 https://docs.google.com/document/d/1S5ygkxR1alNI8cWGG4O4iV8zp8dA6Oc23zQCvFxr83U/edit#

[2] MCP协议介绍 https://github.com/istio/api/tree/master/mcp

[3] Pilot serviceentries增量推送的PR https://github.com/istio/istio/pull/12276

[4] Nacos提供的独立的MCP Server,Go语言开发 https://github.com/nacos-group/nacos-istio

[5] Nacos对接Istio的issue,以及相关的commits https://github.com/alibaba/nacos/issues/1409