经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Kubernetes » 查看文章
Kubernetes: Kubectl 源码分析
来源:cnblogs  作者:lubanseven  时间:2023/8/2 9:17:58  对本文有异议

0. 前言

kubectl 看了也有一段时间,期间写了两篇设计模式的文章,是时候对 kubectl 做个回顾了。

1. kubectl 入口:Cobra

kubectlkubernetes 的命令行工具,通过 kubectl 实现资源的增删改查。kubectl 通过 client-gokube-apiserver 进行交互,其背后封装了 https,配置文件为 kubeconfig

kubectl 的命令行框架为 Cobra。首先,将外部参数,配置统统赋给 KubectlOptions 对象:

  1. // NewDefaultKubectlCommand creates the `kubectl` command with default arguments
  2. func NewDefaultKubectlCommand() *cobra.Command {
  3. return NewDefaultKubectlCommandWithArgs(KubectlOptions{
  4. PluginHandler: NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes),
  5. Arguments: os.Args,
  6. ConfigFlags: defaultConfigFlags,
  7. IOStreams: genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr},
  8. })
  9. }

该对象包含四个属性:

  • PluginHandler: PluginHandler is capable of parsing command line arguments and performing executable filename lookups to search for valid plugin files, and execute found plugins.
  • Arguments: os.Args;
  • ConfigFlags: ConfigFlags composes the set of values necessary for obtaining a REST client config;
  • IOStreams: IOStreams provides the standard names for iostreams. This is useful for embedding and for unit testing. Inconsistent and different names make it hard to read and review code;

接着通过 ConfigFlags 属性创建工厂,工厂提供了与 kube-apiserver 的交互方式,以及验证资源对象等方法:

  1. kubeConfigFlags := o.ConfigFlags
  2. if kubeConfigFlags == nil {
  3. kubeConfigFlags = defaultConfigFlags
  4. }
  5. kubeConfigFlags.AddFlags(flags)
  6. matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
  7. matchVersionKubeConfigFlags.AddFlags(flags)
  8. f := cmdutil.NewFactory(matchVersionKubeConfigFlags)

1.1 创建命令

这里以创建 get 为例 getCmd := get.NewCmdGet("kubectl", f, o.IOStreams),工厂 fIOStreams 作为参数传给 get 包的 NewCmdGet 函数,在函数内实现 get 命令的创建。

创建 GetOptions 对象,该对象包含和 get 命令相关的输入。

  1. func NewCmdGet(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
  2. o := NewGetOptions(parent, streams)
  3. cmd := &cobra.Command{
  4. ...
  5. Run: func(cmd *cobra.Command, args []string) {
  6. cmdutil.CheckErr(o.Complete(f, cmd, args))
  7. cmdutil.CheckErr(o.Validate())
  8. cmdutil.CheckErr(o.Run(f, args))
  9. },
  10. SuggestFor: []string{"list", "ps"},
  11. }
  12. ...

CobraRun 函数实现运行 get 命令的行为。

首先,o.Complete(f, cmd, args) 补全 GetOptions 对象的输入:

  1. func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  2. var err error
  3. o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace()
  4. if err != nil {
  5. return err
  6. }
  7. ...

需要注意的是,f.ToRawKubeConfigLoader().Namespace() 调用工厂的 ToRawKubeConfigLoader() 方法解析 kubeconfig 中的配置,然后调用 Namespace() 方法将 kubeconfig 中定义的 namespace 解析出来,解析 kubeconfig 的过程是反序列化 kubeconfig 文件的过程。这一过程太长,这里就不多做介绍了。

完成了输入补全,在 o.Validate() 中对输入做验证。最后,通过 o.Run(f, args) 运行命令:

  1. func (o *GetOptions) Run(f cmdutil.Factory, args []string) error {
  2. ...
  3. r := f.NewBuilder().
  4. Unstructured().
  5. NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
  6. FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
  7. LabelSelectorParam(o.LabelSelector).
  8. FieldSelectorParam(o.FieldSelector).
  9. Subresource(o.Subresource).
  10. RequestChunksOf(chunkSize).
  11. ResourceTypeOrNameArgs(true, args...).
  12. ContinueOnError().
  13. Latest().
  14. Flatten().
  15. TransformRequests(o.transformRequests).
  16. Do()
  17. ...

这里涉及到 建造者设计模式。通过 f 创建建造者,建造者通过一系列方法补全自身属性,在 Do 方法中根据这些属性建造 resource.Result 对象:

  1. func (b *Builder) Do() *Result {
  2. r := b.visitorResult()
  3. ...
  4. return r
  5. }

Do 方法值得重点关注,其实现了 访问者设计模式,且是嵌套的访问者,访问的对象为 info 结构体。

首先,b.visitorResult() 方法通过 visit 多个 item 创建 resource.Result。这里以 visit resource name 为例:

  1. func (b *Builder) visitByName() *Result {
  2. result := &Result{
  3. singleItemImplied: len(b.names) == 1,
  4. targetsSingleItems: true,
  5. }
  6. client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
  7. if err != nil {
  8. result.err = err
  9. return result
  10. }
  11. ...
  12. visitors := []Visitor{}
  13. for _, name := range b.names {
  14. info := &Info{
  15. Client: client,
  16. Mapping: mapping,
  17. Namespace: selectorNamespace,
  18. Name: name,
  19. Subresource: b.subresource,
  20. }
  21. visitors = append(visitors, info)
  22. }
  23. result.visitor = VisitorList(visitors)
  24. result.sources = visitors
  25. return result
  26. }

visitByName() 方法内创建了一组 info 对象,其中保存了 resource 的信息。该对象保存在存储访问者 Visitorvisitors 列表,并赋值给 result.visitorresult.sources

关于 result.visitor 要注意的一点是,其中的 VisitorList 也实现了 Visit 方法,它是横向的调用 info, info 是主体,fn 是这里的访问者:

  1. type VisitorList []Visitor
  2. // Visit implements Visitor
  3. func (l VisitorList) Visit(fn VisitorFunc) error {
  4. for i := range l {
  5. if err := l[i].Visit(fn); err != nil {
  6. return err
  7. }
  8. }
  9. return nil
  10. }

得到 resource.Result 之后,通过各个访问者访问 info 资源:

  1. func (b *Builder) Do() *Result {
  2. r := b.visitorResult()
  3. if b.flatten {
  4. r.visitor = NewFlattenListVisitor(r.visitor, b.objectTyper, b.mapper)
  5. }
  6. helpers := []VisitorFunc{}
  7. if b.defaultNamespace {
  8. helpers = append(helpers, SetNamespace(b.namespace))
  9. }
  10. if b.requireNamespace {
  11. helpers = append(helpers, RequireNamespace(b.namespace))
  12. }
  13. helpers = append(helpers, FilterNamespace)
  14. if b.requireObject {
  15. helpers = append(helpers, RetrieveLazy)
  16. }
  17. if b.continueOnError {
  18. r.visitor = ContinueOnErrorVisitor{Visitor: r.visitor}
  19. }
  20. r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
  21. return r
  22. }

其中,FlattenListVisitor, ContinueOnErrorVisitorDecoratedVisitor 是纵向的访问者嵌套关系,SetNamespace, RequireNamespaceRetrieveLazy 是横向的嵌套关系。

这里关于访问者模式和访问者嵌套的调用顺序就不过多介绍,有兴趣的话可以参考 浅析访问者模式

Do 方法返回 Result,接着调用 infos, err := r.Infos() 方法实现 resource 的访问:

  1. func (r *Result) Infos() ([]*Info, error) {
  2. ...
  3. infos := []*Info{}
  4. err := r.visitor.Visit(func(info *Info, err error) error {
  5. if err != nil {
  6. return err
  7. }
  8. infos = append(infos, info)
  9. return nil
  10. })
  11. return infos, err
  12. }

这里 infos 是一组 info 对象访问 kube-apiserver 获得的返回结果集合。那么,哪里有定义访问 kube-apiserver 的地方呢?

答案在 RetrieveLazy 访问者:

  1. func RetrieveLazy(info *Info, err error) error {
  2. if err != nil {
  3. return err
  4. }
  5. if info.Object == nil {
  6. return info.Get()
  7. }
  8. return nil
  9. }
  10. // Get retrieves the object from the Namespace and Name fields
  11. func (i *Info) Get() (err error) {
  12. obj, err := NewHelper(i.Client, i.Mapping).WithSubresource(i.Subresource).Get(i.Namespace, i.Name)
  13. if err != nil {
  14. if errors.IsNotFound(err) && len(i.Namespace) > 0 && i.Namespace != metav1.NamespaceDefault && i.Namespace != metav1.NamespaceAll {
  15. err2 := i.Client.Get().AbsPath("api", "v1", "namespaces", i.Namespace).Do(context.TODO()).Error()
  16. if err2 != nil && errors.IsNotFound(err2) {
  17. return err2
  18. }
  19. }
  20. return err
  21. }
  22. i.Object = obj
  23. i.ResourceVersion, _ = metadataAccessor.ResourceVersion(obj)
  24. return nil
  25. }
  26. func (m *Helper) Get(namespace, name string) (runtime.Object, error) {
  27. req := m.RESTClient.Get().
  28. NamespaceIfScoped(namespace, m.NamespaceScoped).
  29. Resource(m.Resource).
  30. Name(name).
  31. SubResource(m.Subresource)
  32. return req.Do(context.TODO()).Get()
  33. }

RetrieveLazy 中定义如果 info.Object 没有信息,则调用 infoGet 方法,在 Get 方法中根据 i.Clienti.Mapping 创建 Helper,通过 HelperGet 方法通过 client-go 实现同 kube-apiserver 的交互,获得 info 的资源信息。

1.2 UML 交互图

通过上例分析给出 UML 交互图如下:

image


原文链接:https://www.cnblogs.com/xingzheanan/p/17599502.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号