k8s的架构是用户使用kubectl工具对虚拟机资源进行各种各样的控制和定制。
而kubectl本身并不包含对其核心资源的访问与控制。而是通过http通信与api-server进行交互实现资源的管理。
而api-server的核心其实就是etcd数据库,它将各种资源的管理通过对etcd中的数据进行更改实现。这篇文章简要分析一下kubectl对
api-server发起访问的过程。
以kubectl create 指令为例,其指令的相关代码在kubernetes/pkg/kubectl/cmd/create.go这个文件中。
一,参数的构造
稍加分析不难看出,其创建资源的核心函数是func RunCreate()。
r := builder. Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &options.FilenameOptions). SelectorParam(options.Selector). Flatten(). Do()
这段代码调用了builder的一串函数,最后生成一个Result类型的变量r。(Result 定义在 kubernetes/pkg/kubectl/resource/result.go)
这里builder的作用是将之前解析得到的各种参数进行各种分析后填充到builder中,最后再生成一个Result类型的变量r。
此时r中最关键的数据是visitor,这个visitor中包含了多个资源,也就是说一个visitor包含了一个对象(此处存疑)。
接着以一个函数func为参数调用r的Visit函数。经过分析我们可以知道这是一个调用链函数,其中继续对参数进行各种分析,处理。
r.Visit(func(info *resource.Info, err error) error {
.....
})
大致的调用顺序为
Result.Visit()
ContinueOnError.Visit()
Decorated.Visit()
FlattenList.Visit()
EagerVisitorList.Visit()
FileVisitor.Visit()
StreamVisitor.Visit()
在StreamVisitor之前,都是对数据的各种处理,并不会调用func函数,在StreamVisitor.Visit()中一个关键的处理是生成一个临时变量Info。
info, err := v.InfoForData(ext.Raw, v.Source)
这里的ext.Raw和v.Source都是之前对用户的输入进行解析的结果。
ext.Raw是byte[]类型,v.Source是String类型,打印出来:
ext.Raw={
"apiVersion":"v1",
"kind":"Pod",
"metadata":
{"name":"with-node-affinity-2"},
"spec":
{"affinity":
{"nodeAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"preference":{"matchExpressions":[{"key":"another-node-label-key","operator":"In","values":["another-node-label-value"]}]},
"weight":1}],
"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"kubernetes.io/e2e-az-name","operator":"In","values":["e2e-az1","e2e-az2"]}]}]}}},
"containers":[{"image":"gcr.io/google_containers/pause:2.0","name":"with-node-affinity"}]}},
v.Source=/home/wlh/mywork/yaml_file/pod-with-node-affinity.yaml
可以看到其中存储了生成的资源的各种特性,有了这个info之后执行func(info)。
二,访问api-server
得到了info这个数据结构后,调用func()函数对api-server发起访问请求。
if !dryRun { if err := createAndRefresh(info); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } }
这个dryRun标志位的意思是,只打印出将要创建的资源的各种特性,而不会真正去创建他。
而createAndRefresh函数中的核心调用是
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
这里创建了一个Helper类型的对象,仔细分析后不难发现Info.Client中定义了访问动作,而info.Mapping中定义了这些动作所要访问的资源。
此时Helper中已经包含了发起一个对api-server请求的所有资源,接着调用Create函数,它创建一个具体去执行这些请求的Request对象。(定义在kubernetes/vendor/k8s.io/client-go/rest/request.go中)
Requeste发起对api-server的请求,并且将访问存储在ResutInfo中,与此同时返回一个Runtime.Object对象。
这样一次完整的访问api-server的流程就完成了。