k8s-kubelet进程初始化源码分析

kubelet命令的入口类源码位置如下:

/cmd/kubelet/kubelet.go

入口main函数如下:

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	command := app.NewKubeletCommand(server.SetupSignalHandler())
	logs.InitLogs()
	defer logs.FlushLogs()

	if err := command.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}

可以看到最重要的初始化函数是NewKubeletCommand,该方法的实现在:

/cmd/kubelet/app/server.go

// NewKubeletCommand creates a *cobra.Command object with default parameters
func NewKubeletCommand(stopCh <-chan struct{}) *cobra.Command {
	cleanFlagSet := pflag.NewFlagSet(componentKubelet, pflag.ContinueOnError)
	cleanFlagSet.SetNormalizeFunc(flag.WordSepNormalizeFunc)
	kubeletFlags := options.NewKubeletFlags()
	kubeletConfig, err := options.NewKubeletConfiguration()
	// programmer error
	if err != nil {
		glog.Fatal(err)
	}

	cmd := &cobra.Command{
		Use: componentKubelet,
		Long: `The kubelet is the primary "node agent" that runs on each
node. The kubelet works in terms of a PodSpec. A PodSpec is a YAML or JSON object
that describes a pod. The kubelet takes a set of PodSpecs that are provided through
various mechanisms (primarily through the apiserver) and ensures that the containers
described in those PodSpecs are running and healthy. The kubelet doesn't manage
containers which were not created by Kubernetes.

Other than from an PodSpec from the apiserver, there are three ways that a container
manifest can be provided to the Kubelet.

其中定义了一个非常重要的结构体:KubeletServer

该结构体就是kubelet功能的核心结构体。

// construct a KubeletServer from kubeletFlags and kubeletConfig
			kubeletServer := &options.KubeletServer{
				KubeletFlags:         *kubeletFlags,
				KubeletConfiguration: *kubeletConfig,
			}

然后这里面的两个结构体定义在:/cmd/kubelet/app/options/options.go

type KubeletFlags struct {
	KubeConfig          string
	BootstrapKubeconfig string

	// Insert a probability of random errors during calls to the master.
	ChaosChance float64
	// Crash immediately, rather than eating panics.
	ReallyCrashForTesting bool

	// TODO(mtaufen): It is increasingly looking like nobody actually uses the
	//                Kubelet's runonce mode anymore, so it may be a candidate
	//                for deprecation and removal.
	// If runOnce is true, the Kubelet will check the API server once for pods,
	// run those in addition to the pods specified by static pod files, and exit.
	RunOnce bool

	// enableServer enables the Kubelet's server
	EnableServer bool


其实在这里可以总结出k8s对于这些命令或进程的大致框架是如何的。

比如kubectl,kubelet,kube-scheduler等。

首先是二进制命令的入口源码,一般都在/cmd/对应的命令/对应的命令.go,如下图所示:


比如kubelet命令入口在,/cmd/kubelet/kubelet.go

也就是上面的main函数。

然后main函数中一般包含一个NewkubeletCommand函数,该函数就是初始化的地方。

该函数一般都在和kubelet.go文件同一层的app文件夹下。也就是/cmd/kubelet/app/下,一般名为server.go。


然后在NewkubeletCommand函数中,一般会定义一个结构体,该结构体描述了命令的所有功能。

也就是KubeletServer结构体,一般该结构体都在app/options/options.go文件中定义。



可以总结在kubelet创建和同步Pod实例的整个流程:

1.汇总,先将多个Pod source上过来的Podupdata事件汇聚到一个总的channel上去。

2.初审,分析并过滤掉不符合本节点的podupdate事件,对满足条件的podupdate则生成一个workupdate事件,交给podWorker处理。

3.接待,podWorker对每个Pod的workupdate事件排队,并负责更新cache中的pod状态,而把具体的任务转给kubelet处理(syncpod方法)

4.终审,kubelet对符合条件的Pod进一步审查,例如检查Pod是否有权在本节点运,然后准备工作,包括目录创建,PV创建,Image获取,处理Mirror Pod问题。然后交给DockerManager处理

5.落地:任务抵达DockerManager后,DockerManager分析每个Pod的情况,以决定是重启,新建还是更新。给出分析结果后,就是dockerclient的工作了。


以pod同步流程为例,总结如下:

首先,config.Podconfig创建了一个或多个pod source,在默认情况下创建的是API source,并没有创建新的数据结构,而是使用之前介绍的cache.Reflector结合cache.UndeltaStore,从k8s API server上拉取Pod数据放入内部channal,而内部chachannel收到POd数据会调用podStorage的merge方法实现多个channel数据的合并,产生kubelet.Podupdate消息 并写入Podconfig的汇总channel上,随后PodUpdate消息进入kubelet kernel中进行下一步处理。

kubelet的syncloop方法监听Podconfig的汇总channel,过滤掉不合适的podupdate并把符合条件的放入syncPods方法中,最终为每个符合条件的Pod产生一个kubelet.workUpdate事件并放入Podworker队列汇总,随后调用podworker的managerPodloop方法进行处理,在处理过程中,调用dockermanager的syncpod方法。由此dockermanager接班,在进行必要的Pod周边操作,对于需要重启或更新的容器,DM交给docker.client去执行具体的动作。后者通过调用docker engine的API  server来实现具体功能。

posted on 2018-06-10 15:51  sichenzhao  阅读(318)  评论(0编辑  收藏  举报

导航