写点什么

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

评论

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

华为云“创原会”:40+技术精英论道云原生2.0

华为云开发者联盟

k8s 华为云

【原创】Spring Boot集成Redis的玩法

田维常

spring Boot Starter

【原创】SpringBoot快速整合Thymeleaf模板引擎

田维常

spring Boot Starter

【原创】Spring Boot 过滤器、监听器、拦截器的使用

田维常

spring Boot Starter

【原创】Spring Boot 如何手写stater

田维常

spring Boot Starter

产品经理团队的管理秘法

马踏飞机747

管理 产品经理 团队

MySQL-技术专题-STRAIGHT_JOIN

码界西柚

阿里P8架构师呕心沥血整理的【Docker实战】文档带你玩转Docker。

Java架构之路

Java 程序员 架构 面试 编程语言

Spring Boot 如何快速实现定时任务

田维常

spring Boot Starter

详解软件行业低代码开发平台以及敏捷开发方案

Marilyn

敏捷开发

WebSocket连接错误Error during WebSocket handshake Unexpected response code 404

码界西柚

【原创】Spring Boot 集成Spring Data JPA的玩法

田维常

spring Boot Starter

【原创】Spring Boot终极篇《下》

田维常

spring Boot Starter

Spring Boot 集成 Druid 监控数据源

田维常

spring Boot Starter

【原创】Spring Boot一口气说自动装配与案例

田维常

spring Boot Starter

2020双十一终极清单!阿里云CDN&视频云最全优惠攻略

阿里云Edge Plus

CDN

华为云FusionInsight湖仓一体解决方案的前世今生

华为云开发者联盟

数据库 华为 仓库

华为20级工程师吐血整理出600页Spring微服务架构设计,绝了!

996小迁

Java spring 编程 架构 微服务

MySQL-技术专题-创建临时表

码界西柚

为什么11·11物流一年比一年快?奥秘就在这里!

华为云开发者联盟

物联网 物流 仓储

【原创】Spring Boot终极篇《上》

田维常

spring Boot Starter

用时半个月,终于把2020年各大公司的Java面试题精选整理成文档了

Java架构之路

Java 架构 面试 编程语言

阿里P8Java大神给迷茫的程序员一些中肯建议:“请不要再虚度光阴了!”

Java架构之路

Java 阿里巴巴 程序员 架构 编程语言

只需三步,带你从0到1玩转React,附源码我一定给你讲明白

小Q

Java 学习 编程 架构 面试

区块链数字货币商城系统开发技术

薇電13242772558

区块链 数字货币

【原创】Spring Boot集成Mybatis的玩法

田维常

spring Boot Starter

想不通(关于人生的突发奇想)

干啥啥不行的赢

国家超算深圳中心计划2年内提升计算能力至少1000倍;图神经网络的生成式预训练论文解读

京东科技开发者

云计算

【原创】SpringBoot 这几种配置文件方式,你都用过吗?

田维常

spring Boot Starter

如何快速构建Spring Boot基础项目?

田维常

spring Boot Starter

浅析一个较完整的SpringBoot项目

田维常

spring Boot Starter

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