kubernetes源码分析 kubelet

简介

从官方的架构图中很容易就能找到 kubelet

执行 kubelet -h 看到 kubelet 的功能介绍:

  • kubelet 是每个 Node 节点上都运行的主要“节点代理”。使用如下的一个向 apiserver 注册 Node 节点:主机的 hostname;覆盖 host 的参数;或者云提供商指定的逻辑。

  • kubelet 基于 PodSpec 工作。PodSpec 是用 YAML 或者 JSON 对象来描述 Pod。Kubelet 接受通过各种机制(主要是 apiserver)提供的一组 PodSpec,并确保里面描述的容器良好运行。

除了由 apiserver 提供 PodSpec,还可以通过以下方式提供:

  • 文件

  • HTTP 端点

  • HTTP 服务器

kubelet 功能归纳一下就是上报 Node 节点信息,和管理(创建、销毁)Pod。 功能看似简单,实际不然。每一个点拿出来都需要很大的篇幅来讲,比如 Node 节点的计算资源,除了传统的 CPU、内存、硬盘,还提供扩展来支持类似 GPU 等资源;Pod 不仅仅有容器,还有相关的网络、安全策略等。

架构 

kubelet 的架构由 N 多的组件组成,下面简单介绍下比较重要的几个:

PLEG

Pod Lifecycle Event Generator,字面意思 Pod 生命周期事件(ContainerStartedContainerDiedContainerRemovedContainerChanged)生成器。

其维护着 Pod 缓存;定期通过 ContainerRuntime 获取 Pod 的信息,与缓存中的信息比较,生成如上的事件;将事件写入其维护的通道(channel)中。

PodWorkers

处理事件中 Pod 的同步。核心方法 managePodLoop() 间接调用 kubelet.syncPod() 完成 Pod 的同步:

  • 如果 Pod 正在被创建,记录其延迟

  • 生成 Pod 的 API Status,即 v1.PodStatus:从运行时的 status 转换成 api status

  • 记录 Pod 从 pendingrunning 的耗时

  • StatusManager 中更新 pod 的状态

  • 杀掉不应该运行的 Pod

  • 如果网络插件未就绪,只启动使用了主机网络(host network)的 Pod

  • 如果 static pod 不存在,为其创建镜像(Mirror)Pod

  • 为 Pod 创建文件系统目录:Pod 目录、卷目录、插件目录

  • 使用 VolumeManager 为 Pod 挂载卷

  • 获取 image pull secrets

  • 调用容器运行时(container runtime)的 #SyncPod() 方法

PodManager

存储 Pod 的期望状态,kubelet 服务的不同渠道的 Pod

StatusProvider

提供节点和容器的统计信息,有 cAdvisorCRI 两种实现。

ContainerRuntime

顾名思义,容器运行时。与遵循 CRI 规范的高级容器运行时进行交互。

生命周期事件生成器PLEG

对于 Pod来说,Kubelet 会从多个数据来源(api、file以及http) watch Pod spec 中的变化。对于容器来说,Kubelet 会定期轮询容器运行时,以获取所有容器的最新状态。随着 Pod 和容器数量的增加,轮询会产生较大开销,带来的周期性大量并发请求会导致较高的 CPU 使用率峰值,降低节点性能,从而降低节点的可靠性。为了降低 Pod 的管理开销,提升 Kubelet 的性能和可扩展性,引入了 PLEG(Pod Lifecycle Event Generator)。改进了之前的工作方式:

  1. 减少空闲期间的不必要工作(例如 Pod 的定义和容器的状态没有发生更改)。

  2. 减少获取容器状态的并发请求数量。

为了进一步降低损耗,社区推出了基于event实现的PLEG,当然也需要CRI运行时支持。

它定期检查节点上 Pod 运行情况,如果发现感兴趣的变化,PLEG 就会把这种变化包 装成 Event 发送给 Kubelet 的主同步机制 syncLoop 去处理。

syncLoop

Kubelet启动后通过syncLoop进入到主循环处理Node上Pod Changes事件,监听来自file,apiserver,http三类的事件并汇聚到kubetypes.PodUpdate Channel(Config Channel)中,由syncLoopIteration不断从kubetypes.PodUpdate Channel中消费。

源码分析

源码版本

origin/release-1.30

启动流程

入口函数

入口位于 cmd/kubelet/app/server.go 。

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

startKubelet
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, kubeDeps.TracerProvider)
    }
    if kubeCfg.ReadOnlyPort > 0 {
        go k.ListenAndServeReadOnly(netutils.ParseIPSloppy(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort))
    }
    go k.ListenAndServePodResources()
}

跳转到 pkg/kubelet/kubelet.go ,这里就是 kubelet 的启动接口代码所在。

kubelet.Run
// Run starts the kubelet reacting to config updates
func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
    // ...

    // Start the cloud provider sync manager
    if kl.cloudResourceSyncManager != nil {
        go kl.cloudResourceSyncManager.Run(wait.NeverStop)
    }
    
    // 加载部分模块,如镜像管理, 观察oom, 
    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
    // 开启volume 管理
    go kl.volumeManager.Run(kl.sourcesReady, wait.NeverStop)

    //...
    
    // 开启pleg, 即watch方式获取 runtime的event
    // Start the pod lifecycle event generator.
    kl.pleg.Start()

    // Start eventedPLEG only if EventedPLEG feature gate is enabled.
    if utilfeature.DefaultFeatureGate.Enabled(features.EventedPLEG) {
        kl.eventedPleg.Start()
    }

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

事件watch

syncLoop

// syncLoop is the main loop for processing changes. It watches for changes from
// three channels (file, apiserver, and http) and creates a union of them. For
// any new change seen, will run a sync against desired state and running state. If
// no changes are seen to the configuration, will synchronize the last known desired
// state every sync-frequency seconds. Never returns.
func (kl *Kubelet) syncLoop(ctx context.Context, updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
   //...

    for {
        if err := kl.runtimeState.runtimeErrors(); err != nil {
            klog.ErrorS(err, "Skipping pod synchronizatio
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值