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 向Sink
Server建立新连接,然后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
创建Galley
Server。然后调用其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
元数据配置文件