写点什么

Kubelet 源码剖析

  • 2019-11-28
  • 本文字数:3539 字

    阅读完需:约 12 分钟

Kubelet 源码剖析

本篇文章主要介绍了 kubelet 服务启动和创建 pod 的流程,给想要阅读 kubelet 源码的同学一个参考。

前言

“Together we will ensure that Kubernetes is a strong and open container management framework for any application and in any environment – whether in a private, public or hybrid cloud,” Google senior VP Urs Hölzlesaid in the announcement today.


本文 Kubelet 版本: 1.7.4

1 Kubelet 介绍

在 Kubernetes 集群中,在每个 Node 节点上都会启动一个 kubelet 服务进程。该进程用于处理 Master 节点下发到本节点的任务,管理 Pod 及 Pod 中的容器。每个 Kubelet 进程会在 APIServer 上注册节点自身信息,定期向 Master 节点汇报节点资源的使用情况,并通过 cAdvise 监控容器和节点资源。

2 Kubelet 功能

  • Pod 管理

  • 容器的健康检测

  • 容器监控

3 Kubelet 代码结构

➜  kubelet git:(master) tree.├── BUILD├── OWNERS├── app│   ├── BUILD│   ├── OWNERS│   ├── auth.go│   ├── options│   │   ├── BUILD│   │   ├── container_runtime.go│   │   ├── options.go│   │   └── options_test.go│   ├── plugins.go│   ├── server.go│   ├── server_linux.go│   ├── server_test.go│   └── server_unsupported.go└── kubelet.go
2 directories, 15 files
复制代码


// kubelet 服务的入口 (main)


  • cmd/kubelet/kubelet.go


// 主要负责校验参数,创建和 api-server 交互的 client 及对运行 kubelet 权限检测,启动 Kubelet 等等


  • cmd/kubelet/app/server.go


除了入口,kubelet 其它的主要功能实现在 pkg/kubelet 下。这里就不一一介绍了,在下面的时序图中,会标记 pkg 中用到了哪些文件,并主要实现了什么功能。

4 Kubelet 服务启动流程


上面的时序图就是整个 kubelet 的启动流程。


// 主要对 kubelet 的 NewKubeletServer 结构体进行参数校验


  • validateConfig


// 创建与控制节点 api-server 交互的 client (kubeClient, eventClient)


  • CreateAPIServerClientConfig


// 对运行 kubelet 进程的用户的权限验证


  • checkPermission


在 RunKubelet 中主要做 CreateAndInitKubelet 和 startKubelet 两件事:


func CreateAndInitKubelet(...) (k kubelet.KubeletBootstrap, err error) {    k, err = kubelet.NewMainKubelet(....)    if err != nil {        return nil, err    }
k.BirthCry()
k.StartGarbageCollection()
return k, nil}
复制代码


NewMainKubelet 实例化一个 kubelet 对象,并对 kubelet 内部各个 component 进行初始化工作,如:


  • containerGC // 容器的垃圾回收

  • statusManager // pod 状态的管理

  • imageManager // 镜像的管路

  • probeManager // 容器健康检测

  • gpuManager // GPU 的支持

  • PodCache // Pod 缓存的管理

  • secretManager // secret 资源的管理

  • configMapManager // configMap 资源的管理

  • InitNetworkPlugin // 网络插件的初始化


// 对 pod 的管理, e.g., CRUD


  • PodManager


// pod 元数据的来源 (FILE, URL, api-server)


  • makePodSourceConfig


// 磁盘空间的管理


  • diskSpaceManager


// 容器运行时的选择(docker 或 rkt)


  • ContainerRuntime


// 通知 api-server 服务 kubelet 启动


  • BirthCry


// 开启垃圾回收服务


  • StartGarbageCollection


当之前所有的预处理工作处理完成之后,准备启动我们的 kubelet 服务 startKubelet:


func startKubelet() {    // start the kubelet    go wait.Until(func() { k.Run(podCfg.Updates()) }, 0, wait.NeverStop)       // start the kubelet server    if kubeCfg.ReadOnlyPort > 0 {        go wait.Until(func() {            k.ListenAndServeReadOnly(net.ParseIP(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort))        }, 0, wait.NeverStop)    }}
复制代码


startKubelet 方法中的第一个 goroutine 负责启动 kubelet,在 Run 中主要做的事儿就是启动 diskSpaceManager, volumeManager, statusManager, probeManager 及注册节点等相关服务组件,并最后执行 syncLoop 也就是创建 pod 的入口。


而后面则创建一个 kubelet http server,通过该 server 可以获取 pod 及 node 的相关信息。

5 Pod 的创建流程


syncLoop 是 kubelet 的主循环方法,它从不同的管道 (FILE, URL, api-server) 监听到 pod 的变化,并把它们聚合起来。当有新的 pod 变化发生时,它会调用对应的函数,保证 Pod 处于期望的状态。


func (kl *Kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler) {    for {        kl.syncLoopIteration(updates, handler...)     }}
复制代码


kl.syncLoopIteration 这个方法会对多个管道进行遍历,如果有 pod 动作,则会调用相应的 Handler。


下面是对应的 Interface.


type SyncHandler interface {    HandlePodAdditions(pods []*v1.Pod)    HandlePodUpdates(pods []*v1.Pod)    HandlePodRemoves(pods []*v1.Pod)    HandlePodReconcile(pods []*v1.Pod)    HandlePodSyncs(pods []*v1.Pod)    HandlePodCleanups() error}
复制代码


我们以创建 Pod 为例,它会调用对应的 HandlePodAdditionsHandler 进行处理。HandlePodAdditions 做的任务就是通过 canAdmitPod 方法校验 Pod 能否在该计算节点创建 (e.g., 磁盘空间)。之后把创建 Pod 的事交给 dispatchWork。dispatchWork 主要工作就是把接收到的参数封装成 UpdatePodOptions,调用 UpdatePod 方法.


syncPod 是创建 Pod 的核心逻辑。其中有几个主要的方法:


// 根据最新拿到的 pod 配置与当前运行的容器配置进行对比,计算其中的变化 (一个具体的 hash 值),得到需要重启的容器的信息


  • computePodContainerChanges


// 创建一个 PodSandBox


  • createPodSandBox


func (m *kubeGenericRuntimeManager) createPodSandbox(pod *v1.Pod, attempt uint32) (string, string, error) {    podSandboxConfig, err := m.generatePodSandboxConfig(pod, attempt)    ......    podSandBoxID, err := m.runtimeService.RunPodSandbox(podSandboxConfig)    ......    return podSandBoxID, "", nil}
复制代码


// 获取 PodSandbox 的配置 (e.g., metadata, clusterDNS, 容器的端口映射等)


  • generatePodSandboxConfig


// 创建并开启一个 Pod 级别的 sandbox


  • RunPodSandbox


在 RunPodSandbox 中主要调用如下几个方法:


// 检测用户是否设置了自己的 pause 镜像,如果没有设置则使用默认的镜像 gcr.io/google_containers/pause-amd64:3.0


  • ensureSandboxImageExists


// 生成创建 pause 容器的配置信息


  • makeSandboxDockerConfig

  • CreateContainer // 创建容器

  • StartContainer // 启动容器


// 设置容器的网络 (kubelet 加载 cni 插件对容器的网络进行设置等)


  • network.SetUpPod


上面的这些操作就把我们 Pod 中的第一个 pause 容器创建并启动了。之后要做的就是把该 Pod 中的其它业务容器逐一的启动。但是在启动真正的业务容器之前,首先会检查用户是否设置了 init_container。如果设置了,则会按 init_container 设置的顺序依次的执行 init_container (注意: 当其中的 init_container 执行失败了,则 Pod 会异常,并且业务容器不会被创建)。当 init_container 执行完成之后,我们真正的业务容器才会被逐一的启动。


业务容器启动的逻辑和 Pod 的初始化 pause 容器的启动的流程基本一致。下面的代码是循环的启动业务容器:


for idx := range podContainerChanges.ContainersToStart {     container := &pod.Spec.Containers[idx]     //.....      if msg, err := m.  startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podI; err != nil {          //.....          continue     }}
复制代码


在 startContainer 方法中主要调用如下方法:


// 检查业务镜像是否存在,不存在则到 Docker/Private Registry 拉取镜像


  • EnsureImageExists


// 生成业务容器的配置信息


  • generateContainerConfig


// 通过 client.CreateContainer 调用 docker engine-api 创建业务容器


  • CreateContainer

  • StartContainer //启动业务容器


// 这个方法的主要作用就是在业务容器起来的时候,首先会执行一个 container hook(PostStart 和 PreStop),做一些预处理工作。只有 container kook 执行成功才会运行具体的业务服务,否则容器异常。


  • runner.Run


这样 Pod 大体的启动流程就描述完了。

6 总结

kubelet 的主干道已经大体介绍完了,kubelet 还有许多中间服务,如 volumeManager, diskSpaceManager, secretManager, configMapManager 以及节点注册等等,接下来有时间会把各个功能组件的基本实现原理介绍给大家。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/7V1byf0Q_2OLWG0zh7LDgw


2019-11-28 15:081915

评论

发布
暂无评论
发现更多内容

1月月更|推荐学java——Spring集成MyBatis

逆锋起笔

mybatis ssm Spring Framework spring aop spring+mybatis

低代码实现探索(二十四)业务的领域可视化构思

零道云-混合式低代码平台

Form 表单在数栈的应用(下):深入篇

袋鼠云数栈

前端

鸿蒙开发初体验【鸿蒙01】

坚果

鸿蒙 1月月更

社区知识库|常见问答 FAQ 集合第 4 期:消息保留及延迟、Broker、Pulsar 权限等相关问题

Apache Pulsar

架构 云原生 开源社区 消息中间件 Apachepulsar

Linux之date命令

入门小站

Linux

前端开发之样式调试

@零度

前端开发

从零开发区块链应用(三)--mysql初始化及gorm框架使用

杰哥的技术杂货铺

Tcp网络模型

你?

博文推荐|深入解析Apache BookKeeper 系列:第一篇 — 架构原理

Apache Pulsar

开源 架构 云原生 中间件 Apache Pulsar

只有天空才是你的极限,我们热爱探索的过程并沉浸其中丨图数据库 TiMatch 团队访谈

PingCAP

从零开发区块链应用(二)--mysql安装及数据库表的安装创建

杰哥的技术杂货铺

从零开发区块链应用(四)--自定义业务错误信息

杰哥的技术杂货铺

带你读AI论文丨RAID2020 Cyber Threat Intelligence Modeling GCN

华为云开发者联盟

网络威胁情报 CTI 异构信息网络 GCN HINTI

详解 Flink 中 Time 与 Window

五分钟学大数据

flink 1月月更

博文推荐|Apache Pulsar: 统一消息流平台

Apache Pulsar

开源 云原生 broker Apache Pulsar 消息中间件

Spring的底层实现机制

编程江湖

Spring JPA

模块 5 作业

miliving

web技术分享| 白板SDK之函数和方程式的运用

anyRTC开发者

前端 音视频 视频会议 白板 web技术分享

社区知识库|常见问答 FAQ 集合第 3 期:Topic 数量、Broker 设置等相关问题

Apache Pulsar

分布式 云原生 Apache Pulsar 开源社区 消息中间件

设计模式【11】-- 搞定组合模式

秦怀杂货店

Java 组合模式

HTTP跨域小结

wong

nginx HTTP cros

2022AJAX常见面试题分享

编程江湖

ajax

(1-15/15) 预训练模型+微调范式下如何做到文本数据安全

mtfelix

300天创作 2022Y300P

HarmonyOS工程【鸿蒙开发02】

坚果

鸿蒙开发 1月月更

社区知识库|常见问答 FAQ 集合第 2 期:Topic 配置、ACK 等问题的解决

Apache Pulsar

架构 云原生 Apache Pulsar 开源社区 消息中间件

ReactNative进阶(二十四):react-native-scrollable-tab-view 标签导航器组件详解

No Silver Bullet

1月月更 ReactNative 标签导航

SIG技术直播来了!技术专家教你,如何使用 iLogtail 采集可观测数据?

OpenAnolis小助手

Linux 开源 数据 直播 采集

Spring中部署Activiti流程定义的三种姿势

华为云开发者联盟

spring 工作流 Activiti流程 Activiti框架 Activiti Explorer

从零开发区块链应用(一)--golang配置文件管理工具viper

杰哥的技术杂货铺

golang 区块链

Go 语言快速入门指南:Go 结构体

宇宙之一粟

Go 语言 结构体 1月月更

Kubelet 源码剖析_文化 & 方法_王希刚_InfoQ精选文章