首頁 > 軟體

Kubernetes kubectl中Pod建立流程原始碼解析

2022-11-27 14:01:54

確立目標

  • 建立pod的全流程入手,瞭解各元件的工作內容,元件主要包括以下

kubectl

kube-apiserver

kube-scheduler

kube-controller

kubelet

  • 理解各個元件之間的相互共同作業,目前是kubectl

先寫一個Pod的Yaml

apiVersion: v1 
kind: Pod 
metadata: 
  name: nginx-pod 
spec:    
  containers:    
  - name: nginx     
    image: nginx:1.8 

部署Pod

kubectl create -f nginx_pod.yaml 
pod/nginx-pod created

提示建立成功

查詢Pod

kubectl get pods  
NAME                     READY   STATUS              RESTARTS   AGE 
nginx-pod                1/1     Running             0          4m22s 

列印出狀態:

  • NAME - nginx-pod就是對應上面 metadata.name
  • READY - 就緒的個數
  • STATUS - 當前的狀態,RUNNING表示執行中
  • RESTARTS - 重啟的次數
  • AGE - 多久之前建立的(執行的時間)

kubectl create 的呼叫邏輯

我們的目標是檢視kubectl create -f nginx_pod.yaml 這個命令是怎麼執行的

Main

在cmd/kubectl中

func main() {
 	// 如果不呼叫rand.Seed,每次重新執行這個main函數,rand下的函數返回值始終一致
	// Seed即隨機的種子,每次用時間戳作為種子,就能保證隨機性
	rand.Seed(time.Now().UnixNano())
        // 建立了kubectl命令的預設引數
	command := cmd.NewDefaultKubectlCommand()
	// TODO: once we switch everything over to Cobra commands, we can go back to calling
	// cliflag.InitFlags() (by removing its pflag.Parse() call). For now, we have to set the
	// normalize func and add the go flag set by hand.
	pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	// cliflag.InitFlags()
        // 紀錄檔的初始化與退出
	logs.InitLogs()
	defer logs.FlushLogs()
        // 執行command
	if err := command.Execute(); err != nil {
		os.Exit(1)
	}
}

Match

// k8s的命令列工具採用了 cobra 庫,具有命令提示等強大功能,比go語言自帶的flag強大很多,可參考 github.com/spf13/cobra
func NewDefaultKubectlCommand() *cobra.Command {
	return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr)
}
func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string, in io.Reader, out, errout io.Writer) *cobra.Command {
  // 初始化NewKubectlCommand,採用標準輸入、輸出、錯誤輸出
	cmd := NewKubectlCommand(in, out, errout)
	if pluginHandler == nil {
		return cmd
	}
	if len(args) > 1 {
    // 這裡為傳入的引數,即 create -f nginx_pod.yaml 部分
		cmdPathPieces := args[1:]
		// 呼叫cobra的Find去匹配args
		if _, _, err := cmd.Find(cmdPathPieces); err != nil {
			if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil {
				fmt.Fprintf(errout, "%vn", err)
				os.Exit(1)
			}
		}
	}
	return cmd
}

Command

程式碼較長,選擇關鍵性的內容進行講解

func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
	warningHandler := rest.NewWarningWriter(err, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(err)})
	warningsAsErrors := false
	// 建立主命令 告訴你kubectl該怎麼用
	cmds := &cobra.Command{
		Use:   "kubectl",
		Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
		Long: templates.LongDesc(`
      kubectl controls the Kubernetes cluster manager.
      Find more information at:
            https://kubernetes.io/docs/reference/kubectl/overview/`),
		Run: runHelp,
		// 初始化後,在執行指令前的勾點
		PersistentPreRunE: func(*cobra.Command, []string) error {
			rest.SetDefaultWarningHandler(warningHandler)
      // 這裡是做pprof效能分析,跳轉到對應程式碼可以看到,我們可以用引數 --profile xxx 來採集效能指標,預設儲存在當前目錄下的profile.pprof中
			return initProfiling()
		},
    // 執行指令後的勾點
		PersistentPostRunE: func(*cobra.Command, []string) error {
      // 儲存pprof效能分析指標
			if err := flushProfiling(); err != nil {
				return err
			}
      // 列印warning條數
			if warningsAsErrors {
				count := warningHandler.WarningCount()
				switch count {
				case 0:
					// no warnings
				case 1:
					return fmt.Errorf("%d warning received", count)
				default:
					return fmt.Errorf("%d warnings received", count)
				}
			}
			return nil
		},
    // bash自動補齊功能,可通過 kubectl completion bash 命令檢視
    // 具體安裝可參考 https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion
		BashCompletionFunction: bashCompletionFunc,
	}
  // 範例化Factory介面,工廠模式
	f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
	// 省略範例化的過程程式碼
  // kubectl定義了7類命令,結合Message和各個子命令的package名來看
	groups := templates.CommandGroups{
		{
      // 1. 初級命令,包括 create/expose/run/set   
			Message: "Basic Commands (Beginner):",
			Commands: []*cobra.Command{
				create.NewCmdCreate(f, ioStreams),
				expose.NewCmdExposeService(f, ioStreams),
				run.NewCmdRun(f, ioStreams),
				set.NewCmdSet(f, ioStreams),
			},
		},
		{
      // 2. 中級命令,包括explain/get/edit/delete
			Message: "Basic Commands (Intermediate):",
			Commands: []*cobra.Command{
				explain.NewCmdExplain("kubectl", f, ioStreams),
				get.NewCmdGet("kubectl", f, ioStreams),
				edit.NewCmdEdit(f, ioStreams),
				delete.NewCmdDelete(f, ioStreams),
			},
		},
		{
      // 3. 部署命令,包括 rollout/scale/autoscale
			Message: "Deploy Commands:",
			Commands: []*cobra.Command{
				rollout.NewCmdRollout(f, ioStreams),
				scale.NewCmdScale(f, ioStreams),
				autoscale.NewCmdAutoscale(f, ioStreams),
			},
		},
		{
      // 4. 叢集管理命令,包括 cerfificate/cluster-info/top/cordon/drain/taint
			Message: "Cluster Management Commands:",
			Commands: []*cobra.Command{
				certificates.NewCmdCertificate(f, ioStreams),
				clusterinfo.NewCmdClusterInfo(f, ioStreams),
				top.NewCmdTop(f, ioStreams),
				drain.NewCmdCordon(f, ioStreams),
				drain.NewCmdUncordon(f, ioStreams),
				drain.NewCmdDrain(f, ioStreams),
				taint.NewCmdTaint(f, ioStreams),
			},
		},
		{
      // 5. 故障排查和偵錯,包括 describe/logs/attach/exec/port-forward/proxy/cp/auth
			Message: "Troubleshooting and Debugging Commands:",
			Commands: []*cobra.Command{
				describe.NewCmdDescribe("kubectl", f, ioStreams),
				logs.NewCmdLogs(f, ioStreams),
				attach.NewCmdAttach(f, ioStreams),
				cmdexec.NewCmdExec(f, ioStreams),
				portforward.NewCmdPortForward(f, ioStreams),
				proxy.NewCmdProxy(f, ioStreams),
				cp.NewCmdCp(f, ioStreams),
				auth.NewCmdAuth(f, ioStreams),
			},
		},
		{
      // 6. 高階命令,包括diff/apply/patch/replace/wait/convert/kustomize
			Message: "Advanced Commands:",
			Commands: []*cobra.Command{
				diff.NewCmdDiff(f, ioStreams),
				apply.NewCmdApply("kubectl", f, ioStreams),
				patch.NewCmdPatch(f, ioStreams),
				replace.NewCmdReplace(f, ioStreams),
				wait.NewCmdWait(f, ioStreams),
				convert.NewCmdConvert(f, ioStreams),
				kustomize.NewCmdKustomize(ioStreams),
			},
		},
		{
      // 7. 設定命令,包括label,annotate,completion
			Message: "Settings Commands:",
			Commands: []*cobra.Command{
				label.NewCmdLabel(f, ioStreams),
				annotate.NewCmdAnnotate("kubectl", f, ioStreams),
				completion.NewCmdCompletion(ioStreams.Out, ""),
			},
		},
	}
	groups.Add(cmds)
	filters := []string{"options"}
	// alpha相關的子命令
	alpha := cmdpkg.NewCmdAlpha(f, ioStreams)
	if !alpha.HasSubCommands() {
		filters = append(filters, alpha.Name())
	}
	templates.ActsAsRootCommand(cmds, filters, groups...)
  // 程式碼補全相關
	for name, completion := range bashCompletionFlags {
		if cmds.Flag(name) != nil {
			if cmds.Flag(name).Annotations == nil {
				cmds.Flag(name).Annotations = map[string][]string{}
			}
			cmds.Flag(name).Annotations[cobra.BashCompCustom] = append(
				cmds.Flag(name).Annotations[cobra.BashCompCustom],
				completion,
			)
		}
	}
  // 新增其餘子命令,包括 alpha/config/plugin/version/api-versions/api-resources/options
	cmds.AddCommand(alpha)
	cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), ioStreams))
	cmds.AddCommand(plugin.NewCmdPlugin(f, ioStreams))
	cmds.AddCommand(version.NewCmdVersion(f, ioStreams))
	cmds.AddCommand(apiresources.NewCmdAPIVersions(f, ioStreams))
	cmds.AddCommand(apiresources.NewCmdAPIResources(f, ioStreams))
	cmds.AddCommand(options.NewCmdOptions(ioStreams.Out))
	return cmds
}

Create

func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
  // create子命令的相關選項
	o := NewCreateOptions(ioStreams)
  // create子命令的相關說明
	cmd := &cobra.Command{
		Use:                   "create -f FILENAME",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Create a resource from a file or from stdin."),
		Long:                  createLong,
		Example:               createExample,
    // 驗證引數並執行
		Run: func(cmd *cobra.Command, args []string) {
			if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
				ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -knn"))
				defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
				defaultRunFunc(cmd, args)
				return
			}
			cmdutil.CheckErr(o.Complete(f, cmd))
			cmdutil.CheckErr(o.ValidateArgs(cmd, args))
      // 核心的執行程式碼邏輯是在這裡的RunCreate
			cmdutil.CheckErr(o.RunCreate(f, cmd))
		},
	}
	o.RecordFlags.AddFlags(cmd)
	usage := "to use to create the resource"
  // 加入檔名選項的flag -f,儲存到o.FilenameOptions.Filenames中,對應上面
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
	cmdutil.AddValidateFlags(cmd)
	cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
	cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
		"Only relevant if --edit=true. Defaults to the line ending native to your platform.")
	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)
	cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
	cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to POST to the server.  Uses the transport specified by the kubeconfig file.")
	cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-create")
	o.PrintFlags.AddFlags(cmd)
	// create的子命令,指定create物件
	cmd.AddCommand(NewCmdCreateNamespace(f, ioStreams))
	cmd.AddCommand(NewCmdCreateQuota(f, ioStreams))
	cmd.AddCommand(NewCmdCreateSecret(f, ioStreams))
	cmd.AddCommand(NewCmdCreateConfigMap(f, ioStreams))
	cmd.AddCommand(NewCmdCreateServiceAccount(f, ioStreams))
	cmd.AddCommand(NewCmdCreateService(f, ioStreams))
	cmd.AddCommand(NewCmdCreateDeployment(f, ioStreams))
	cmd.AddCommand(NewCmdCreateClusterRole(f, ioStreams))
	cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, ioStreams))
	cmd.AddCommand(NewCmdCreateRole(f, ioStreams))
	cmd.AddCommand(NewCmdCreateRoleBinding(f, ioStreams))
	cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, ioStreams))
	cmd.AddCommand(NewCmdCreatePriorityClass(f, ioStreams))
	cmd.AddCommand(NewCmdCreateJob(f, ioStreams))
	cmd.AddCommand(NewCmdCreateCronJob(f, ioStreams))
	return cmd
}

RunCreate

func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
	// f為傳入的Factory,主要是封裝了與kube-apiserver互動使用者端
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
	if err != nil {
		return err
	}
	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
  // 範例化Builder,這塊的邏輯比較複雜,我們先關注檔案部分 這些大部分是給builder設定引數,在Do的時候執行邏輯,返回我們想要的Result
	r := f.NewBuilder().
		Unstructured().
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
  	// 讀取檔案資訊,發現除了支援簡單的本地檔案,也支援標準輸入和http/https協定存取的檔案,儲存為Visitor 
		FilenameParam(enforceNamespace, &o.FilenameOptions).
		LabelSelectorParam(o.Selector).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	count := 0
  // 呼叫visit函數,建立資源
	err = r.Visit(func(info *resource.Info, err error) error {
		// 我們看到的pod建立成功就是在這裡列印的 列印結果 xxxx created 
		return o.PrintObj(info.Object)
	})
	return nil
}

站在前人的肩膀上,向前輩致敬,Respect!

Summary

  • 我們從一個建立pod的過程開始,在cmd/kubectl中找到kubectl的啟動過程,kubernetes的命令列工具了利用spf13/cobra庫,傳入並解析引數,去匹配子命令,設定kubectl的預設引數。
  • NewKubectlCommand中進行初始化,有初始化前後的勾點和七類命令,還實現了Factory,在命令中進入Create,進行驗證引數並執行,把檔名設定到CreateOptions中,進入RunCreate,其中傳入的Factory,封裝了與kube-apiserver互動的使用者端。
  • 根據設定範例化Builder,該函數呼叫了NewBuilder、Schema等一系列函數,這段程式碼所做的事情是將命令列接收到的引數轉化為一個資源的列。它使用了Builder模式的變種,使用獨立的函數做各自的資料初始化工作。函數Schema、ContinueOnError、NamespaceParam、DefaultNamespace、FilenameParam、SelectorParam和Flatten都引入了一個指向Builder結構的指標,執行一些對它的修改,並且將這個結構體返回給呼叫鏈中的下一個方法來執行這些修改。
  • FilenameParam裡面就進行了解析檔案,除了支援簡單的本地檔案,也支援標準輸入和http/https協定存取的檔案,儲存為Visitor,再呼叫Visit返回結果,下一節將介紹Visitor存取者模式是和發請求建立pod的細節是怎麼實現的。

以上就是Kubernetes kubectl中Pod建立流程原始碼解析的詳細內容,更多關於Kubernetes kubectl Pod建立的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com