目录

kubernetes源码-kubelet 原理和源码分析(一)

kubelet 的启动流程,源码为 kubernetes 的 release-1.22 分支 .

写在前面

kubelet 运行在每个节点之上,功能就是上报 Node 节点信息,管理(创建、销毁)Pod ,单纯从功能上来看,貌似很简单,实际不然。每一个点单独拿出来看,都需要花很多精力去分析,记录起来也需要很大的篇幅。

面对这么多的内容,我们一时间也不知道从何看起,不如就先从 kubelet 启动流程开始看起。

入口函数

我们从入口函数开始,入口位于 cmd/kubelet/app/server.go 。

其中 RunKubelet() 接口下的 startKubelet 即是启动 kubelet 的启动函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableServer bool) {
    // start the kubelet
    go k.Run(podCfg.Updates())

    // start the kubelet server
    if enableServer {
        go k.ListenAndServe(kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth)
    }
    if kubeCfg.ReadOnlyPort > 0 {
        go k.ListenAndServeReadOnly(net.ParseIP(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort))
    }
    if utilfeature.DefaultFeatureGate.Enabled(features.KubeletPodResources) {
        go k.ListenAndServePodResources()
    }
}

跟随 k.Run() 接口我们跳转到 pkg/kubelet/kubelet.go ,这里就是 kubelet 的启动接口代码所在。

 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
// Run starts the kubelet reacting to config updates
func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
    if kl.logServer == nil {
        kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))
    }
    if kl.kubeClient == nil {
        klog.InfoS("No API server defined - no node status update will be sent")
    }

    // Start the cloud provider sync manager
    if kl.cloudResourceSyncManager != nil {
        go kl.cloudResourceSyncManager.Run(wait.NeverStop)
    }

    if err := kl.initializeModules(); err != nil {
        kl.recorder.Eventf(kl.nodeRef, v1.EventTypeWarning, events.KubeletSetupFailed, err.Error())
        klog.ErrorS(err, "Failed to initialize internal modules")
        os.Exit(1)
    }

    // Start volume manager
    go kl.volumeManager.Run(kl.sourcesReady, wait.NeverStop)

    if kl.kubeClient != nil {
        // Start syncing node status immediately, this may set up things the runtime needs to run.
        go wait.Until(kl.syncNodeStatus, kl.nodeStatusUpdateFrequency, wait.NeverStop)
        go kl.fastStatusUpdateOnce()

        // start syncing lease
        go kl.nodeLeaseController.Run(wait.NeverStop)
    }
    go wait.Until(kl.updateRuntimeUp, 5*time.Second, wait.NeverStop)

    // Set up iptables util rules
    if kl.makeIPTablesUtilChains {
        kl.initNetworkUtil()
    }

    // Start component sync loops.
    kl.statusManager.Start()

    // Start syncing RuntimeClasses if enabled.
    if kl.runtimeClassManager != nil {
        kl.runtimeClassManager.Start(wait.NeverStop)
    }

    // Start the pod lifecycle event generator.
    kl.pleg.Start()
    kl.syncLoop(updates, kl)
}

启动流程

  1. StartGarbageCollection() ,启动垃圾回收器,初始化 podWorkers (第一步是在 构造 kubelet 阶段完成,接着才运行 startKubelet )。

  2. cloudResourceSyncManager.Run() ,启动云提供商资源同步管理器,如获取当前节点 ip 列表。

  3. initializeModules() ,初始化模块,这里的模块不依赖容器运行时的运行状态。

    a. metrics.Register ,注册 Prometheus 监控指标。

    b. setupDataDirs ,设置文件目录。

    • a. the root directory .

    • b. the pods directory .

    • c. the plugins directory .

    • d. the pod-resources directory .

    c. ContainerLogsDir ,创建容器日志目录。

    d. imageManager.Start() ,启动镜像管理器。

    e. serverCertificateManager.Start() ,启动证书管理器。

    f. oomWatcher.Start() ,启动 oom 监听器。

    g. resourceAnalyzer.Start() ,启动资源分析管理器。

  4. volumeManager.Run() ,启动存储卷管理器。

  5. syncNodeStatus, 同步节点状态。.

  6. nodeLeaseController.Run() ,同步节点租约。

  7. initializeRuntimeDependentModules() ,初始化运行时所依赖的模块。

    • containerManager.Start() ,启动容器管理器。

    • evictionManager.Start() ,驱逐管理器。

    • containerLogManager.Start() ,容器日志管理器。

    • kl.pluginManager.Run() ,启动插件管理器。

    • kl.shutdownManager.Start() ,启动 shutdown 管理器。

  8. initNetworkUtil ,设置 iptables规则。

  9. statusManager.Start() ,同步 pod 状态,并更新 pod 状态到缓存。

  10. runtimeClassManager.Start() ,启用 RuntimeClasses 管理器,为 Pod 选择合适运行时,检测集群运行时等。

  11. pleg.Start() ,启动 plge ,管理和上报 pod 生命周期。

  12. syncLoop() ,启动主进程,接口 kl.syncLoopIteration() 监听来自 file/apiserver/http 事件源发送过来的事件,并对事件做出对应的同步处理。

    a. 从 configCh 通道获取配置更新事件,事件类型:

    • ADD

    • UPDATE

    • REMOVE

    • RECONCILE

    • DELETE

    b. 从生命周期通道 plegCh 获取事件。

    c. 从 syncCh 通道获取事件,跟踪并更新 Pod 状态,如果 pod 状态发生变化,则触发跟 apiserver 同步最新的 pod 信息。

    d. livenessManager.Updates() 事件。

    e. readinessManager.Updates() 事件。

    f. startupManager.Updates() 事件。

    g. 从 housekeepingCh 通道获取事件。

流程图

光看上面那些文字可能不太好理解,我们可以画个思维导图,来重新帮我们整理一下他们之间的关系。

/kubernetes%E6%BA%90%E7%A0%81-kubelet-%E5%8E%9F%E7%90%86%E5%92%8C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B8%80/Kubelet.png
Kubelet 启动流程

总结

kubelet 也是代码量相当庞大的组件之一,我们光从启动流程我们就能得出这个结论。

光靠我们一个人,一口气吃不下这么大的信息量,我们只能拆分成一个个点去看,搞清楚它的启动流程后,我们就可以先从它是如何创建 pod 这一部分的代码来看。