看完此文,谁还敢说你不会创建k8s资源

我们都知道,K8S中一切皆资源,在使用K8S时,所有的pod或者controller都是通过yaml文件进行创建的。

那么接下来,就和大家一起看一下K8S是如何创建资源的。

创建资源对象的过程

Deployment是一种常见的资源对象。在Kubernetes系统中创建资源对象有很多种方法。本节将对用kubectl create命令创建Deployment资源对象的过程进行分析。kubectl资源对象创建过程如图所示。

看完此文,谁还敢说你不会创建k8s资源_第1张图片
使用kubectl创建资源对象是Kubernetes中最常见的操作之一,内部运行原理是客户端与服务端进行一次HTTP请求的交互。Kubernetes整个系统架构的设计方向是通用和具有高扩展性,所以以上功能在代码实现上略微复杂。

创建资源对象的流程可分为:

  1. 实例化Factory接口
  2. 通过Builder和Visitor将资源对象描述文件(deployment.yaml)文本格式转换成资源对象。
  3. 将资源对象以HTTP请求的方式发送给kube-apiserver,并得到响应结果。
  4. 最终根据Visitor匿名函数集的errors判断是否成功创建了资源对象。

1. 编写资源对象描述文件

Kubernetes系统的资源对象可以使用JSON或YAML文件来描述,一般使用YAML文件居多。下面提供了一个简单的Deployment Example资源对象文件:

本文主要了解k8s创建资源对象的过程,因此代码使用截图代替。
看完此文,谁还敢说你不会创建k8s资源_第2张图片
通过kubectl create命令与kube-apiserver交互并创建资源对象,执行命令如下:

kubectl create -f nginx-deployment.yaml

2. 实例化Factory接口

在执行每一个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

看完此文,谁还敢说你不会创建k8s资源_第3张图片
cmdutil.Factory接口说明如下:

  • DynamicClient:动态客户端。
  • KubernetesClientSet:ClientSet客户端。
  • RESTClient:RESTClient客户端。
  • NewBuilder:实例化Builder,Builder用于将命令行获取的参数转换成资源对象。
  • Validator:验证资源对象。

cmdutil.Factory接口封装了3种client-go客户端与kube-apiserver交互的方式,分别是DynamicClient、KubernetesClientSet(简称ClientSet)及RESTClient。3种交互方式各有不同的应用场景。

关于三种客户端的介绍,将会在下一篇文章详细介绍client-go客户端的使用。

PS:动动小手关注我,后续继续学习crd与client-go的使用。

3. Builder构建资源对象

Builder用于将命令行获取的参数转换成资源对象(Resource Object)。它实现了一种通用的资源对象转换功能。Builder结构体保存了命令行获取的各种参数,并通过不同函数处理不同参数,将其转换成资源对象。Builder的实现类似于Builder建造者设计模式,提供了一种实例化对象的最佳方式。代码示例如下:
vendor/k8s.io/kubectl/pkg/cmd/create/create.go:251

看完此文,谁还敢说你不会创建k8s资源_第4张图片
首先通过f.NewBuilder实例化Builder对象,通过函数Unstructured、Schema、ContinueOnError、NamespaceParam、FilenameParam、LabelSelectorParam、Flatten对参数赋值和初始化,将参数保存到Builder对象中。最后通过Do函数完成对资源的创建。

其中,FilenameParam函数用于识别kubectl create命令行参数是通过哪种方式传入资源对象描述文件的,kubectl目前支持3种方式:

  • 第1种,标准输入Stdin(即cat deployment.yaml|kubectl create-f);
  • 第2种,本地文件(即kubectl create-f deployment.yaml);
  • 第3种,网络文件(即kubectl create-f http:///deployment.yaml)。

4. Visitor多层匿名函数嵌套

在Builder Do函数中,Result对象中的结果由Visitor执行并产生,Visitor的设计模式类似于Visitor访问者模式。Visitor接口定义如下:

vendor/k8s.io/cli-runtime/pkg/resource/interfaces.go:94

看完此文,谁还敢说你不会创建k8s资源_第5张图片
Visitor接口包含Visit方法,实现了Visit(VisitorFunc) error的结构体都可以成为Visitor。其中,VisitorFunc是一个匿名函数,它接收Info与error信息,Info结构用于存储RESTClient请求的返回结果,而VisitorFunc匿名函数则生成或处理Info结构。

Visitor的设计较为复杂,并非单纯实现了访问者模式,它相当于一个匿名函数集。在Kubernetes源码中,Visitor被设计为可以多层嵌套(即多层匿名函数嵌套,使用一个Visitor嵌套另一个Visitor)。直接阅读Visitor源码,会比较晦涩,为了更好地理解Visitor的工作原理,这里提供了代码示例。Visitor Example代码示例如下:

看完此文,谁还敢说你不会创建k8s资源_第6张图片

看完此文,谁还敢说你不会创建k8s资源_第7张图片

看完此文,谁还敢说你不会创建k8s资源_第8张图片

在Visitor Example代码示例中,定义了Visitor接口,增加了VisitorList对象,该对象相当于多个Visitor匿名函数的集合。另外,增加了3个Visitor的类,分别实现Visit方法,在每一个VisitorFunc执行之前(before)和执行之后(after)分别输出print信息。Visitor Example代码执行结果输出如下:

看完此文,谁还敢说你不会创建k8s资源_第9张图片

通过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中,代码示例如下:

看完此文,谁还敢说你不会创建k8s资源_第10张图片

  • EagerVisitorList:当遍历执行Visitor时,如果遇到错误,则保留错误信息,继续遍历执行下一个Visitor。最后一起返回所有错误。
  • VisitorList:当遍历执行Visitor时,如果遇到错误,则立刻返回。

Kubernetes Visitor中存在多种实现方法,不同实现方法的作用不同

看完此文,谁还敢说你不会创建k8s资源_第11张图片
下面将资源创建(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,分别介绍如下。

  • resource.SetNamespace:设置命名空间(Namespace),确保每个Info对象都有命名空间。
  • resource.RequireNamespace:设置命名空间,并检查资源对象描述文件中提供的命名空间与命令行参数(–namespace)提供的命名空间是否相符,如果不相符则返回错误。
  • resource.RetrieveLazy:如果info.Object为空,则根据info的Namepsace和Name等字段调用Helper获取obj,并更新info的Object字段。

由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信息为空判断创建资源请求执行成功。

你可能感兴趣的:(kubernetes,运维,Go,kubernetes,容器,云原生)