写点什么

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:081890

评论

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

不用PyScript,网页端运行的Python编辑器

Alexads

Python 代码编辑器

学生管理系统(2)

5月月更

Cocos Creator学习の有限状态机

空城机

Cocos 5月月更

一份让面试官满意的简历究竟要做到什么

宇宙之一粟

简历模板 简历 5月月更

最「难搞」的英伟达也开源了,苹果会是下一个吗?

腾源会

开源 英伟达 苹果 腾源会

即时通讯安全篇(九):为什么要用HTTPS?深入浅出,探密短连接的安全性

JackJiang

https 即时通讯 ssl im开发

druid 源码阅读(四)返回一个连接

爱晒太阳的大白

5月月更

[数据分析实践]-音频分析-BirdCLE-2

浩波的笔记

人工智能 机器学习 数据分析

druid 源码阅读 4——走一个单测看sql运行

张大彪

企评家 | 每日互动股份有限公司成长性评价简介

企评家

周日直播,龙蜥社区等8位专家在线报告 | 2022大学生操作系统大赛培训会

OpenAnolis小助手

操作系统 人才培养 大学生 龙蜥社区

Maven 项目

Emperor_LawD

maven 5月月更

从相亲来看Flutter 的 StatefulWidget 和 StatelessWidget

岛上码农

flutter ios开发 安卓开发 跨平台应用 5月月更

使用声网 SDK 为Android APP添加视频直播

声网

android 教程 视频直播

【LeetCode】一次编辑Java题解

Albert

LeetCode 5月月更

福昕软件与神州数码达成中国区分销合作

联营汇聚

如何设计产品帮助中心?从这几点出发

小炮

使用 Amazon Cloud WAN 构建您的全球网络

云物互联

云计算 AWS Cloud WAN

设计模式之原型模式

乌龟哥哥

5月月更

Druid连接池源码阅读04

石小天

数据库连接池 -Druid 源码学习(四)

wjchenge

Druid 数据库连接池

企评家,为沪深主板企业成长性评价

企评家

【愚公系列】2022年05月 二十三种设计模式(十三)-职责链模式(Chain of Responsibility Pattern)

愚公搬代码

5月月更

看 AWS 如何通过 Nitro System 构建竞争优势

云物互联

云计算 AWS DPU 硬件虚拟化技术

前缀和算法

工程师日月

算法 5月月更

druid源码学习四-多线程之锁探究

Nick

Apache Druid 锁机制 多线程安全

Redis「2」缓存一致性与异常处理

Samson

Redis 核心技术与实战 学习成长 5月月更

覆盖 70% 核心业务,ShardingSphere 如何成为喜马拉雅架构演进的催化剂

SphereEx

Apache 数据库 开源 ShardingSphere SphereEx

干货| 四种渲染到底是啥?终于有人讲明白了!(上)

Orillusion

开源 WebGL 元宇宙 Metaverse webgpu

Collections和Objects的使用注意

zarmnosaj

5月月更

面向CV编程:COPY了别人文章中的代码,想让代码能像作者一样跑通,应该注意什么呢?怎样才能让代码愉快地跑起来呢

迷彩

读书笔记 高效工作 程序员 个人思考 5月月更

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