kubelet是k8s集群中一个组件,其作为一个agent的角色分布在各个节点上,无论是master还是worker,功能繁多,逻辑复杂。主要功能有

  1. 节点状态同步:kublet给api-server同步当前节点的状态,会同步当前节点的CPU,内存及磁盘空间等资源到api-server,为scheduler调度pod时提供基础数据支撑
  2. Pod的启停及状态管理:kubelet会启动经scheduler调度到本节点的pod,同步它的状态保障它运行,当Pod关闭时负责资源回收

主要模块

kublet有以下几个监听端口

  • 10250 kubelet API :用于与api-server通讯,访问可获得node的资源和状态
  • 4194 cAdvisor :用于获取节点的环境信息和pod的状态的指标信息,
  • 10255 readonly API :以只读形式获取Pod和node的信息
  • 10248 /healthz :用于健康检查

kubelet包含的模块有以下

PLEG,cAdvisor,Container Manager,Volume Manager,Eviction Manager,OOMWatcher,ProbeManager,StatusManager,ImageGC,ContainerGC,ImageManager,CertificateManager,runtimeClassManager……如下图所示

挑几个介绍一下

  • PLEG(Pod Lifecycle Event Generator):kubelet的核心模块,一直调用 container runtime 获取本节点 containers/sandboxes 的信息,并与自身维护的 pods cache 信息进行对比,生成对应的 PodLifecycleEvent,通过 eventChannel 发送到 kubelet syncLoop 进行消费,然后由 kubelet syncPod 来触发 pod 同步处理过程,最终达到用户的期望状态。
  • cAdvisor :google 开发的容器监控工具,集成在 kubelet 中,起到收集本节点和容器的监控信息,对外提供了 interface 接口,该接口也被 imageManager,OOMWatcher,containerManager 等所使用。
  • OOMWatcher:系统 OOM 的监听器,会与 cadvisor 模块之间建立 SystemOOM,通过 Watch方式从 cadvisor 那里收到的 OOM 信号,并产生相关事件。
  • statusManager : 负责维护状态信息,并把 pod 状态更新到 apiserver,但是它并不负责监控 pod 状态的变化,而是提供对应的接口供其他组件调用,比如 probeManager。
  • volumeManager : 负责 node 节点上 pod 所使用 volume 的管理,volume 与 pod 的生命周期关联,负责 pod 创建删除过程中 volume 的 mount/umount/attach/detach 流程
  • ProbeManager:依赖于 statusManager,livenessManager,containerRefManager,会定时去监控 pod 中容器的健康状况,当前支持两种类型的探针:livenessProbe 和readinessProbe。

启动命令

kubelet是以二进制运行在各个节点上,通过ps -ef可以看到其启动命令,一般安装集群的时候会让其托管到system service中,让节点启动的时候将kubelet也自动启动。

进入/usr/lib/systemd/system/kubelet.service.d目录打开里面的文件查看

cd /usr/lib/systemd/system/kubelet.service.d
ls
10-kubeadm.conf
cat 10-kubeadm.conf
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS

kubelet启动有四个环境变量拼凑成参数,前两个环境变量已在1,2行提供。按照注释KUBELET_CONFIG_ARGS是“kubeadm init”和“kubeadm join”在运行时生成的文件,文件的路径是/var/lib/kubelet/kubeadm-flags.env;最后一个参数KUBELET_EXTRA_ARGS是用户用于覆盖kubelet的最后手段,文件的路径在/etc/sysconfig/kubelet。按照实验机器内容如下

cat /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS="--cgroup-driver=systemd --cluster-dns=10.96.0.10 --docker-endpoint=unix:///var/run/docker.sock --hostname-override=master1 --network-plugin=cni --pod-infra-container-image=deploy.deepexi.com/kubeadm/pause:3.2 --root-dir=/var/lib/kubelet --v=4"
cat /etc/sysconfig/kubelet
-bash: cd: /etc/sysconfig/kubelet: No such file or directory

最终组合而成的kubelet的启动命令是

/usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=systemd --cluster-dns=10.96.0.10 --docker-endpoint=unix:///var/run/docker.sock --hostname-override=master1 --network-plugin=cni --pod-infra-container-image=deploy.deepexi.com/kubeadm/pause:3.2 --root-dir=/var/lib/kubelet --v=4

与ps -ef|grep kubelet得到的结果是一致的

ps -ef|grep kubelet
root 1302 1 3 9月08 ? 16:17:27 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=systemd --cluster-dns=10.96.0.10 --docker-endpoint=unix:///var/run/docker.sock --hostname-override=master1 --network-plugin=cni --pod-infra-container-image=deploy.deepexi.com/kubeadm/pause:3.2 --root-dir=/var/lib/kubelet --v=4

上述参数中还有config参数传递的是一个yaml文件,打开它发现还有相当一部分的参数,由于篇幅太长则不在这展示

kubelet启动流程

源码版本:1.19

kubelet通过cobra框架来处理启动命令和启动参数,从main函数进来直接跳到cobra的Run函数注册则可,大致做下面几件事情

  1. 初始化启动命令和参数
  2. 初始化FeatureGate
  3. 校验命令行参数
  4. 加载KubeletConfigFile并验证之,即“启动命令”一节中提到的config参数传入的文件
  5. 加载使用动态配置,如果有启用
  6. 构造kubeletServer及kubeletDeps
  7. 调用Run函数运行kubelet

    代码位于/cmd/kubelet/app/server.go
func(cmd *cobra.Command, args []string) {
//1. 初始化启动命令和参数
if err := cleanFlagSet.Parse(args); err != nil {
}
//2. 初始化FeatureGate
if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil {
}
//3. 校验命令行参数
if err := options.ValidateKubeletFlags(kubeletFlags); err != nil {
}
//4. 加载KubeletConfigFile并验证之
if configFile := kubeletFlags.KubeletConfigFile; len(configFile) > 0 {
kubeletConfig, err = loadConfigFile(configFile)
}
if err := kubeletconfigvalidation.ValidateKubeletConfiguration(kubeletConfig); err != nil {
klog.Fatal(err)
} ////5. 加载使用动态配置的部分略 //6. 构造kubeletServer及kubeletDeps
// construct a KubeletServer from kubeletFlags and kubeletConfig
kubeletServer := &options.KubeletServer{
KubeletFlags: *kubeletFlags,
KubeletConfiguration: *kubeletConfig,
} // use kubeletServer to construct the default KubeletDeps
kubeletDeps, err := UnsecuredDependencies(kubeletServer, utilfeature.DefaultFeatureGate)
if err != nil {
klog.Fatal(err)
} //7. 调用Run函数运行kubelet
if err := Run(ctx, kubeletServer, kubeletDeps, utilfeature.DefaultFeatureGate); err != nil {
klog.Fatal(err)
} }

run函数

run函数主要为kubelet启动做一些环境检查,准备及校验操作

  1. 将当前的配置文件注册到 http server /configz URL 中
  2. 初始化各种客户端
  3. 初始化 auth,cgroupRoot,cadvisor,ContainerManager
  4. 为 kubelet 进程设置 oom 分数
  5. 初始化Runtime Server,设置CRI
  6. 调用 RunKubelet 方法执行后续的启动操作
  7. 启动 Healthz http server

    代码位于 /cmd/kubelet/server/server.go
func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate) (err error) {
//1 将当前的配置文件注册到 http server /configz URL 中
err = initConfigz(&s.KubeletConfiguration)
//2 初始化各种客户端,主要是非standalone模式下会进入这个,否则会将所有客户端都置为空
switch {
case kubeDeps.KubeClient == nil, kubeDeps.EventClient == nil, kubeDeps.HeartbeatClient == nil:
kubeDeps.KubeClient, err = clientset.NewForConfig(clientConfig)
kubeDeps.EventClient, err = v1core.NewForConfig(&eventClientConfig)
kubeDeps.HeartbeatClient, err = clientset.NewForConfig(&heartbeatClientConfig)
}
//3 初始化 auth,cgroupRoot,cadvisor,ContainerManager
if kubeDeps.Auth == nil {
auth, runAuthenticatorCAReload, err := BuildAuth(nodeName, kubeDeps.KubeClient, s.KubeletConfiguration)
}
nodeAllocatableRoot := cm.NodeAllocatableRoot(s.CgroupRoot, s.CgroupsPerQOS, s.CgroupDriver)
if kubeDeps.CAdvisorInterface == nil {
imageFsInfoProvider := cadvisor.NewImageFsInfoProvider(s.ContainerRuntime, s.RemoteRuntimeEndpoint)
}
if kubeDeps.ContainerManager == nil {
kubeDeps.ContainerManager, err = cm.NewContainerManager(...)
}
//4. 为 kubelet 进程设置 oom 分数
if err := oomAdjuster.ApplyOOMScoreAdj(0, int(s.OOMScoreAdj)); err != nil {
}
//5. 初始化Runtime Server,设置CRI
err = kubelet.PreInitRuntimeService(...)
//6. 调用 RunKubelet 方法执行后续的启动操作
if err := RunKubelet(s, kubeDeps, s.RunOnce); err != nil {
return err
}
//7. 启动 Healthz http server
if s.HealthzPort > 0 {
go wait.Until(func() {
err := http.ListenAndServe(net.JoinHostPort(s.HealthzBindAddress, strconv.Itoa(int(s.HealthzPort))), mux)
}, 5*time.Second, wait.NeverStop)
}
}

RunKubelet

RunKubelet函数核心就两个

  1. 初始化kubelet对象
  2. 将kubelet及相关kubelet的api跑起来
func RunKubelet(kubeServer *options.KubeletServer, kubeDeps *kubelet.Dependencies, runOnce bool) error {
k, err := createAndInitKubelet(...)
if runOnce {
}else{
startKubelet(k, podCfg, &kubeServer.KubeletConfiguration, kubeDeps, kubeServer.EnableCAdvisorJSONEndpoints, kubeServer.EnableServer)
}
}
createAndInitKubelet

createAndInitKubelet先构造出kubelet对象,NewMainKubelet的函数传入的参数也很多,函数里面包含了前文中“主要模块”的初始化操作。构造完毕后调用BirthCry方法往k8s发一个Starting kubelet.的event。然后就马上启动containerGC

    func createAndInitKubelet(......) {
k, err = kubelet.NewMainKubelet(...) k.BirthCry()
k.StartGarbageCollection()
return k, nil
}
startKubelet

startKubelet函数是通过调用kubelet的Run方法将kubelet跑起来,kubelet.Run包含了一部分“主要模块”中提及的manager的start方法调用,意味着kubelet的各个模块从此开始运行起来,此外还包括了kubelet的核心循环syncLoop在这里开始调用

运行了kubelet后,kubelet api、readonly API等server也在这里开始运行

func startKubelet(...) {
// start the kubelet
go k.Run(podCfg.Updates()) // start the kubelet server
if enableServer {
go k.ListenAndServe(net.ParseIP(kubeCfg.Address), uint(kubeCfg.Port), kubeDeps.TLSOptions, kubeDeps.Auth,
enableCAdvisorJSONEndpoints, kubeCfg.EnableDebuggingHandlers, kubeCfg.EnableContentionProfiling, kubeCfg.EnableSystemLogHandler) }
if kubeCfg.ReadOnlyPort > 0 {
go k.ListenAndServeReadOnly(net.ParseIP(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort), enableCAdvisorJSONEndpoints)
}
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletPodResources) {
go k.ListenAndServePodResources()
}
}

至此,kubelet运行起来了,开始执行它节点资源上报,Pod的启停及状态管理等工作。

启动流程的调用链

通过下面调用链大致回顾整个启动流程

main                                                                             // cmd/kubelet/kubelet.go
|--NewKubeletCommand // cmd/kubelet/app/server.go
|--Run // cmd/kubelet/app/server.go
|--initForOS // cmd/kubelet/app/server.go
|--run // cmd/kubelet/app/server.go
|--initConfigz // cmd/kubelet/app/server.go
|--BuildAuth
|--cm.NodeAllocatableRoot
|--cadvisor.NewImageFsInfoProvider
|--NewContainerManager
|--ApplyOOMScoreAdj
|--PreInitRuntimeService
|--RunKubelet // cmd/kubelet/app/server.go
| |--k = createAndInitKubelet // cmd/kubelet/app/server.go
| | |--NewMainKubelet
| | | |--watch k8s Service
| | | |--watch k8s Node
| | | |--klet := &Kubelet{}
| | | |--init klet fields
| | |
| | |--k.BirthCry()
| | |--k.StartGarbageCollection()
| |
| |--startKubelet(k) // cmd/kubelet/app/server.go
| |--go k.Run() // -> pkg/kubelet/kubelet.go
| | |--go cloudResourceSyncManager.Run()
| | |--initializeModules
| | |--go volumeManager.Run()
| | |--go nodeLeaseController.Run()
| | |--initNetworkUtil() // setup iptables
| | |--go Until(PerformPodKillingWork, 1*time.Second, neverStop)
| | |--statusManager.Start()
| | |--runtimeClassManager.Start
| | |--pleg.Start()
| | |--syncLoop(updates, kl) // pkg/kubelet/kubelet.go
| |
| |--k.ListenAndServe
|
|--go http.ListenAndServe(healthz)

参考文章

kubelet 架构浅析

kubelet 启动流程分析

万字长文:K8s 创建 pod 时,背后到底发生了什么?

kubelet源码分析——kubelet简介与启动的更多相关文章

  1. kubelet源码分析——关闭Pod

    上一篇说到kublet如何启动一个pod,本篇讲述如何关闭一个Pod,引用一段来自官方文档介绍pod的生命周期的话 你使用 kubectl 工具手动删除某个特定的 Pod,而该 Pod 的体面终止限期 ...

  2. kubelet源码分析——监控Pod变更

    前言 前文介绍Pod无论是启动时还是关闭时,处理是由kubelet的主循环syncLoop开始执行逻辑,而syncLoop的入参是一条传递变更Pod的通道,显然syncLoop往后的逻辑属于消费者一方 ...

  3. heapster源码分析——kubelet的api调用分析

    一.heapster简介 什么是Heapster? Heapster是容器集群监控和性能分析工具,天然的支持Kubernetes和CoreOS.Kubernetes有个出名的监控agent---cAd ...

  4. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  5. Envoy 源码分析--程序启动过程

    目录 Envoy 源码分析--程序启动过程 初始化 main 入口 MainCommon 初始化 服务 InstanceImpl 初始化 启动 main 启动入口 服务启动流程 LDS 服务启动流程 ...

  6. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

  7. neo4j源码分析1-编译打包启动

    date: 2018-03-22 title: "neo4j源码分析1-编译打包启动" author: "邓子明" tags: - 源码 - neo4j - 大 ...

  8. k8s client-go源码分析 informer源码分析(2)-初始化与启动分析

    k8s client-go源码分析 informer源码分析(2)-初始化与启动分析 前面一篇文章对k8s informer做了概要分析,本篇文章将对informer的初始化与启动进行分析. info ...

  9. kubelet源码分析(version: git tag 1.7.6)

    一.概述 kubelet源码入口:cmd/kubelet/kubelet.go main() cmd/kubelet/app 包中的Run函数: 查看先参数,kubelet.KubeletDeps t ...

随机推荐

  1. spring学习日志二

    一.spring依赖注入的方式 1.通过set方法来完成注入 <bean id="student" class="com.zhiyou100.xz.spring.S ...

  2. QT 中的模态和非模态对话框

    void MainWindow::on_pushButton_clicked() { //模态 QDialog dlg(this); dlg.resize(100,100); dlg.exec(); ...

  3. Hibernate之关联关系

    时间:2017-1-20 16:28 --一对多配置1.第一步:创建实体类    *   客户实体    *   订单实体    示例代码:        /**          * 客户实体    ...

  4. 回顾games101中的SSAA和MSAA

    回顾games101中的AA(抗锯齿) 前言 善于进行课后总结,可以更加巩固自己的知识和具体细节 锯齿(走样)产生的原因 本质上,在光栅化阶段中,用有限离散的数据想表示连续的(类似三角形的某一边),就 ...

  5. Web安全-信息收集

    信息收集 前言:在渗透测试过程中,信息收集是非常重要的一个环节,此环节的信息将影响到后续成功几率,掌握信息的多少将决定发现漏洞的机会的大小,换言之决定着是否能完成目标的测试任务.也就是说:渗透测试的思 ...

  6. windows安装trojan记录

    1.trojan 客户端安装教程 https://v2raytech.com/trojan-clients/ 2.chrome安装插件(crx文件)教程 https://www.jianshu.com ...

  7. mybaits源码分析(一)

    一.源码下载 1.手动编译源码 为了方便在看源码的过程中能够方便的添加注释,可以从官网下载源码编译生成对应的Jar包,然后上传到本地maven仓库,再引用这个Jar. 首先需要编译打包parent项目 ...

  8. ubuntu apt-get Failed to fetch Temporary failure resolving 'security.ubuntu.com'

    发现是因为代理设置原因,导致无法上网,设置代理后问题解决. System Setting -> Network -> Network Proxy -> input IP+Port - ...

  9. 磁盘“Seagate”没有被推出,因为一个或多个程序可能正在使用它。

    推出移动硬盘失败,解决方案: 执行 lsof /Volumes/Seagate/ 可以看到哪些进程在占用磁盘 $ lsof /Volumes/Seagate/ COMMAND PID USER FD ...

  10. 华为分析X HUAWEI Ads,上线深度转化事件回传功能,让ROI 看得见!

    华为分析X HUAWEI Ads,上线深度转化事件回传功能,让ROI 看得见! 随着移动应用生态的流量成本攀升.行业竞争加剧,越来越多的广告商希望通过精准投放来获取更高质量的深度转化用户,比如二手车. ...