k8s----声明式api的工作原理

在kube项目中,一个api对象在etcd中的 完整路径是group。version。resource。三个部分组成的;gvr
通过这样的结构。整个kube里的所有api对那个实际上就可以用下图结构表示:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_容器


kube是如何对resource,group。vresion进行解析的?

首先。kube根据yaml中的apiversion。找到组group/version。从etcd中按照层级找到api对象。

比如书中一个栗子,cronjob。

yaml:apiVersion:batch/v2alpha1

cronjob不是核心api。kube会在/apis层找到对应的group。进而根据batch这个group的名字找到/apis/batch。然后从batch组匹配到版本号v2.。。

在kube中,同一个api对象可以存在多个版本。这是kube纪念性api版本化管理的重要手段;

最后kube会匹配到api对象的资源类型即kind定义的类型:cronjob

此时kube就 可以继续创建这个cronjob对象了;创建api对象的流程图:

cronjob为例:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_List_02


当发起穿件cronjob的post请求后。我们编写的yaml信息就提交给了apiserver

apiserver会先过滤这个请求。同时完成一些前置性的工作。比如授权,超时处理,审计等;

然后请求通过mux和routes流程=====mux和routes是apiserver完成url和handler绑定的场所。apiserver和handler要做的就是按之前的找到gvr的过程。找到对应的cronjob的类型定义;

接着apiserver根据这个cronjob类型定义使用用户提交的yaml配置的字段来创建cronjob对象;

在此过程中,api会进行一个convert操作。把用户提交的yaml文件转换成一个super version的对象,他是该api资源所有版本的字段的全集,

apiserver根据admission和validation操作。

validation负责验证这个对象的字段是否合法。经过验证的api对象保存在apiserver的一个叫做registry的数据结构中 。也就是说。一个api对象的定义能在registry里能查到。他就是有效的kubeapi对象;

嘴鸥apiserver会将经过验证的api 对象转换成用最初提交的版本。进行序列化操作,并调用etcd的api将其保存 ;即panding过程;

声明式api对于kube来说非常重要;

apiserver由于要兼容性能,api完备性。版本化,向后兼容等很多工程化指标,因此kube团队在apiserver项目中使用了大量的go的代码生成功能,来自动化注入convert。deepcopy等与api资源相关的操作,这部分自动生成的代码层一度占到kube项目总代码的20%-30%。

在kube1.7之后。增加了一个全线的api插件机制;CRD,自定义API资源;

现在要定义一个crd:一个栗子:

现在添加一个名叫network的api资源类型;

作用:一旦用户创建一个network对象,那么kube就应该能使用这个对象定义的网络参数,调用真实的network插件。来为用户创建一个真的网络;这样用户将来创建的 pod就可以使用network这个api网络资源类型了;

现在我想创建的这个network资源对象的yanl对象:

apiVersion:组:samplecrd.k8s.io。。。version:v1。 资源对象名称:network

参数metadata。name

spec:cidr字段参数。gateway字段参数

那么怎么让kube知道这个api的存在呢。samplecrd.k8s.io/v1/network的存在呢;

其实这个yaml文件就是一个具体的CRD的实例,也叫CR。custom resource。为了能让kube认识这个CR。就需要让kube明白这个CR的宏观定义是什么也就是CRD。

所以要定义CRD的model。让kube知道什么CRD是network这个资源类型;

首先:定义一个CRD的yaml文件配置。apiVersion:apiextensions.k8s.io/v1。kind使用CustomResourceDefineition。这个crd这个apiresource。贴一个crd的配置yaml

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_03


可以看到在这个CRD资源类型中,指定了group:samplecrd.k8s.io。version;v1这样的API信息。也制定了这个CR的资源类型为Network。附属是networks。然后还声明了他的scope是namespaced。也就是说这个network是一个属于namespace的对戏那个。类似于pod

这就是一个Network的CRD的api的定义。等于告诉kube。以后所有声明式api类型是samplecrd.k8s.io/v1/network的yaml对象。就属于Network这个CRD的api资源对象;

接下来。要让kube理解这个yaml中描述的网络部分。比如这种network资源所需要的一些字段含义,比如:cidr网段,gateway网关等

需要创建一个目录:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_kubernetes_04


其中pkg/apis/samplecrd是api组 group的名字。v1是版本信息目录。而v1下面的types.go文件里定义了network对象的完整描述

然后在pkg/apis/samplecrd目录下创建一个register.go的文件。用来放置后面要用到的全局变量。这里定义一些全局变量;

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_kubernetes_05


接着。需要在pkg/apis/samplecrd目录下添加一个doc.go文件。golang的文档源文件。这个文件里的内容:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_List_06


这个文件中包含+<tag_time> [=value]格式的注释,这就是kube进行代码生成的annotation风格的注释。

其中:+k8s:deepcopy-gen=package意思是:为整个v1包里的所有类型定义自动生成DeepCopy方法;

+groupName=samplecrd.k8s.io,定义了这个包对应的API组的名字;

可以看到这些定义在doc.go文件的注释的作用是全局的代码生成控制,这里的注释也成为Global Tags

接下来。需要添加types.go文件,顾名思义。他的作用就是定义一个Network类型到底有哪些字段。比如spec字段里的内容。

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_List_07


代码中可以看到network类型定义方法跟标准的kube对象一样。包含typemeta。API元数据和ObjectMeta对象元数据字段;

其中的spec字段是需要自定义的部分。所以在networkspec中国呢定义了crdr和gateway字段。其中每个字段最后的部分。比如json:“cidr”。指的就是这个字段被转换成json字段后的名字就是yaml文件里的字段名字;

除了network类型还需要定义一个networkList类型。用来描述这个对象应该包含哪些字段。之所以需要这样一个类型是因为在kube中获取所有x对象的List()的返回值都是List类型。而不是x类型的数组;

其中:+genclient的意思是为下面这个api资源类型生成对应的client代码。

+genclient:noStatus的意思是:这个api资源类型定义里没有status字段。否则。生成的client就会自动带上updateStatus方法;

如果你的类型定义包含了Status字段。就不需要这个+genclient:noStatus的注释定义了;

可以如下定义:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_08


需要注意的是。+genclient只需要写在network类型上;而不用写在networkList类型上。这是因为networkList只是一个返回值类型。Network才是主类型;

在globaltags中已经定义了所有类型生成deepcopy方法。因为这里不需要再显式的加上+k8s:deepcopy-gen=true了。这也意味着可以用+k8s:deepcopy-gen=false来阻止为某些类型生成deepcopy;

在两个类型上还有语句注释:+k8s.deepcopy-gen:interface=k8s.io/apimachinery/pkg/runtime.Object.他的意思是。在生成deepcopy代码的时候 实现kube提供的runtime.Object接口。否则,在这些版本的kube里这个类型定义出现编译错误;

最后。还需要在编写一个pkg/apis/samplecrd/v1/register.go文件。

apiserver工作原理中。registry的作用是注册一个类型给apiserver。其中network资源类型在服务端的注册工作。apiserver会自动帮我们完成;但与之对应的。还需要客户端知道。network资源类型的定义;这就需要在项目里增加一个register.go。他主要的功能是定义如下的addKnowTypes()方法;

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_09


声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_10


有这个方法。kube就能在生成client时知道network以及networkList类型的定义了;

像上面这种register.go里的内容是非常固定的,可以直接使用这个代码作为模版。然后吧其中的资源类型,GroupName。Version替换成自己的定义即可;

致辞netwrork的Crd对象的定义就完成了

小结:

第一部分是自定义资源类型的API描述,包括组。版本。资源类型等。这相当于告诉kube。这种crd包含哪些gvr

第二部分是自定义类型的对象描述,包含Spec。Status等。这相当于告诉kube。这些kind要包含哪些clum字段;接下来。要使用kube提供的代码生成工具,为前面定义的crd network资源类型自动生成clientset informer 和lister,其中clientset就是操作network对象需要的client。

代码生成工具叫做k8s.io/code-generator使用方法:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_API_11


代码生成工作生成完成之后。项目的代码目录结构:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_12


声明式pipeline podtemplate可以在哪里出现 什么是声明式api_API_13


其中pkg/apis/samplecrd/v1/zz_generated.deepcopy.go文件就是自动生成的DeepCopy代码文件;

整个client目录以及下面的clientset。informer。lister。都是kube为network类型生成的客户端库,

可以看到。其实到目前为止的这些工作其实不要求你写多少代码。主要考验。复制。粘贴,替换这种基本功;

有这些内容。就可以在kube集群中创建一个network类型的api对象了。

实验一下:

创建network。crd配置;

kubectl apply -f crd/network.yaml

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_API_14


kubectl get crd

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_List_15


这样就可以创建network对象了;

创建network对象:

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_字段_16


可以通过kube get来获取network对象:还可以通过describe来查看network对象的细节;

声明式pipeline podtemplate可以在哪里出现 什么是声明式api_API_17


小结:

今天详细解析了kube声明式api的工作原理。讲解如何遵循声明式api的设计,为kube定义名为network的CRD的API资源类型。从而可以通过现有的标准,kubectl create。get来操作和管理自定义API对象;

创建出这样的CRD。只是完成kube声明式API的一半工具。剩下的工作是为这个API资源类型编写一个定义控制器。这样kube才能根据network对象的curd操作。在真实环境中作出相应的响应;比如真正的创建。删除,修改真正的neutron网络。这就是network这个API所关注的业务逻辑;

明天看API编程范式的具体原理;