我们都知道,K8S中一切皆资源,在使用K8S时,所有的pod或者controller都是通过yaml文件进行创建的。
那么接下来,就和大家一起看一下K8S是如何创建资源的。
Deployment是一种常见的资源对象。在Kubernetes系统中创建资源对象有很多种方法。本节将对用kubectl create命令创建Deployment资源对象的过程进行分析。kubectl资源对象创建过程如图所示。
使用kubectl创建资源对象是Kubernetes中最常见的操作之一,内部运行原理是客户端与服务端进行一次HTTP请求的交互。Kubernetes整个系统架构的设计方向是通用和具有高扩展性,所以以上功能在代码实现上略微复杂。
创建资源对象的流程可分为:
Kubernetes系统的资源对象可以使用JSON或YAML文件来描述,一般使用YAML文件居多。下面提供了一个简单的Deployment Example资源对象文件:
本文主要了解k8s创建资源对象的过程,因此代码使用截图代替。
通过kubectl create命令与kube-apiserver交互并创建资源对象,执行命令如下:
kubectl create -f nginx-deployment.yaml
在执行每一个kubectl命令之前,都需要执行实例化cmdutil.Factory
接口对象的操作。Factory是一个通用对象,它提供了与kube-apiserver的交互方式,以及验证资源对象等方法。cmdutil.Factory
接口代码示例如下:
源码使用最新版本k8s进行阅读:
vendor/k8s.io/kubectl/pkg/cmd/cmd.go:332
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
vendor/k8s.io/kubectl/pkg/cmd/util/factory.go:40
cmdutil.Factory
接口说明如下:
cmdutil.Factory
接口封装了3种client-go客户端与kube-apiserver交互的方式,分别是DynamicClient、KubernetesClientSet(简称ClientSet)及RESTClient。3种交互方式各有不同的应用场景。
关于三种客户端的介绍,将会在下一篇文章详细介绍client-go客户端的使用。
PS:动动小手关注我,后续继续学习crd与client-go的使用。
Builder用于将命令行获取的参数转换成资源对象(Resource Object)。它实现了一种通用的资源对象转换功能。Builder结构体保存了命令行获取的各种参数,并通过不同函数处理不同参数,将其转换成资源对象。Builder的实现类似于Builder建造者设计模式,提供了一种实例化对象的最佳方式。代码示例如下:
vendor/k8s.io/kubectl/pkg/cmd/create/create.go:251
首先通过f.NewBuilder实例化Builder对象,通过函数Unstructured、Schema、ContinueOnError、NamespaceParam、FilenameParam、LabelSelectorParam、Flatten对参数赋值和初始化,将参数保存到Builder对象中。最后通过Do函数完成对资源的创建。
其中,FilenameParam函数用于识别kubectl create命令行参数是通过哪种方式传入资源对象描述文件的,kubectl目前支持3种方式:
在Builder Do函数中,Result对象中的结果由Visitor执行并产生,Visitor的设计模式类似于Visitor访问者模式。Visitor接口定义如下:
vendor/k8s.io/cli-runtime/pkg/resource/interfaces.go:94
Visitor接口包含Visit方法,实现了Visit(VisitorFunc) error的结构体都可以成为Visitor。其中,VisitorFunc是一个匿名函数,它接收Info与error信息,Info结构用于存储RESTClient请求的返回结果,而VisitorFunc匿名函数则生成或处理Info结构。
Visitor的设计较为复杂,并非单纯实现了访问者模式,它相当于一个匿名函数集。在Kubernetes源码中,Visitor被设计为可以多层嵌套(即多层匿名函数嵌套,使用一个Visitor嵌套另一个Visitor)。直接阅读Visitor源码,会比较晦涩,为了更好地理解Visitor的工作原理,这里提供了代码示例。Visitor Example代码示例如下:
在Visitor Example代码示例中,定义了Visitor接口,增加了VisitorList对象,该对象相当于多个Visitor匿名函数的集合。另外,增加了3个Visitor的类,分别实现Visit方法,在每一个VisitorFunc执行之前(before)和执行之后(after)分别输出print信息。Visitor Example代码执行结果输出如下:
通过Visitor代码示例的输出,能够更好地理解Visitor的多层嵌套关系。在main函数中,首先将Visitor1嵌入VisitorList中,VisitorList是Visitor的集合,可存放多个Visitor。然后将VisitorList嵌入Visitor2中,接着将Visitor2嵌入Visitor3中。最终形成Visitor3{Visitor2{VisitorList{Visitor1}}}的嵌套关系。
根据输出结果,最先执行的是Visitor1中fn匿名函数之前的代码,然后是VisitorList、Visitor2和Visitor3中fn匿名函数之前的代码。紧接着执行VisitFunc(visitor.Visit)。最后执行Visitor3、Visitor2、VisitorList、Visitor1的fn匿名函数之后的代码。整个多层嵌套关系的执行过程有些类似于递归操作。
多层嵌套关系理解起来有点困难,如果读者看过电影《盗梦空间》的话,该过程可以类比为其中的场景。每次执行Visitor相当于进入盗梦空间中的另一层梦境,在触发执行了visitFunc return后,就开始从每一层梦境中苏醒过来。
回到Kubernetes源码中的Visitor,再次阅读源码时,就容易理解了。Visitor中的VisitorList(存放Visitor的集合)有两种,定义在vendor/k8s.io/cli-runtime/pkg/resource/visitor.go中,代码示例如下:
Kubernetes Visitor中存在多种实现方法,不同实现方法的作用不同
下面将资源创建(kubectl create-f yaml/deployment.yaml)过程中的Visitor多层匿名函数嵌套关系整理了出来:
EagerVisitorList{FileVisitor{StreamVisitor{FlattenListVisitor{FlattenListVisitor{ContinueOnErrorVisitor{DecoratedVisitor{result.Visit{}}}}}}}}
EagerVisitorList是Visitor集合,集合中包含FileVisitor和StreamVisitor,执行FileVisitor和StreamVisitor并保留执行后的error信息,然后继续执行下面的Visitor。FileVisitor和StreamVisitor将资源对象描述文件(deployment.yaml)的内容通过infoForData函数转换成Info对象。FlattenListVisitor将资源对象描述文件中定义的资源类型转换成Info对象。ContinueOnErrorVisitor将Visitor调用过程中产生的错误保留在[]error中。DecoratedVisitor会执行注册过的VisitorFunc,分别介绍如下。
由result.Visit执行createAndRefresh:第1步,通过Helper.Create向kube-apiserver发送创建资源的请求,Helper对client-go的RESTClient进行了封装,在此基础上实现了Get、List、Watch、Delete、Create、Patch、Replace等方法,实现了与kube-apiserver的交互功能;第2步,将与kube-apiserver交互后得到的结果通过info.Refresh函数更新到info.Object中。最后逐个退出Visitor,其过程为
DecoratedVisitor→ContinueOnErrorVisitor → FlattenListVisitor →FlattenListVisitor → StreamVisitor →FileVisitor→EagerVisitorList。
最终根据Visitor的error信息为空判断创建资源请求执行成功。