当前位置: 首页 > news >正文

k8s源码分析——kubectl命令行交互

Cobra库

k8s各组件的cli部分都使用Cobra库实现,Cobra 中文文档 - 掘金 (juejin.cn),获取方式如下:

go get -u github.com/spf13/cobra@latest

cobra库中的Command结构体的字段,用于定义命令行工具的行为和选项。它们的作用如下:

    Use: 命令名称。Aliases: 命令的别名。SuggestFor: 命令建议使用的单词列表。Short: 命令简短描述。GroupID: 命令所属的命令组。Long: 命令详细描述。Example: 命令的使用示例。ValidArgs: 命令接受的参数列表。ValidArgsFunction: 命令用于提供动态参数补全的函数。Args: 命令的位置参数列表。ArgAliases: 位置参数的别名。BashCompletionFunction: 生成Bash补全的函数。Deprecated: 命令是否已经过时的标志。Annotations: 命令的附加注释信息。Version: 命令版本号。PersistentPreRun: 每次执行该命令之前都会执行的函数。PersistentPreRunE: 每次执行该命令之前都会执行的返回错误的函数。PreRun: 每次执行该命令之前都会执行的函数。PreRunE: 每次执行该命令之前都会执行的返回错误的函数。Run: 执行命令的函数。RunE: 执行命令的返回错误的函数,RunE与Run的差别是,RunE执行有错误会直接return,Run无论是否有错误,都会继续执行后面PostRun和PersistentPostRun等逻辑。PostRun: 每次执行该命令之后都会执行的函数。PostRunE: 每次执行该命令之后都会执行的返回错误的函数。PersistentPostRun: 每次执行该命令之后都会执行的函数。PersistentPostRunE: 每次执行该命令之后都会执行的返回错误的函数。FParseErrWhitelist : 忽略特定的解析错误CompletionOptions :控制 shell 自动完成的选项TraverseChildren: 解析父命令的标志后再执行子命令Hidden : 隐藏命令,不在可用命令列表中显示SilenceErrors : 静默下游错误SilenceUsage : 静默错误时不显示用法DisableFlagParsing : 禁用标志解析DisableAutoGenTag : 禁用自动生成的标记DisableFlagsInUseLine : 在打印帮助或生成文档时禁用“[flags]”在用法行中的添加DisableSuggestions : 禁用基于Levenshtein距离的建议SuggestionsMinimumDistance : 显示建议的最小Levenshtein距离

kubectl执行流程

创建cobra.Command对象

    主要流程在cmd.NewDefaultKubectlCommand()中,构建KubectlOptions对象(Kubectl Command的配置对象),指定插件、命令行参数、通用配置和输入输出流,然后调用NewDefaultKubectlCommandWithArgs函数创建Command对象,NewDefaultKubectlCommandWithArgs中调用NewKubectlCommand构建Command对象。

  • 创建&cobra.Command{实例,指定Run函数(执行cmd.help)。指定PersistentPreRunE函数,在Run前进行初始化。指定PersistentPostRunE函数,在Run后执行,将配置写入到磁盘。
  • 为所有的子命令和flag设置规范化函数cliflag.WarnWordSepNormalizeFunc,当参数中包含 "_" 时,会将参数中的 "_" 替换为 "-",同时提示警告信息。
  • addProfilingFlags(flags):增加性能调优的参数开关,统计CPU,内存等相关信息,用于性能优化
  • 添加bool类型全局标志warnings-as-errors,默认值为false
  • 创建ConfigFlags对象,设置命令参数,将参数解析值绑定到kubeConfigFlags
  • 添加一个是否匹配client与server版本的参数match-server-version
  • addCmdHeaderHooks(cmds, kubeConfigFlags):为为rest client 增加HTTP Header,依照SIG CLI KEP 859标准
  • 将kubeconfig对象包装成一个Factory类型,Factory是一个通用对象,它提供了与kube-apiserver的交互方式,以及验证资源对象等方法。 Factory接口封装了 DynamicClient、KubernetesClientSet(简称ClientSet)及RESTClient 3种client-go客户端与kube-apiserver交互的方式。
  • 添加所有的子命令,将所有命令存放在不同的group数组中,然后groups.Add(cmds)将所有的子命令添加,groups.Add函数中会调用cobra库中的AddCommand方法添加子命令。
  • 添加其他子命令。
  • 返回command对象

Command对象的执行

  创建了cobra.Command对象后,调用Kubectl封装的RunNoErrOutput方法,进入Command对象的执行。

  • 设置全局规范化参数cliflag.WordSepNormalizeFunc,将参数中的 "_" 替换为 "-"。
  • flag解析错误打印设置,如果有错误不打印使用方法
  • 日志相关设置
  • 调用 cmd.Execute() 函数执行command

  每个子命令的主要处理逻辑(cobra.Command.Run函数)Complete、Validate 和 Run三个函数,其中 complete() 函数中会将命令行参数整理对命令行options进行初始化,设置一些默认值;Validate() 函数会对options中的选项进行检查,打印相应的错误提示信息;Run()函数中执行各子命令的主要处理流程。

创建资源对象的过程(kubectl create -f FILENAME)

image

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

  • Factory是一个通用对象,它提供了与kube-apiserver的交互方式,以及验证资源对象等方法。 Factory接口封装了 DynamicClient、KubernetesClientSet(简称ClientSet)及RESTClient 3种client-go客户端与kube-apiserver交互的方式。
  • Builder用于将命令行获取的参数转换成资源对象(Resource Object)。它实现了一种通用的资源对象转换功能。
  • Kubernetes Visitor中存在多种实现方法, 不同实现方法的作用不同,如下:

image

 RunCreate()函数流程

  • raw参数处理。
  • 首先通过f.NewBuilder()实例化Builder对象, 通过函数Unstructured()、 Schema()、 ContinueOnError()、NamespaceParam()、 FilenameParam()、LabelSelectorParam()、 Flatten()对参数赋值和初始化, 将参数保存到Builder对象中。 最后通过Do()函数生成最终的rusult对象,设置rusult.visitor。
 1    // 实例化builder对象 r := f.NewBuilder().
 2     // 以map的方式传输数据对象,对响应内容中的数据做一层封装,这样就可以保留所有字段而不需要首先解析成一个struct
 3     Unstructured().
 4     //
 5     Schema(schema).
 6     // 配置result对象在出现错误的行为,意思很明显,在出错后继续
 7     ContinueOnError().
 8     // 基于命令行参数设置查询的namespace
 9     NamespaceParam(cmdNamespace).DefaultNamespace().
10     // 解析文件名参数 参数 -f,文件名被存放在 b.paths 中
11     FilenameParam(enforceNamespace,&o.FilenameOptions).
12     // 解析标签选择器 参数 -l
13     LabelSelectorParam(o.Selector). 
14     // 将对象展开,比如对象是[a, b], 如果没有flatten就是完成访问[a,b]作为一个整体,反之, 让外层函数分别访问a,b
15     Flatten().
16     // 基于之前的配置,生成最终的result对象
17 Do()

  • Do函数中设置rusult.visitor多层匿名函数嵌套关系如下:
 1 result.Visitor = DecoratedVisitor {    // 在函数Do函数中通过NewDecoratedVisitor函数执设置,并且注册了SetNamespace、RequireNamespace、FilterNamespace、RetrieveLazy等修饰函数。
 2     visitor: ContinueOnErrorVisitor {    // 在函数Do函数中,如果b.continueOnError为真设置,b.continueOnError在函数ContinueOnError()设置。
 3         visitor: FlattenListVisitor {    // 在函数Do函数中设置,这个感觉有点多余,在后面的流程中还会设置一个FlattenListVisitor。
 4             visitor: FilteredVisitor {    // 在函数visitByPaths中,含有Selector时设置,对每个对象对应的info对象进行检查,检查函数为FilterByLabelSelector(selector)。
 5                 visitor: FlattenListVisitor {    // 在函数visitByPaths中,b.flatten为真时设置,b.flatten在上面的Flatten()函数中设为true。
 6                     Visitor: EagerVisitorList {    // 在函数visitByPaths中将b.paths强转成EagerVisitorList,调用关系:Do() -> b.visitorResult() -> b.visitByPaths()。
 7                         []b.paths FileVisitor {    // 每个文件对应一个FileVisitor,所有的FileVisitor被append到 b.paths 数组中。
 8                             StreamVisitor: StreamVisitor {
 9                                 Reader: r,
10                                 mapper: mapper,
11                                 Source: source,    
12                                 Schema: b.schema,
13                             },
14                         },
15                     },
16                 },
17             },
18         },
19     },
20 } 
  • 执行Result.Visit(),该函数中会按照上面多层嵌套关系执行每一个Visit函数,按顺序 处理逻辑如下:
    • 从 DecoratedVisitor.Visit 一直到 EagerVisitorList.Visit都是在函数开始就直接执行对象的成员visitor的Visit函数,直到FileVisitor.Visit中才是先执行本身的流程,然后再执行对象的成员visitor的Visit函数。
    • FileVisitor:打开xml文件,读取里面的数据到一个io.Reader中,然后执行StreamVisitor.Visit。
    • StreamVisitor:对xml文件中的数据进行解码,然后执行 infoForData 函数将解码后的数据转换成info对象,然后执行 EagerVisitorList定义的VisitorFunc(通过 FileVisitor 转传入)。
    • EagerVisitorList:将所有的err信息收集到一个集合中返回,如果StreamVisitor出现错误直接 return,如果没有错误执行FlattenListVisitor定义的VisitorFunc。
    • FlattenListVisitor:如果yaml文件中包含多个资源对象,将runtime.ObjectTyper解析成多个runtime.Object,再转换为多个Info,逐个调用VisitorFunc,即执行FilteredVisitor定义的VisitFunc函数。
    • FilteredVisitor:对Info进行检验, 进行Selector检查。如果不满足条件,则返回error信息,如果满足条件则执行VisitorFunc,即FlattenListVisitor定义的VisitFunc函数。
    • FlattenListVisitor:因为上面已经执行过一次FlattenListVisitor了,这里会直接执行ContinueOnErrorVisitor定义的VisitorFunc。
    • ContinueOnErrorVisitor:将Visitor调用过程中产生的错误保留在[]error中,然后执行DecoratedVisitor定义的VisitorFunc。
    • DecoratedVisitor:执行注册过的VisitorFunc,然后执行result指定的VisitorFunc。
    • Result指定的VisitFunc:通过Helper.Create向kube-apiserver发送创建资源的请求,然后将与kube-apiserver交互后得到的结果通过info.Refresh函数更新到info.Object中。Helper.Create最终会进入createResource函数
 1 func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
 2     // RESTFUL接口风格中,POST请求对应的就时CREATE方法
 3     return c.Post(). 
 4     NamespaceIfScoped(namespace,m.NamespaceScoped).
 5     Resource(resource).
 6     VersionedParams(options,metav1.ParameterCodec).
 7     Body(obj).
 8     // 发送请求
 9     Do(context.TODO()).
10     // 将请求结果转换成runtime.Object
11     Get()
12 }
 
http://www.wxhsa.cn/company.asp?id=2029

相关文章:

  • 将 seata 2.5 发布到私服
  • 一些感悟
  • 五款免费低代码平台深度横评:斑斑、简道云、宜搭、氚云、织信如何选?
  • ubuntu历史版本下载
  • 读书笔记:数据库索引的智能优化:反向键与降序索引
  • 代码随想录算法训练营第十天| 232.用栈实现队列、 225. 用队列实现栈、20. 有效的括号 、1047. 删除字符串中的所有相邻重复项
  • 零成本搭建企业系统:五款免费低代码平台推荐
  • 故障处理:access$表在数据库丢失的恢复
  • 从需求出发:教你判断选斑斑还是织信
  • PLC结构化文本设计模式——建造者模式(Builder Pattern)
  • C++ - STL - 迭代器
  • MATLAB的智能扫地机器人工作过程仿真
  • linux redis 8.2.1软件开机启动redis.service与etc下的rc.local配置2种方式
  • 在GA中添加Tag-GetDynamicSpecSourceTags().AddTag(NewTag)
  • python如何在函数中使用全局变量?
  • 296、贾生
  • ubuntu 24.04部署mysql8.0.41(glibc2.17)
  • C++ - STL - 键值对pair
  • 第四天学习:LSTM
  • MATLAB的稀疏自编码器实现
  • 题解:P2157 [SDOI2009] 学校食堂
  • LLM 应用开发中的常见模式
  • vue3 与 element-plus
  • 可爱的二维数据结构们
  • 网络安全相关职业
  • 202005_CTFHUB_Redis流量
  • langchain学习之路
  • 通义灵码产品演示: 数据库设计与数据分析
  • win10安装mysql,MySQL5.7详细教程
  • 第二周作业