昨天咱们介绍了k8s中资源插件机制的核心关键组件,今天咱们继续来看下各个组件是如何进行通讯的,以及k8s中针对事件处理背后的关键设计后端
PluginManager是一个上层组件,其内部包含了上篇文章中的关键组件,而且协调其内部数据流,并且还提供针对不一样插件的具体的控制器api
核心结构里面其实就是按照数据流来进行设计的,首先须要一个感知插件desiredStateOfWorldPopulator用于感知后端服务的建立或者删除,而后将感知到的事件加入到desiredStateOfWorld指望状态缓存,由reconciler负责期进行底层的注册和下线,而且将结果存储到actualStateOfWorld实际状态缓存缓存
type pluginManager struct { // 插件感知 desiredStateOfWorldPopulator *pluginwatcher.Watcher // 协调器插件 reconciler reconciler.Reconciler // 实际状态缓存 actualStateOfWorld cache.ActualStateOfWorld // 指望状态缓存 desiredStateOfWorld cache.DesiredStateOfWorld }
初始化中会将dsw和asw都交给reconciler用于进行事件的感知和更新对应的缓存数据结构
func NewPluginManager( sockDir string, recorder record.EventRecorder) PluginManager { asw := cache.NewActualStateOfWorld() dsw := cache.NewDesiredStateOfWorld() // 这里会将指望状态缓存和实际状态缓存,都交给reconciler reconciler := reconciler.NewReconciler( operationexecutor.NewOperationExecutor( operationexecutor.NewOperationGenerator( recorder, ), ), loopSleepDuration, dsw, asw, ) pm := &pluginManager{ // 启动一个watcher而且存储dsw指望状态缓存,后续reconciler就能够经过dsw感知到新的状态了 desiredStateOfWorldPopulator: pluginwatcher.NewWatcher( sockDir, dsw, ), reconciler: reconciler, desiredStateOfWorld: dsw, actualStateOfWorld: asw, } return pm }
插件管理器启动其实就是启动内部的desiredStateOfWorldPopulator就会讲watcher感知的事件,不断的修改本身的内部缓存这样reconciler就能够不断的经过指望状态缓存,进行对应grpc的调用从而知足指望状态ide
func (pm *pluginManager) Run(sourcesReady config.SourcesReady, stopCh <-chan struct{}) { defer runtime.HandleCrash() // 运行指望状态缓存,其实主要是经过watcher感知到的事件,修改自身的缓存 // 后续reconciler会周期性的获取 pm.desiredStateOfWorldPopulator.Start(stopCh) klog.V(2).Infof("The desired_state_of_world populator (plugin watcher) starts") klog.Infof("Starting Kubelet Plugin Manager") // 周期性的运行校证数据 go pm.reconciler.Run(stopCh) metrics.Register(pm.actualStateOfWorld, pm.desiredStateOfWorld) <-stopCh klog.Infof("Shutting down Kubelet Plugin Manager") }
控制器其实主要是指的reconciler经过对比指望缓存和实际缓存之间的差别,产生对应的事件以后,针对该类型的插件,后续的处理流程是什么,好比注册/下线具体的grpc接口和对应插件类型的处理机制oop
func (pm *pluginManager) AddHandler(pluginType string, handler cache.PluginHandler) { pm.reconciler.AddHandler(pluginType, handler) }
当前的kubelet中有注册两种类型的插件控制器,CSI与DEVICPLUGIn,从名字上你们也能知道大概的意思插件
kl.pluginManager.AddHandler(pluginwatcherapi.CSIPlugin, plugincache.PluginHandler(csi.PluginHandler)) kl.pluginManager.AddHandler(pluginwatcherapi.DevicePlugin, kl.containerManager.GetPluginRegistrationHandler())
这里咱们只介绍一个即DevicePlugin的核心实现机制设计
Endpoint其实指的就是某个提供扩展资源的服务,在以前说的reconciler中,会获取其对应的grpc服务的地址,后续则会直接调用grpc进行通讯3d
Endpoint须要感知对应的资源设备的变化,同时将对应的设备信息,回调通知给当前的code
Manager则是主要负责实现后端真正的Register/UnRegister的具体实现,其在内部会为每一个Device建立一个Endpoint并负责收集后端提供资源服务上报上来的信息, 最终会讲对应的信息发送给kubelet,而后由kubelet在负责节点信息更新的时候,将信息传递给APIServer
Checkpoint机制其实在不少系统中都比较经常使用,主要是用于周期性的将内存中的数据序列化存储到本地的磁盘中,在后续恢复的时候,会经过磁盘从新加载以前的数据,从而实现内存资源的快速恢复
扩展资源的总体实现流程大概就是这个样子,从如何感知数据,注册资源服务,获取资源服务的资源信息,并最终汇报给kubelet,同时落地本地磁盘,实现了完整的资源从感知到上报的总体流程的探测,其不足主要是在于关于资源实体的描述,从而致使资源的分配和资源的上报上有比较大的扩展性限制,好比要实现精细化的资源分配扩展,则不太能实现
k8s源码阅读电子书地址: https://www.yuque.com/baxiaoshi/tyado3