• kubectl使用及源码阅读


    概述

    k8s 版本 v1.24.16

    kubectl的职责

    • 1.主要的工作是处理用户提交的东西(包括,命令行参数,yaml文件等)
    • 2.将用户提交的这些东西组织成一个数据结构体
    • 3.再将其发送给 api server

    实践

    文章名链接
    linux k8s 源码编译及单集群测试地址
    k8s源码debug地址

    样例

    cat test/fixtures/doc-yaml/user-guide/pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    字段名含义
    kindPod
    metadata.name
    spec.containers.name
    spec.containers.imageimage名称:版本

    对象规约(Spec) 与状态 (Status)

    • 几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置:对象 spec (规约) 和对象 status (状态)
    • 对于具有 spec 的对象,必须在创建对象时设置具体内容,描述你希望对象所具有的特征:期望状态 (Desired State)

    yaml 中的必须字段

    • 在想要创建的 Kubernetes 对象对应的 yaml 文件中,需要配置如下的字段:
      • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
      • kind - 想要创建的对象的类别
      • metadata - 帮助唯一标识对象的一些数据,包括一个 name 字符串、UID和可选的 namespace
    [root@test kubernetes]# ./cluster/kubectl.sh get pods
    NAME    READY   STATUS    RESTARTS   AGE
    nginx   1/1     Running   0          89s
    
    • 1
    • 2
    • 3
    字段名含义
    NAMEnginx就是对应yaml中metadata.name
    READY就绪个数
    STATUS当前的状态,RUNNING表示运行中
    RESTARTS重启的次数,代表没有重启过
    AGE运行的时长

    kubectl 代码原理

    • 1.从命令行和 yaml 文件中获取信息
    • 2.通过 Builder 模式并将其转成一系列的资源
    • 3.最后用 Visitor 模式来迭代处理这些 Resources

    源码位置
    在这里插入图片描述

    kubectl 命令行设置pprof 抓取火焰图

    kubectl 中的 cobra
    • 底层函数 NewKubectlCommand 解析
    • 在 PersistentPreRunE 设置 prrof 采集相关指令
    • 在 PersistentPostRunE 设置了 pprof 统计结果落盘
    • 执行采集 pprof cpu 的 kubelet 命令
    kubectl.go --> command := cmd.NewDefaultKubectlCommand()
    	cmd.go --> return NewDefaultKubectlCommandWithArgs(KubectlOptions{ -->cmd := NewKubectlCommand(o)
    
    • 1
    • 2
    # cpu.pprof 文件在当前命令执行目录下
    kubectl get node --profile=cpu --profile-output=cpu.pprof
    # ll
    # 使用 go 工具将 pprof 转换成 svg  火焰图
    go tool pprof -svg cpu.pprof > kubectl_get_node_cpu.svg
    # 下载下来,在浏览器打开
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    七大分组命令

    kubectl.go --> command := cmd.NewDefaultKubectlCommand()
    	cmd.go --> var defaultConfigFlags = genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0) --> f := cmdutil.NewFactory(matchVersionKubeConfigFlags) --> 	proxyCmd := proxy.NewCmdProxy(f, o.IOStreams)
    	proxyCmd.PreRun = func(cmd *cobra.Command, args []string) {
    		kubeConfigFlags.WrapConfigFn = nil
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    groups := templates.CommandGroups{
    	{
    		Message: "Basic Commands (Beginner):",
    		Commands: []*cobra.Command{
    			create.NewCmdCreate(f, o.IOStreams),
    			expose.NewCmdExposeService(f, o.IOStreams),
    			run.NewCmdRun(f, o.IOStreams),
    			set.NewCmdSet(f, o.IOStreams),
    		},
    	},
    	{
    		Message: "Basic Commands (Intermediate):",
    		Commands: []*cobra.Command{
    			explain.NewCmdExplain("kubectl", f, o.IOStreams),
    			getCmd,
    			edit.NewCmdEdit(f, o.IOStreams),
    			delete.NewCmdDelete(f, o.IOStreams),
    		},
    	},
    	{
    		Message: "Deploy Commands:",
    		Commands: []*cobra.Command{
    			rollout.NewCmdRollout(f, o.IOStreams),
    			scale.NewCmdScale(f, o.IOStreams),
    			autoscale.NewCmdAutoscale(f, o.IOStreams),
    		},
    	},
    	{
    		Message: "Cluster Management Commands:",
    		Commands: []*cobra.Command{
    			certificates.NewCmdCertificate(f, o.IOStreams),
    			clusterinfo.NewCmdClusterInfo(f, o.IOStreams),
    			top.NewCmdTop(f, o.IOStreams),
    			drain.NewCmdCordon(f, o.IOStreams),
    			drain.NewCmdUncordon(f, o.IOStreams),
    			drain.NewCmdDrain(f, o.IOStreams),
    			taint.NewCmdTaint(f, o.IOStreams),
    		},
    	},
    	{
    		Message: "Troubleshooting and Debugging Commands:",
    		Commands: []*cobra.Command{
    			describe.NewCmdDescribe("kubectl", f, o.IOStreams),
    			logs.NewCmdLogs(f, o.IOStreams),
    			attach.NewCmdAttach(f, o.IOStreams),
    			cmdexec.NewCmdExec(f, o.IOStreams),
    			portforward.NewCmdPortForward(f, o.IOStreams),
    			proxyCmd,
    			cp.NewCmdCp(f, o.IOStreams),
    			auth.NewCmdAuth(f, o.IOStreams),
    			debug.NewCmdDebug(f, o.IOStreams),
    		},
    	},
    	{
    		Message: "Advanced Commands:",
    		Commands: []*cobra.Command{
    			diff.NewCmdDiff(f, o.IOStreams),
    			apply.NewCmdApply("kubectl", f, o.IOStreams),
    			patch.NewCmdPatch(f, o.IOStreams),
    			replace.NewCmdReplace(f, o.IOStreams),
    			wait.NewCmdWait(f, o.IOStreams),
    			kustomize.NewCmdKustomize(o.IOStreams),
    		},
    	},
    	{
    		Message: "Settings Commands:",
    		Commands: []*cobra.Command{
    			label.NewCmdLabel(f, o.IOStreams),
    			annotate.NewCmdAnnotate("kubectl", f, o.IOStreams),
    			completion.NewCmdCompletion(o.IOStreams.Out, ""),
    		},
    	},
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    kubectl create

    // 进入口
    create.NewCmdCreate(f, o.IOStreams)
    
    // 核心的cmd.Run函数
    // 校验文件参数
    if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
    	ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -k\n\n"))
    	defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
    	defaultRunFunc(cmd, args)
    	return
    }
    
    // 完善并填充所需字段 
    cmdutil.CheckErr(o.Complete(f, cmd))
    // 校验参数
    cmdutil.CheckErr(o.ValidateArgs(cmd, args))
    // 核心的RunCreate
    // 发送请求与 api server 通信
    cmdutil.CheckErr(o.RunCreate(f, cmd))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    createCmd中的builder模式

    createCmd中的builder建造者设计模式

    // 快速定位代码
    cmdutil.CheckErr(o.RunCreate(f, cmd)) -->
    r := f.NewBuilder().
    		Unstructured().
    		Schema(schema).
    		ContinueOnError().
    		NamespaceParam(cmdNamespace).DefaultNamespace().
    		FilenameParam(enforceNamespace, &o.FilenameOptions).
    		LabelSelectorParam(o.Selector).
    		Flatten().
    		Do()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    createCmd中的visitor访问者模式

    • 访问都模式(Visitor Pattern) 是一种将数据结构与数据操作分离的设计模式
    • 指封装一些作用于某种数据结构中的各元素的操作
    • 可以在不改变数据结构的前提下定义作用于这些元素的新的操作
    • 属于行为型设计模式

    使用场景:

    • 数据结构稳定,作用于数据结构的操作经常变化的场景
    • 需要数据结构与数据操作分享的场景
    • 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景
    // 快速定位代码
    cmdutil.CheckErr(o.RunCreate(f, cmd)) --> err = r.Visit(func(info *resource.Info, err error) error {
    
    // 注意 Visit 是由 Builder 中 FilenameParam 构建的
    r := f.NewBuilder().
    		...
    		// 构建 Visit
    		FilenameParam(enforceNamespace, &o.FilenameOptions).
    		...
    		// 创建 Visit
    		Do()
    // 定位
    FilenameParam --> b.Path(recursive, matches...) --> visitors, err := ExpandPathsToFileVisitors(b.mapper, p, recursive, FileExtensions, b.schema) -->
    		visitor := &FileVisitor{
    			Path:          path,
    			StreamVisitor: NewStreamVisitor(nil, mapper, path, schema),
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    FilenameParam --> b.Path(recursive, matches…) 位置
    在这里插入图片描述

    // NewStreamVisitor is a helper function that is useful when we want to change the fields of the struct but keep calls the same.
    func NewStreamVisitor(r io.Reader, mapper *mapper, source string, schema ContentValidator) *StreamVisitor {
    	return &StreamVisitor{
    		Reader: r,
    		mapper: mapper,
    		Source: source,
    		Schema: schema,
    	}
    }
    
    // 解析 yaml 或者 json 配置
    // Visit implements Visitor over a stream. StreamVisitor is able to distinct multiple resources in one stream.
    func (v *StreamVisitor) Visit(fn VisitorFunc) error {
    	d := yaml.NewYAMLOrJSONDecoder(v.Reader, 4096)
    	for {
    		ext := runtime.RawExtension{}
    		if err := d.Decode(&ext); err != nil {
    			if err == io.EOF {
    				return nil
    			}
    			return fmt.Errorf("error parsing %s: %v", v.Source, err)
    		}
    		// TODO: This needs to be able to handle object in other encodings and schemas.
    		ext.Raw = bytes.TrimSpace(ext.Raw)
    		if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
    			continue
    		}
    		if err := ValidateSchema(ext.Raw, v.Schema); err != nil {
    			return fmt.Errorf("error validating %q: %v", v.Source, err)
    		}
    		info, err := v.infoForData(ext.Raw, v.Source)
    		if err != nil {
    			if fnErr := fn(info, err); fnErr != nil {
    				return fnErr
    			}
    			continue
    		}
    		if err := fn(info, nil); err != nil {
    			return err
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    // 上面方法中,重点如下代码
    info, err := v.infoForData(ext.Raw, v.Source)
    
    // obj 代表 k8s的对象
    // gvk 代表 Group/Version/Kind 的缩写
    obj, gvk, err := m.decoder.Decode(data, nil, nil)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    外层VisitorFunc分析

    • 如查出错即返回错误
    • DryRunStrategy 代表试运行策略
      • 默认为 None 代表不试运行
      • client 代表客户端试运行,不发送请求至 server
      • server 点服务端试运行,发送请求,但是如果会改变状态的话就不做
    err = r.Visit(func(info *resource.Info, err error) error { --> if o.DryRunStrategy != cmdutil.DryRunClient {  --> Create(info.Namespace, true, info.Object)(最终创建资源)
    
    • 1

    结束

    kubectl使用及进阶 至此结束。

  • 相关阅读:
    OneNote 教程,如何在 OneNote 中使用绘图和批注?
    华为设备攻击防范配置命令
    JVM中的java同步互斥工具应用演示及设计分析
    后缀数组 - 应用
    JVM调优
    Spring Cloud Netflix之OpenFeign
    还在用Swagger?我推荐这款零代码侵入的接口管理神器
    【含面试题】MySQL表字段值转换为小写(或大写)的java代码实现
    中秋节——嫦娥奔月
    815 - Flooded! (UVA)
  • 原文地址:https://blog.csdn.net/2301_79691134/article/details/136172093