背景
测试的时候,通常需要将豆荚中容器的频繁地杀死,重启。在这个过程中,Pod的状态经常会出现CrashLoopBackOff,而且容器重启的时间越来越长。
分析
为了避免集装箱频繁地重启,k8对容器重启过程做了限制,使用退下的方法,官方文档中的说法是:
容器由Kubelet重启失败,重新启动了一个指数退下延迟,延迟的倍数sync-frequency 0, 1, 2 x, 4 x 8 x…限制在5分钟,10分钟后重置成功执行。
-
<李> https://kubernetes - v1 - 4. github.io/docs/user guide/pod states/李>
这里先直接给出结论:
-
<李>在豆荚中重启容器的时候(具体时机是,周期性执行SyncPod()的时候),豆荚会通过自身的状态结构找到当前这个容器(因为豆荚中可能有多个容器)上一次退出的时间,记为ts李>
<李>如果是第一次重启,那么直接重启容器,并且在Kubelet的补偿。perItemBackoff(一个地图结构,关键是根据集装箱和所在pod对象计算出来的id)中记录下次倒扣的时间(初始值为10,然后按照指数增长,最大5分钟)李>
<李>如果不是第一次重启,即Kubelet的补偿。perItemBackoff中已经有这个容器的倒扣记录,计为倒扣,那么
-
<李>如果现在()- ts & lt;倒扣,表明等待的时间还不够,抛出CrashLoopBackOff事件(然后等到下一个SyncPod的周期到的时候,重新比较这个值)李>
<李>否则说,明已经等待倒扣时间了,可以重启了,此时执行backOff.Next(),将该容器对应的倒扣翻倍,然后执行重启操作李>
李
源码细节
-
<李>
kubernetes/pkg/kubelet kubelet.go
通过源码发现,kubernetes/pkg/kubelet/kubelet。去文件中有两个常量:
<代码> MaxContainerBackOff=300 * time.Second backOffPeriod=5诙? 10 代码>李>
使用这两个变量构造了一个倒扣对象,这个是kubelet的属性,对该节点上所有豆荚都适用
<代码> klet。倒扣=flowcontrol。NewBackOff (backOffPeriod MaxContainerBackOff) 代码>
倒扣结构如下
<代码>类型倒扣结构{ sync.Mutex 时钟clock.Clock defaultDuration time.Duration maxDuration time.Duration perItemBackoff map [string] * backoffEntry }代码>
然后在SyncPod方法中使用这个对象
<代码>//调用容器运行时的SyncPod回调 结果:=kl.containerRuntime。SyncPod (pod、apiPodStatus podStatus、pullSecrets kl.backOff) 代码>
-
<李> kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go李>
SyncPod具体做的事有:
<代码>//SyncPod同步运行舱所需的pod通过执行以下步骤:////1。计算沙箱和容器的变化。//2。必要时杀了pod沙箱。//3。杀死任何不应该运行的容器。//4。在必要时创建沙箱。//5。创建初始化容器。//6。建立正常的容器。 func (m * kubeGenericRuntimeManager) SyncPod (pod * v1。豆荚,_ v1。PodStatus PodStatus * kubecontainer。PodStatus, pullSecrets [] v1。秘密,倒扣* flowcontrol.Backoff)(结果kubecontainer.PodSyncResult){代码>
同样在这个文件中,有一个关键的函数
<代码>//如果一个容器仍在倒扣,函数将返回一个简短的错误和补偿//详细的错误消息。 func (m * kubeGenericRuntimeManager) doBackOff (pod * v1。豆荚,容器* v1。容器,podStatus * kubecontainer。PodStatus,倒扣* flowcontrol.Backoff) (bool、字符串错误){ var cStatus * kubecontainer.ContainerStatus _,c:=podStatus范围。ContainerStatuses { 如果c。名字==容器。的名字,,c。==kubecontainer状态。ContainerStateExited { cStatus=c 打破 } } 如果cStatus==nil { 返回false”、“零 } 蒟蒻阁。Infof(“检查为容器倒扣在豆荚% % q q ",容器。名字,format.Pod (pod))//使用最新的完成时间退出容器为起点计算是否退下。 ts:=cStatus.FinishedAt//确定容器倒扣需要一个独特的关键。 关键:=getStableKey (pod、容器) 如果补偿。IsInBackOffSince(关键、ts) { 如果裁判,犯错:=kubecontainer。GenerateContainerRef (pod、容器);呃==nil { m.recorder。Eventf (ref, v1。EventTypeWarning,事件。BackOffStartContainer”,关闭后重新启动失败的容器”) } 错:=fmt。Errorf(“退下% s重新启动失败的容器=% s pod=% s”, backOff.Get(关键),容器。名字,format.Pod (pod)) 蒟蒻阁。Infof (“% s”, err.Error ()) kubecontainer.ErrCrashLoopBackOff返回true, err.Error () } 倒扣。下一个(键,ts) 返回false”、“零 }豆荚中容器重启流程