本篇文章主要介绍了 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 服务的入口 (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 两件事:
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:
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 处于期望的状态。
kl.syncLoopIteration 这个方法会对多个管道进行遍历,如果有 pod 动作,则会调用相应的 Handler。
下面是对应的 Interface.
我们以创建 Pod 为例,它会调用对应的 HandlePodAdditionsHandler 进行处理。HandlePodAdditions 做的任务就是通过 canAdmitPod 方法校验 Pod 能否在该计算节点创建 (e.g., 磁盘空间)。之后把创建 Pod 的事交给 dispatchWork。dispatchWork 主要工作就是把接收到的参数封装成 UpdatePodOptions,调用 UpdatePod 方法.
syncPod 是创建 Pod 的核心逻辑。其中有几个主要的方法:
// 根据最新拿到的 pod 配置与当前运行的容器配置进行对比,计算其中的变化 (一个具体的 hash 值),得到需要重启的容器的信息
computePodContainerChanges
// 创建一个 PodSandBox
createPodSandBox
// 获取 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 容器的启动的流程基本一致。下面的代码是循环的启动业务容器:
在 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
评论