计算机系统是分层的,也就是下层作一些支持的工做,暴露接口给上层用。注意:语言的本质是一种接口。api
计算机的最下层是CPU指令,其本质就是用“变量定义+顺序执行+分支判断+循环”所表达的逻辑过程。计算机应用的最上层是实现人类社会的某种功能。因此全部计算机编码的过程,就是用逻辑表达现实的过程。层与层之间定义的借口,越接近现实的表达就叫越“声明式”(declarative),越接近计算机的执行过程就叫越“命令式”(imperative)。注意这不是绝对的概念,而是相对的概念。缓存
当接口越是在表达“要什么”,就是越声明式;越是在表达“要怎样”,就是越命令式。SQL就是在表达要什么(数据),而不是表达怎么弄出我要的数据,因此它就很“声明式”。C++就比C更声明式,由于面向对象自己就是一种声明式的体现。HTML也很声明式,它只描述我要一张什么样的表,并不表达怎么弄出一张表。bash
越是声明式,意味着下层要作更多的东西,或者说能力越强。也意味着效率的损失。越是命令式,意味着上层对下层有更多的操做空间,能够按照本身特定的需求要求下层按照某种方式来处理。数据结构
想要使用Kubernetes 的 API 对象,须要编写一个对应的 YAML 文件交给 Kubernetes,而声明式API,则为kubectl apply 命令。而先 kubectl create,再 replace 的操做,称为命令式配置文件操做,并非声明式API。app
kube-apiserver 在响应命令式请求(如kubectl replace)的时候, 一次只能处理一个写请求,不然会有产生冲突的可能;而对于声明式请求(如kubectl apply),一次能处理多个写操做,而且具有 Merge 能力。函数
声明式 API是 Kubernetes 项目编排能力“赖以生存”的核心所在:编码
首先,“声明式”就是提交一个定义好的API对象来声明所指望的状态是什么url
其次,声明式API容许有多个API写端,以PATCH的方式对API对象进行修改,而无需关心本地原始YAML文件的内容code
RESTful 使用POST来建立一个资源,使用PUT或者PATCH来更新一个资源 区别是: – PUT用来总体更新一个资源,因此请求中必须包含完整的资源信息。若是缺乏部分信息,会致使这部分数据被更新为NULL。 – PATCH则是部分更新。仅更新提供的字段,请求中缺乏的字段仍保持不变
最后,也是最重要的,有了上述两个能力,Kubernetees项目才能够基于对API对象的增、删、改、查在彻底无需外界干预的状况下,完成对“实际状态”和“指望状态”的调谐(Reconcile)过程。orm
首先知道一下一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)、 Version(API 版本)和 Resource(API 资源类型)三个部分组成的,能够用以下图的树形结构表示出来:
API对象的组织方式是层层递进的,Kubernetes会对Group、Version和Resource进行解析,也就是层层匹配,获得相应的对象定义,如Cronjob(Pod、Node 等核心API对象不须要Group,直接匹配Version)。
把YAML 文件提交给 Kubernetes 以后,建立出 API 对象的流程:以建立 CronJob为例
发起建立CronJob的POST请求后,编写的YAML的信息就被提交给api-server
Api-server过滤这个请求,完成一些前置性的工做,好比受权、超时处理、审计等
请求进入mux和routes流程,mux和routees是api-server完成url和handler绑定的场所,api-server的Handler要作的事情就是按照层层匹配的过程,找到对应的CronJob类型定义
Api-server根据crontJob类型定义,使用用户提交的YAML文件里的字段,建立一个CrontJob对象
Api-server会进行一个convert工做,即把用户提交的YAML文件转换成一个叫作Super Version
的对象,它正是该API资源类型全部版本的字段全集,这样用户提交的不一样版本的YAML就均可以用这个Super Version对象来进行处理了
前后进行admission()和validation()操做
Admission Controller和Initializer都属于Admission的内容,Validation负责验证这个对象里的各个字段是否合法,这个被验证过的API对象,都保存在来api-server里一个叫作Registry的数据结构中,也就是说只要一个API对象的定义能在Registry里查到,他就是一个有效的Kubernetes API对象
把验证过的API对象转换成用户最初提交的版本,进行序列化操做,并调用Etcd的API把它保存起来
若是想要添加自定义API资源类型,建议使用CRD( Custom Resource Definition),它容许用户在 Kubernetes 中添加一个跟 Pod、Node 相似的、新的 API 资源类型,即:自定义 API 资源。
使用CRD建立出自定义API对象后,就是为这个 API 对象编写一个自定义控制器(Custom Controller),这样, Kubernetes 才能根据 自定义 API 对象的“增、删、改”操做,在真实环境中作出相应的响应。
编写自定义控制器分为三个过程:编写 main 函数、编写自定义控制器的定义,以编写控制器里的业务逻辑。
首先从 Kubernetes 的 APIServer 里获取它所关心的对象,也就是自定义的控制器对象。这个操做,依靠的是一个叫做 Informer(通知器)的代码库完成的;Informer 与 API 对象是一一对应的,因此须要传递给自定义控制器一个Informer;
Informer是一个带有本地缓存和索引机制的、能够注册EventHandler 的 client。它是自定义控制器跟 APIServer 进行数据同步的重要组件。
建立Informer的时候须要传一个Client,Informer 正是使用Client,跟 APIServer 创建了链接;真正负责维护这个链接的是 Informer 所使用的 Reflector 包。
Reflector 使用的是一种叫做ListAndWatch的方法,来“获取”并“监听”这些API对象实例的变化(Informer经过 ListAndWatch,把 APIServer 中的 API 对象缓存在了本地,并负责更新和维护这个缓存。)在 ListAndWatch 机制下,一旦 APIServer 端有新的API对象实例被建立、删除或者更新, Reflector 都会收到“事件通知”;这时,该事件及它对应的 API 对象这个组合,就被称为增量 (Delta),它会被放进一个 Delta FIFO Queue(即:增量先进先出队列)中;Informe 会不断地从 Delta FIFO Queue 里读取(Pop)增量。每拿到一个增量,Informer 就会判断这个增量里的事件类型,而后建立或者更新本地对象的缓存;这个缓存,在 Kubernetes 里通常被叫做 Store
Informer 的第二个职责,则是根据这些事件的类型,触发事先注册好的 ResourceEventHandler;
这些 Handler,须要在建立控制器的时候注册给它对应的 Informer。
Informer 与要编写的控制循环之间,则使用了一个工做队列来进行协同,防止控制循环执行过慢把Informer拖死。
接下来就是熟悉的控制循环的逻辑了
参考连接: