本文以v1.12版本进行分析

当一个pod删除时,client端向apiserver发送请求,apiserver将pod的deletionTimestamp打上时间。kubelet watch到该事件,开始处理。

syncLoop

kubelet对pod的处理主要都是在syncLoop中处理的。

func (kl *Kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
for {
...
if !kl.syncLoopIteration(updates, handler, syncTicker.C, housekeepingTicker.C, plegCh) {
break
}
...

与pod删除主要在syncLoopIteration中需要关注的是以下这两个。

func (kl *Kubelet) syncLoopIteration(configCh <-chan kubetypes.PodUpdate, handler SyncHandler,
syncCh <-chan time.Time, housekeepingCh <-chan time.Time, plegCh <-chan *pleg.PodLifecycleEvent) bool {
select {
case u, open := <-configCh:
...
switch u.Op {
...
case kubetypes.UPDATE:
handler.HandlePodUpdates(u.Pods)
...
case <-housekeepingCh:
if !kl.sourcesReady.AllReady() {
} else {
if err := handler.HandlePodCleanups(); err != nil {
glog.Errorf("Failed cleaning pods: %v", err)
}
}
}

第一个是由于发送给apiserver的DELETE请求触发的,增加了deletionTimestamp的事件。这里对应于kubetypes.UPDATE。也就是会走到HandlePodUpdates函数。

另外一个与delete相关的是每2s执行一次的来自于housekeepingCh的定时事件,用于清理pod,执行的是handler.HandlePodCleanups函数。这两个作用不同,下面分别进行介绍。

HandlePodUpdates

先看HandlePodUpdates这个流程。只要打上了deletionTimestamp,就必然走到这个流程里去。

func (kl *Kubelet) HandlePodUpdates(pods []*v1.Pod) {
for _, pod := range pods {
...
kl.dispatchWork(pod, kubetypes.SyncPodUpdate, mirrorPod, start)
}
}

在HandlePodUpdates中,进而将pod的信息传递到dispatchWork中处理。

func (kl *Kubelet) dispatchWork(pod *v1.Pod, syncType kubetypes.SyncPodType, mirrorPod *v1.Pod, start time.Time) {
if kl.podIsTerminated(pod) {
if pod.DeletionTimestamp != nil {
kl.statusManager.TerminatePod(pod)
}
return
}
// Run the sync in an async worker.
kl.podWorkers.UpdatePod(&UpdatePodOptions{
Pod: pod,
MirrorPod: mirrorPod,
UpdateType: syncType,
OnCompleteFunc: func(err error) {
...

这里首先通过判断了kl.podIsTerminated(pod)判断pod是不是已经处于了Terminated状态。如果是的话,则不进行下面的kl.podWorkers.UpdatePod。

func (kl *Kubelet) podIsTerminated(pod *v1.Pod) bool {
status, ok := kl.statusManager.GetPodStatus(pod.UID)
if !ok {
status = pod.Status
}
return status.Phase == v1.PodFailed || status.Phase == v1.PodSucceeded || (pod.DeletionTimestamp != nil && notRunning(status.ContainerStatuses))
}

这个地方特别值得注意的是,并不是由了DeletionTimestamp就会认为是Terminated状态,而是有DeletionTimestamp且所有的容器不在运行了。也就是说如果是一个正在正常运行的pod,是也会走到kl.podWorkers.UpdatePod中的。UpdatePod通过一系列函数调用,最终会通过异步的方式执行syncPod函数中进入到syncPod函数中。

func (kl *Kubelet) syncPod(o syncPodOptions) error {
...
if !runnable.Admit || pod.DeletionTimestamp != nil || apiPodStatus.Phase == v1.PodFailed {
var syncErr error
if err := kl.killPod(pod, nil, podStatus, nil); err != nil {
...

在syncPod中,调用killPod从而对pod进行停止操作。

killPod

killPod是停止pod的主体。在很多地方都会使用。这里主要介绍下起主要的工作流程。停止pod的过程主要发生在killPodWithSyncResult函数中。

func (m *kubeGenericRuntimeManager) killPodWithSyncResult(pod *v1.Pod, runningPod kubecontainer.Pod, gracePeriodOverride *int64) (result kubecontainer.PodSyncResult) {
killContainerResults := m.killContainersWithSyncResult(pod, runningPod, gracePeriodOverride)
...
for _, podSandbox := range runningPod.Sandboxes {
if err := m.runtimeService.StopPodSandbox(podSandbox.ID.ID); err != nil {
...

killPodWithSyncResult的主要工作分为两个部分。killContainersWithSyncResult负责将pod中的container停止掉,在停止后再执行StopPodSandbox。

func (m *kubeGenericRuntimeManager) killContainer(pod *v1.Pod, containerID kubecontainer.ContainerID, containerName string, reason string, gracePeriodOverride *int64) error {
if err := m.internalLifecycle.PreStopContainer(containerID.ID); err != nil {
return err
}
...
err := m.runtimeService.StopContainer(containerID.ID, gracePeriod)

killContainersWithSyncResult的主要工作是在killContainer中完成的,这里可以看到,其中的主要两个步骤是在容器中进行prestop的操作。待其成功后,进行container的stop工作。至此所有的应用容器都已经停止了。下一步是停止pause容器。而StopPodSandbox就是执行这一过程的。将sandbox,也就是pause容器停止掉。StopPodSandbox是在dockershim中执行的。

func (ds *dockerService) StopPodSandbox(ctx context.Context, r *runtimeapi.StopPodSandboxRequest) (*runtimeapi.StopPodSandboxResponse, error) {
...
if !hostNetwork && (ready || !ok) {
...
err := ds.network.TearDownPod(namespace, name, cID, annotations)
...
}
if err := ds.client.StopContainer(podSandboxID, defaultSandboxGracePeriod); err != nil {

StopPodSandbox中主要的部分是先进行网络卸载,再停止相应的容器。在完成StopPodSandbox后,至此pod的所有容器都已经停止完成。至于volume的卸载,是在volumeManager中进行的。本文不做单独介绍了。停止后的容器在pod彻底清理后,会被gc回收。这里也不展开讲了。

HandlePodCleanups

上面这个流程并不是删除流程的全部。一个典型的情况就是,如果container都不是running,那么在UpdatePod的时候都return了,那么又由谁来处理呢?这里我们回到最开始,就是那个每2s执行一次的HandlePodCleanups的流程。也就是说比如container处于crash,container正好不是running等情况,其实是在这个流程里进行处理的。当然HandlePodCleanups的作用不仅仅是清理not running的pod,再比如数据已经在apiserver中强制清理掉了,或者由于其他原因这个节点上还有一些没有完成清理的pod,都是在这个流程中进行处理。

func (kl *Kubelet) HandlePodCleanups() error {
...
for _, pod := range runningPods {
if _, found := desiredPods[pod.ID]; !found {
kl.podKillingCh <- &kubecontainer.PodPair{APIPod: nil, RunningPod: pod}
}
}

runningPods是从cache中获取节点现有的pod,而desiredPods则是节点上应该存在未被停止的pod。如果存在runningPods中有而desiredPods中没有的pod,那么它应该被停止,所以发送到podKillingCh中。

func (kl *Kubelet) podKiller() {
...
for podPair := range kl.podKillingCh {
... if !exists {
go func(apiPod *v1.Pod, runningPod *kubecontainer.Pod) {
glog.V(2).Infof("Killing unwanted pod %q", runningPod.Name)
err := kl.killPod(apiPod, runningPod, nil, nil)
...
}(apiPod, runningPod)
}
}
}

在podKiller流程中,会去接收来自podKillingCh的消息,从而执行killPod,上文已经做了该函数的介绍了。

statusManager

在最后,statusManager中的syncPod流程,将会进行检测,通过canBeDeleted确认是否所有的容器关闭了,volume卸载了,cgroup清理了等等。如果这些全部完成了,则从apiserver中将pod信息彻底删除。

func (m *manager) syncPod(uid types.UID, status versionedPodStatus) {
...
if m.canBeDeleted(pod, status.status) {
deleteOptions := metav1.NewDeleteOptions(0)
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(pod.UID))
err = m.kubeClient.CoreV1().Pods(pod.Namespace).Delete(pod.Name, deleteOptions)
...

pod删除主要流程源码解析的更多相关文章

  1. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  2. Redis运行流程源码解析--转载

    http://blog.nosqlfan.com/html/4007.html http://www.searchdatabase.com.cn/showcontent_62166.htm 导读:本文 ...

  3. 自定义控件(View的绘制流程源码解析)

    参考声明:这里的一些流程图援引自http://a.codekk.com/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7% ...

  4. Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探

    目录 1. 前言 1.1 IOC容器到底是什么 1.2 BeanFactory和ApplicationContext的联系以及区别 1.3 解读IOC容器启动流程的意义 1.4 如何有效的阅读源码 2 ...

  5. NioEventLoop启动流程源码解析

    NioEventLoop的启动时机是在服务端的NioServerSocketChannel中的ServerSocketChannel初始化完成,且注册在NioEventLoop后执行的, 下一步就是去 ...

  6. java架构之路-(SpringMVC篇)SpringMVC主要流程源码解析(上)源码执行流程

    做过web项目的小伙伴,对于SpringMVC,Struts2都是在熟悉不过了,再就是我们比较古老的servlet,我们先来复习一下我们的servlet生命周期. servlet生命周期 1)初始化阶 ...

  7. SpringMvc请求流程源码解析

    目录 SpringMvc请求流程图 请求流程粗讲解 方法细讲 doDispatcher --> 核心 找到Handler#getHandler getHandler(request) mappi ...

  8. Spring Security登录验证流程源码解析

    一.登录认证基于过滤器链 Spring Security的登录验证流程核心就是过滤器链.当一个请求到达时按照过滤器链的顺序依次进行处理,通过所有过滤器链的验证,就可以访问API接口了. SpringS ...

  9. 关于 Spring 中 getBean 的全流程源码解析

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你提出问题,就要给出解决方案! 最近有粉丝小伙伴反馈,与自己的上级沟通总是遇到障碍, ...

随机推荐

  1. ESP8266开发之旅 基础篇④ ESP8266与EEPROM

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  2. 百万年薪python之路 -- 函数初始练习

    1.整理函数相关知识点 2.写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者. def func(lst): lst = lst[1::2] return l ...

  3. 小白学 Python(9):基础数据结构(列表)(上)

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...

  4. 哈夫曼树C++实现详解

    哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这个定 ...

  5. 如何利用C# Roslyn编译器写一个简单的代码提示/错误检查?

    OK, 废话不多说,这些天在写C#代码时突然对于IDE提示有了一些想法,之前也有了解过,不过并没有深入. 先看个截图: 一段再简单不过的代码了,大家注意看到 count 字段下面的绿色波浪线了吗,我们 ...

  6. JS-SDK

    1. 签名 ​ 看到网上的大部分问题都集中在签名部分,请大家一定请熟读微信JS-SDK说明文档附录5-常见错误及解决方法 部分. 注意 在计算签名的过程中,如果url总是不对请 实验 首页的url或 ...

  7. (day27)subprocess模块+粘包问题+struct模块+ UDP协议+socketserver

    目录 昨日回顾 软件开发架构 C/S架构 B/S架构 网络编程 互联网协议 socket套接字 今日内容 一.subprocess模块 二.粘包问题 三.struct模块 四.UDP 五.QQ聊天室 ...

  8. java入门到秃路线导航,元芳你怎么看?【教学视频+博客+书籍整理】

    目录 一.Java基础 二.关于JavaWeb基础 三.关于数据库 四.关于ssm框架 五.关于数据结构与算法 六.关于开发工具idea 七.关于项目管理工具Mawen.Git.SVN.Gradle. ...

  9. WinDbg命令系统

    WinDbg命令系统 WinDbug三种命令 WinDbug是一个强大的调试器,大部分很多功能都是通过命令来实现的,命令在命令窗口中输入,主要分为以下三类: 标准命令 标准命令提供了调试器的基本功能, ...

  10. 指定路径批量将xls转换成csv

    PS : 用到spire库,.net控制台应用程序 其实本来没打算写这个工具的,只是最近需要用到,手头上正好没有这样的工具,那么怎么办,写呗! 其实说白了就是省事,策划想怎么玩,把表把工具丢给他,省得 ...