目录
1、kubelet启动流程
1.1 cmd/kubelet/app/server.go Run方法
1.2 cmd/kubelet/app/server.go run方法
1.3 cmd/kubelet/app/server.go RunKubelet方法
1.4 cmd/kubelet/app/server.go CreateAndInitKubelet方法
1.5 cmd/kubelet/app/server.go startKubelet方法
2、pod状态变化感知流程
2.1 pkg/kubelet/kubelet.go makePodSourceConfig函数
2.2 pkg/kubelet/config/apiserver.go NewSourceApiServer函数
2.3 pkg/kubelet/config/config.go Merge函数
3、pod消费流程
1、kubelet启动流程
函数调用图:
1.1 cmd/kubelet/app/server.go Run方法
initForOS
通过对操作系统的判断,如果是windows系统需要做一些预先的特殊处理;
run
方法即通过传入的kubeDeps
参数开始执行启动操作。
1.2 cmd/kubelet/app/server.go run方法
进入run
方法,开始主要执行对参数的再一次验证,以及新的结构体的初始化。后续开始构建一些重要的客户端,包括eventClient
主要处理事件的上报,与apiserver打交道;heartbeatClient
主要处理心跳操作,与之后的PLEG相关;csiClient
主要与CSI接口相关。配置完成之后,最终进入RunKubelet
方法。
1.3 cmd/kubelet/app/server.go RunKubelet
方法
RunKubelet
方法最重要的方法有两个:CreateAndInitKubelet
和startKubelet
,可以理解为CreateAndInitKubelet
为参数的配置,startKubelet
为最终的启动。
1.4 cmd/kubelet/app/server.go CreateAndInitKubelet
方法
CreateAndInitKubelet
方法通过调用NewMainKubelet
返回Kubelet
结构体。在NewMainKubelet
中,主要的配置有:
1、PodConfig。通过makePodSourceConfig
可以发现kubelet获取Pod的来源有以下途径:静态Pod、静态Pod的URL地址以及kube-apiserver;
2、容器与镜像的GC参数。
3、驱逐Pod策略。
最终通过参数填充Kubelet
结构体,完成kubelet结构体参数的最终配置。
1.5 cmd/kubelet/app/server.go startKubelet
方法
startKubelet
方法内部调用了最终的Run
方法
可以看到,在该方法内,完成的就是最终的kubelet的任务,通过多个goroutine完成。包括以下系列:
1、volumeManager,volume相关管理;
2、syncNodeStatus,定时同步Node状态;
3、updateRuntimeUp,定时更新Runtime状态;
4、syncNetworkUtil,定时同步网络状态;
5、podKiller,定时清理死亡的pod;
6、statusManager,pod状态管理;
7、probeManager,pod探针管理;
8、启动PLEG, 即PodLifecycleEventGenerator,用来记录Pod生命周期中对应的各种事件。
9、syncLoop,最重要的主进程,不停监听外部数据的变化执行pod的相应操作。
至此,kubelet启动过程完成。启动主要完成的任务就是参数的配置和多个任务的启动,通过构造一个循环进程不停监听外部事件的变化,执行对应的pod处理工作,这也就是kubelet所需要负责的任务。
2、pod状态变化感知流程
函数调用图:
2.1 pkg/kubelet/kubelet.go makePodSourceConfig函数
makePodSourceConfig
中多处调用了cfg.Channel,可以发现传入了三类source(file、http、api),也就是表示pod的变更来自于这三类source。
cfg.Channel(kubetypes.FileSource)
cfg.Channel(kubetypes.HTTPSource)
cfg.Channel(kubetypes.ApiserverSource)
只看apiserver这类源相关的代码就好了,道理都是一样的。
调用cfg.channel生成updatechannel,将updatechannel作为参数传给NewSourceApiServer函数,NewSourceApiServer内部将listwatch发现的pods变化写入updatechannel队列中
2.2 pkg/kubelet/config/apiserver.go NewSourceApiServer函数
// NewSourceApiserver creates a config source that watches and pulls from the apiserver.
func NewSourceApiserver(c clientset.Interface, nodeName types.NodeName, updates chan<- interface{}) {
lw := cache.NewListWatchFromClient(c.CoreV1().RESTClient(), "pods", metav1.NamespaceAll, fields.OneTermEqualSelector(api.PodHostField, string(nodeName)))
newSourceApiserverFromLW(lw, updates)
}
可以发现实际上kubelet创建了一个listwatch去watch所有namespace的、绑定到本node上的pod。将listwatch发现的pods变化写入updatechannel队列中
2.3 pkg/kubelet/config/config.go Merge函数
看注释可以了解到:
- pod的变化有多个来源
- 最终会将变化push到update channel中
稍微浏览下代码即可发现:merge()将入参的change解析分类,然后又push到podStorage的updates中。
3、pod消费流程
makePodSourceConfig的返回类型PodConfig包含updates channel,
赋值给了kubeDeps.PodConfig
,我们只要找到谁消费了kubeDeps.PodConfig
就能知道pod的变化最终在哪里消费了。
kubeDeps.PodConfig, err = makePodSourceConfig(kubeCfg, kubeDeps, nodeName, bootstrapCheckpointPath)
根据kubeDeps.PodConfig
的调用关系(使用IDE查看谁调用了PodConfig),发现kubeDeps.PodConfig
是在RunKubelet
中被传给了startKubelet
,然后一路被传到pkg/kubelet/kubelet.go路径下的syncLoop
了。
调用链如下图
syncLoop是处理pod变化的主loop,一旦观察变换就会同步期望状态和运行状态。