MCP协议&Galley
[TOC]
MCP协议
- MCP协议全称是- Mesh Configuration Protocol。
- 在Istio历史版本里,基于k8s来存储数据导致其与k8s耦合,限制了Istio的拓展性(非k8s机制则无法使用)。
- MCP协议的出现即是为了解决与- k8s耦合的问题。- MCP定义了一套配置订阅与下发的标准协议,- Pilot和- Mixer作为- MCP client,任何实现了- MCP协议的Server(如- Galley)通过- MCP协议向- Pilot下发配置。
MCP中对配置生生产者和消费者进行了如下抽象:
- source:配置源,即- Istio的- Galley(配置中心),对应的是Grpc中定义的 Service:- ResourceSource;
- sink:配置消费者,即- Istio中的- Pilot、- Mixer,对应的Grpc中定义的- ResourceSink;
- resource:配置资源的抽象定义。
- 通常情况下,source作为Grpc的server,提供ResourceSource服务;sink作为Grpc的client,提供ResourceSink服务,sink主动向source发起连接,请求资源。当有资源更新时source将信息push给sink;
- 但MCP也支持一些特殊场景,Source作为Client 向SinkServer建立新连接,然后Sever向Client发起资源请求(实际还是Sink请求Source,只是Sink作为Server,让Source连上来)。

MCP中Sink与Source交互接口定义:
| 1 | type ResourceSource_EstablishResourceStreamClient interface { | 
Galley
前面介绍了 MCP 协议,接下来我们先介绍下Galley以及其工作原理,然后再以其与Pilot的交互流程讲解 MCP 的工作原理。
Galley 主要负责两方面的功能:配置验证(校验配置的正确性)以及配置管理(输入、转换、分发)
初始化
Galley启动入口位置:galley/cmd/galley/main.go。
首先,创建server.Args,通过参数初始化之后调用server.New创建GalleyServer。然后调用其Start方法开启Galley服务。
创建Server的处理过程如下:
| 1 | func New(a *settings.Args) *Server { | 
Galley 让所有组件都实现 Component接口,其定义了Start方法和Stop方法。Server创建所有组件后执行所有组件的Start方法,当Server要退出时执行Stop方法。
配置校验器
Istio 中配置非常之多,而 Istio 配置数据的主要来源是 k8s。在 k8s 中创建各种类型的配置 CRD 资源,Istio 通过监听这种类型资源进行处理转换后下发给 Envoy。配置资源如果任意设置可能会导致意想不到的结果,而 k8s 提供了一种叫Admission WebHooks的拓展,用来支持对配置的验证工作。
工作原理如下:
- 向k8s中注入用于准入的WebHookConfiguration(配置校验服务器信息),在k8s中有配置输入时,会向配置的校验服务器发起准入请求;
- 校验服务器会拿到请求中的资源信息,根据其配置的规则进行校验,若不通过,则返回告诉k8s拒绝,并输入具体信息;否则返回成功。

初始化
| 1 | webhookServerReady := make(chan struct{}) | 
其过程分为两步:创建并运行校验模块,当校验模块就绪后向 k8s 注入WebhookConfiguration。
首先,先看校验模块的初始化过程:
| 1 | func RunValidation(ready chan<- struct{}, stopCh chan struct{}, vc *WebhookParameters, kubeConfig string, | 
总结其过程:首先创建默认的校验器,其次创建WebHook监听端口并添加指定路径的处理函数,最后通过查询k8s查询到节点可读时告知WebhookController向k8s注册ValidatingWebhookConfiguration。
校验
接下来,继续跟进具体的校验逻辑,对于Pilot配置逻辑校验入口admitPilot;而对于Mixer配置逻辑校验入口为admitMixer。
| 1 | func (wh *Webhook) admitPilot(request *AdmissionRequest) *AdmissionResponse { | 
总结,Pilot的校验过程较简单,主要检查:
- 解码是否正常;
- 是否能转换成Istio类型;
- 是否符合Schema中配置的Validate函数;
- 必须属性是否无缺失
Mixer配置校验过程如下:
| 1 | switch request.Operation { | 
总结其过程如下:
- 解码,若解码异常则返回失败
- 必备字段是否无缺失
- 是否是Mixer关注的类型
- 是否能转换成Mixer中定义的类型所对应的对象
- 应用变更
- 查看是否能成功构建快照成功(之所以这么做的原因是:Mixer中配置之间存在一定的关联性(adapter、template…),只校验单个实体是不够的)
配置管理
Galley中负责配置管理组件是Processing,它有两个版本Processing和Processing2,下面我们将主要研究Processing2的实现。
| 1 | // 资源快照的缓存 | 
processing2由以下几个组件组成:
- Source:配置源,如k8s、fs;
- Runtime:- Galley中最复杂的模块,它将- Source、- Transform、- Snapshotter、- distributor连接在一起。- runtime中创建带有状态机机制的组件 session 来追踪开始、结束事件处理循环- inactive:未激活状态;
- starting:当会话开始时,即转变为开始状态。为了避免在启动阶段阻塞并可以正确处理所有生命周期事件,其是一个很短暂的状态。当所有数据源初始化完成时,它将会被转换成- buffering状态。
- buffering: 此阶段,所有数据源都已经初始化完成,但还没收到- meshconfig时间。所有非- meshconfig事件都将会被缓存,直到- meshconfig到达;
- processing: 一旦接收到- meshconfig后,- runtime将开始做配置转换并开始分发缓存的事件到各转换器。当接收到- reset事件或者收到- meshconfig的变更时,- session将转换成- inactive状态或者创建新的- session。
 
 
- snapshot.Cache: 配置快照
processing2 初始化流程实现如下:
| 1 | func (p *Processing2) Start() (err error) { | 
总结其流程如下:
- 初始化配置源Source:meshSource(文件配置源)、apiServer.Source(k8s crd配置源);
- 解析配置文件中的元数据,初始化转换器提供者;
- 创建并初始化 Runtime;
- 创建Callout(将galley作为client方式为目标服务提供配置服务);
- 创建 gRpc Service mcpSource,为配置使用方服务;
- 启动 Runtime,过程比较复杂,下面将详细介绍;
- 启动grpc提供服务;
- 启动callout连接到目标服务,对其提供服务。
可以看到Runtime是Galley的核心,对接各种配置源,并进行配置转换。而mcpSource是用来对配置客户端提供服务的,他们之间通过mcpCache进行衔接。Runtime经过一系列处理后构建Snapshot,然后将快照传进mcpCache。mcpSource从mcpCache中获取配置信息,同时监听mcpCache的配置变更并push到客户端。
Runtime启动流程大致如下:
总结起来,Galley整体架构如下:
配置处理流程
- 当client首次与gRpcServer建立连接后,会从stream中获取其节点信息PeerInfo,同时从PeerInfo中截取AuthInfo进行校验;
- mcpSource调用- Processtream方法对- Stream进行处理:- 首先为每个客户端创建一个connection对象,用来保存其元信息(节点信息、订阅类型以及订阅类型的监听状态等)
- 然后会同时监听connection的请求通道reuqestC和向其推送信息的队列queue
 
- 首先为每个客户端创建一个
| 1 | type connection struct { | 
初次请求处理流程如下:
变更下发流程如下:
以apiServer为配置源,资源 VirtualService 为例来讲解配置下发流程:
可以看出其流程非常之长,但概括起来其过程:
- Source变更 -> Runtime进行转换 -> Cache更新快照 -> connectin连接下发更新
附件
- metadata.yaml元数据配置文件 
 
        