源码研习之 Kubelet

Kubernetes kubelet agent

Posted by 董江 on Wednesday, April 21, 2021

kubernetes kubelet agent 来管理 Node节点上 Pod生命周期,并且管理 Node device、Pod与Node binding调度的核心进程

核心能力

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

kubernetes

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

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

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

除了由 apiserver 提供 PodSpec,还可以通过以下三种方式将容器清单(manifest)提供:

  • 文件(File):利用命令行参数传递路径: 周期性地监视此路径下的文件是否有更新。默认20s
  • HTTP 端点(HTTP endpoint):利用命令行参数指定 HTTP 端点。默认20s
  • HTTP 服务器(HTTP server):侦听 HTTP 并响应简单的 API (目前没有完整规范)

kubelet 功能归纳一下就是上报 Node 节点信息管理(创建、销毁)Pod。功能看似简单,实际不然。每一个点拿出来都需要很大的篇幅来讲,比如:

Node 节点的计算资源,除了传统的 CPU、内存、硬盘,还提供扩展来支持类似 GPU 等资源;

Pod 不仅仅有容器,还有相关的网络、安全策略等等;

Probe 心跳上报,以及异常处理等等;

Volume 创建、附加、挂载、卸载、分离和读写策略等等;

……

核心架构

kubelet

重要组件

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

PLEG

PLEGPod Lifecycle Event Generator,字面意思 Pod 生命周期事件(ContainerStarted、ContainerDied、ContainerRemoved、ContainerChanged)生成器。

其维护着 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. 比如:本地拉起的静态Pod、远端 文件、APIServer和HTTP

StatsProvider

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

ContainerRuntime

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

Deps.PodConfig

PodConfig 是一个配置多路复用器,它将许多 Pod 配置源合并成一个单一的一致结构,然后按顺序向监听器传递增量变更通知。

配置源有:文件、apiserver、HTTP

syncLoop

接收来自 PodConfig 的 Pod 变更通知、定时任务、PLEG 的事件,以及 ProbeManager 的事件,将 Pod 同步到期望状态

PodAdmitHandlers

Pod admission 过程中调用的一系列处理器,比如 eviction handler(节点内存有压力时,不会驱逐 QoS 设置为 BestEffort 的 Pod)、shutdown admit handler(当节点关闭时,不处理 pod 的同步操作)等。

OOMWatcher

从系统日志中获取容器的 OOM 日志,将其封装成事件并记录。

VolumeManger

VolumeManager 运行一组异步循环,根据在此节点上调度的 pod 确定需要附加/挂载/卸载/分离哪些卷并执行操作。

CertificateManager

处理证书轮换。

ProbeManager

实际上包含了三种 Probe,提供 probe 结果缓存和通道。

  • LivenessManager
  • ReadinessManager
  • StartupManager

EvictionManager

监控 Node 节点的资源占用情况,根据驱逐规则驱逐 Pod 释放资源,缓解节点的压力。

PluginManager

PluginManager 运行一组异步循环,根据此节点确定哪些插件需要注册/取消注册并执行。如 CSI 驱动和设备管理器插件(Device Plugin)。

CSI

Container Storage Interface,由存储厂商实现的存储驱动。

设备管理器插件(Device Plugin)

Kubernetes 提供了一个 设备插件框架,你可以用它来将系统硬件资源发布到 Kubelet。

供应商可以实现设备插件,由你手动部署或作为 DaemonSet 来部署,而不必定制 Kubernetes 本身的代码。目标设备包括 GPU、高性能 NIC、FPGA、 InfiniBand 适配器以及其他类似的、可能需要特定于供应商的初始化和设置的计算资源

Kubelet启动流程

kubelet 的启动命令,以minikube 1.21为例

dongjiang@dongjiangdeMacBook-Pro ~ % ps -aux | grep '/kubelet' | grep -v grep
root        4917  2.6  0.3 1857652 106152 ?      Ssl  01:34  13:05 /var/lib/minikube/binaries/v1.21.0/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=docker --hostname-override=1.21.0 --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.64.5

或者

dongjiang@dongjiangdeMacBook-Pro ~ % systemctl status kubelet.service
● kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/usr/lib/systemd/system/kubelet.service; disabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/kubelet.service.d
             └─10-kubeadm.conf
     Active: active (running) since Sun 2021-04-21 01:34:42 UTC; 11h ago
       Docs: http://kubernetes.io/docs/
   Main PID: 4917 (kubelet)
      Tasks: 15 (limit: 38314)
     Memory: 39.4M
     CGroup: /system.slice/kubelet.service
             └─4917 /var/lib/minikube/binaries/v1.21.0/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=docker --hostname-override=1.21.0 --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.64

源码研习

git@github.com:kubernetes/kubernetes.git 仓库获取代码,使用最新的 release-1.21 分支。

├─'cmd/kubelet/kubelet.go:35''main' 方法为程序入口。
│  ├─调用 'NewKubeletCommand' 方法,创建 command
│  ├─执行 command
│  │  ├─'cmd/kubelet/app/server.go:434''Run' 方法。
│  │  │  ├─调用 'RunKubelet' 方法。
│  │  │  │  ├─调用 'createAndInitKubelet' 方法,创建并初始化 kubelet
│  │  │  │  │  ├─'pkg/kubelet/kubelet.go''NewMainKubelet' 方法,创建 kubelet的 各种组件。共十几个组件,见 kubelet 的构架。
│  │  │  │  │  ├─调用 'BirtyCry' 方法:放出 'Starting' 事件
│  │  │  │  │  ├─调用 'StartGarbageCollection' 方法,开启 'ContainerGC''ImageGC'
│  │  │  │  ├─调用 'startKubelet' 方法(大量使用 goroutine 和通道)
│  │  │  │  │  ├─goroutine:'kubelet.Run()'
│  │  │  │  │  │  ├─初始化模块
│  │  │  │  │  │  │  ├─metrics 相关
│  │  │  │  │  │  │  ├─创建文件系统目录目录
│  │  │  │  │  │  │  ├─创建容器日志目录
│  │  │  │  │  │  │  ├─启动 'ImageGCManager'
│  │  │  │  │  │  │  ├─启动 'ServerCertificateManager'
│  │  │  │  │  │  │  ├─启动 'OOMWatcher'
│  │  │  │  │  │  │  ├─启动 'ResourceAnalyzer'
│  │  │  │  │  │  ├─goroutine:'VolumeManager.Run()' 开始处理 Pod Volume 的卸载和挂载
│  │  │  │  │  │  ├─goroutine:状态更新 'fastStatusUpdateOnce()' (更新 Pod CIDR -> 更新 ContainerRuntime 状态 -> 更新 Node 节点状态)
│  │  │  │  │  │  ├─goroutine: 'NodeLeaseController.Run()' 更新节点租约
│  │  │  │  │  │  ├─goroutine:'podKiller.PerformPodKillingWork' 杀掉未被正确处理的 pod
│  │  │  │  │  │  ├─'StatusManager.Start()' 开始向 apiserver 更新 Pod 状态
│  │  │  │  │  │  ├─'RuntimeClassManager.Start()'
│  │  │  │  │  │  ├─'PLEG.Start()':持续从 'ContainerRuntime' 获取 Pod/容器的状态,并与 kubelet 本地 cache 中的比较,生成对应的 'Event'
│  │  │  │  │  │  ├─'syncLoop()' 重点,'持续监控并处理来自文件、apiserver、http 的变更'。包括 Pod 的增加、更新、优雅删除、非优雅删除、调和。
│  │  │  ├─启动 server,暴露 '/healthz' 端点
│  │  │  ├─通知 'systemd kuberlet' 服务已经启动

kubelet数据流

  • 静态文件、apiserver 以及 HTTP 请求的 Pod 配置变更,被发送到 kubelet.syncLoop
  • PLEG 会定期通过容器运行时获取节点上 Pod 的状态,与其缓存中的 Pod 信息进行比较,封装成事件,进入 PLEG 的通道
  • 定期检查工作队列中的 Pod
  • ProbeManager 的通道中的 Pod
  • 以上4步都会进入 syncLoopIteration,并从对应的通道中获取到对应 Pod,将 Pod 的信息保存到 PodManager;然后分发给 PodWorker,完成一些列的同步工作。

「如果这篇文章对你有用,请随意打赏」

Kubeservice博客

如果这篇文章对你有用,请随意打赏

使用微信扫描二维码完成支付