kubernetes-kubelet进程源码分析(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hahachenchen789/article/details/87863090

接着上篇博文,我们继续分析kubelet进程的另一个重要功能是如何实现的:定期同步Pod状态信息到API sever。

先来看看Pod状态的数据结构定义:

Pod的状态又5种:运行中(PodRunning)、等待中(PodPending)、正常终止(PodSucceeded)、异常停止(PodFailed)及未知状态(PodUnknown),最后一种状态很可能是由于Pod所在主机的通信问题导致的。从上面的定义可以看到Pod的状态同时包括它里面运行的container的状态,另外给出了导致当前状态的原因说明、Pod的启动时间等信息。Podstatusresult则是kubernetes API Server提供的Pod status API接口中用到的wrapper类。

通过之前的代码研读,我们发现在kubernetes中大量使用了channel和协程机制来完成数据的高效传递和处理工作,在kubelet中更是大量使用了这一机制,实现Pod status上报的kubelet.statusManager也是如此,它用一个Map(podStatus)保存了当前kubelet中所有Pod实例的当前状态,并且声明了一个channel(podStatuschannel)来存放Pod状态同步的更新请求(podstatuses),Pod在本地实例化和同步过程中会引发Pod状态的变化,这些变化被封装为podStatusSyncRequest放入Channel中,然后被异步上报到API Server,这就是StatusManager的运行机制。

下面是StatusManager的SetPodStatus方法,先比较缓存的状态信息,如果状态发生变化,则触发Pod状态,生成podStatusSyncRequest并放到队列中等待上报:

下面是在Pod实例化的过程中,kubelet过滤掉不合适本节点Pod所调用的上述方法的代码,类似的调用还有不少:

最后我们看看statusmanager是怎么把channel的数据上报给API Server上的, 这是通过start方法开启一个协程无限循环执行syscBatch方法来实现的,下面是syncBatch代码:

这段代码首先从Channel中拉取一个syncRequest,然后调用API Server接口来获取最新的Pod信息,如果成功,则继续调用API  Server的UpdateStatus接口更新Pod状态,如果调用失败则删除缓存的Pod状态,这将触发kubelet重新计算Pod状态并再次尝试更新。

说完了Pod流程,我们接下来深入分析kubernetes中的容器探针(probe)的实现机制,我们知道,容器正常不代表里面运行的业务进程能够正常运行,比如程序还没初始化好,或者配置文件错误导致无法正常服务,还有诸如数据库连接爆满导致服务异常等各种意外情况都有可能发生,面对这类问题,CAdvisor就束手无措了,所以kubelet引入容器探针技术,容器探针按照作用划分为以下两种:

1.ReadinessProbe:用来探测容器中的用户服务进程是否处于可服务状态,此探针不会导致容器被停止或者重启,而是导致此容器上的服务被标识为不可用,kubernetes不会发送请求到不可用的容器上,直到它们可用为止。

2.LivenessProbe:用来探测容器服务是否处于存活状态,如果服务当前被检测为dead,则会导致容器重启事件发生。

下面是探针的结构定义:

从代码看,探针可以通过执行容器中的一个命令、发起一个指向容器内部的HTTP GET请求或者TCP连接来确定容器内部是否正常工作。

上面代码属于API包的一部分,只是用来描述和存储容器上的探针定义,而真正的探针实现代码位于pkg/kubelet/prober/prober.go里,下面是对prober.Probe的定义:

上述接口方法表示对一个container发起探测并返货其结果,prober.Probe的实现类为prober.prober.结构定义如下:

其中exec、http、tcp三种变量分别对应三种类型的探头,它们已经各自实现了相应的逻辑,比如下面这段代码是HTTP探头的核心逻辑,即连接一个URL发起GET请求:

prober.prober中的runner则是exec探头的执行器,因为后者需要在被检测的容器中执行一个cmd命令:

实际上p.runner就是之前我们分析过的dockermanager,下面是RunInContainer的源码:

docker自1.3版本开始支持exec指令在容器内执行一个命令,我们看看上述代码中使用的dm.client.CreateExec方法是如何实现的:

我们看到,这是标准的Docker API的调用方式,跟之前的创建容器的调用代码很相似。

现在我们再回头看看prober.prober是怎么执行ReadinessProbe/LivenessProbe的检测逻辑的:

这段代码先调用容器的ReadinessProbe进行检测,并且在readinessManager组件中记录容器的Readiness状态,随后调用容器的LivenessProbe进行检测,并返回容器的状态,在检测过程中如果发现状态为失败或者异常,则会连续检测三次:

最后,我们再来简单分析下kubelet中的kubelet Server的实现机制,下面是kubelet进程启动过程中启动kubelet server的源码入口:

在上述代码调用过程中,创建了一个类型为kubelet.Server的HTTP Server并在本地监听:

在kubelet.Server的构造函数里加载如下HTTP Handler:

上述Handler分为两组:首先是健康检查,包括kubelet进程自身的心跳检查、Docker进程的健康检查、kubelet所在主机名检测、Pod同步的健康检查等。然后是获取当前节点上运行信息的接口,例如获取当前节点上的Pod列表,统计信息等。下面是hostnameHealthCheck的实现逻辑,它检查Pod两次同步之间的时延,而这个时延则在之前提到的kubelet的syncLoopIteration方法中进行更新:

handlePods的API则从kubelet中获得当前绑定到本节点的所有Pod的信息并返回:

如果kubelet运行在debug模式,则加载更多的HTTP  Handler:

这些HTTP Handler的实现并不复杂。所以不一一介绍了。

猜你喜欢

转载自blog.csdn.net/hahachenchen789/article/details/87863090