实现方式
官方文档介绍:openkruise.io/zh/docs/use…
源码讲解
通过上面可以看到真正实现容器重启的是在kruise-daemon的模块,所以我们还是从daemon/main.go中开始着手,从初始化daemon开始.
了解本文章可以先看一下kubernetes-CRD的实现原理,了解一下informer的基本原理;openkruise如何实现镜像预热-源码讲解,讲解了daemon整体的一个逻辑,在本文中不再进行赘述,只对如何实现容器重启进行讲解。
对自定义的CRD ContainerRecreateRequest中的逻辑是如何实现的不再赘述,可以通过了解第一个链接了解整体CRD实现原理,大概的业务逻辑可以自行看一下代码,大体就是对要重启的pod 以及 crr进行了一些更新 以及填充了一些信息,便于daemon中进行逻辑处理。
func NewDaemon(cfg *rest.Config, bindAddress string) (Daemon, error) {
...
/**
在NewController中做的主要事情:
1、创建了CRRInformer (关于informer的原理可以观看:[kubernetes-CRD的实现原理])
2、创建了队列
3、informer对队列增加对应的处理逻辑
*/
crrController, err := containerrecreate.NewController(opts)
if err != nil {
return nil, fmt.Errorf("failed to new crr daemon controller: %v", err)
}
var runnables = []Runnable{
puller,
crrController,
}
...
return &daemon{
...
runnables: runnables,
...
}, nil
}
核心方法
crr_daemon_controller.go # func (c *Controller) manage
func (c *Controller) manage(crr *appsv1alpha1.ContainerRecreateRequest) error {
// 此处是在初始化daemon的时候在初始化进入的runtime
runtimeManager, err := c.newRuntimeManager(c.runtimeFactory, crr)
...
for i := range newCRRContainerRecreateStates {
...
msg := fmt.Sprintf("Stopping container %s by ContainerRecreateRequest %s", state.Name, crr.Name)
// KillContainer中核心逻辑,设置preStop & 调用k8s原生接口进行容器的停止
err := runtimeManager.KillContainer(pod, kubeContainerStatus.ID, state.Name, msg, nil)
...
break
}
...
if crr.Spec.Strategy != nil && crr.Spec.Strategy.MinStartedSeconds > 0 {
c.queue.AddAfter(objectKey(crr), time.Duration(crr.Spec.Strategy.MinStartedSeconds)*time.Second)
}
return nil
}
kuberuntime_controller.go # func (m *genericRuntimeManager) KillContainer
此方法的注解如下,通过注解可以清晰的看出这个方法的两个新逻辑
// KillContainer kills a container through the following steps: // * Run the pre-stop lifecycle hooks (if applicable). // * Stop the container.
func (m *genericRuntimeManager) KillContainer(pod *v1.Pod, containerID kubeletcontainer.ContainerID, containerName string, message string, gracePeriodOverride *int64) error {
...
// Run the pre-stop lifecycle hooks if applicable and if there is enough time to run it
if containerSpec.Lifecycle != nil && containerSpec.Lifecycle.PreStop != nil && gracePeriod > 0 {
gracePeriod = gracePeriod - m.executePreStopHook(pod, containerID, containerSpec, gracePeriod)
}
// always give containers a minimal shutdown window to avoid unnecessary SIGKILLs
if gracePeriod < minimumGracePeriodInSeconds {
gracePeriod = minimumGracePeriodInSeconds
}
// 通过继续进入,可以看到最终调用的方法,见下图
err := m.runtimeService.StopContainer(containerID.ID, gracePeriod)
return err
}
感悟
通过此功能的学习,发现目前对k8s的使用和了解仍只处于表面,其本身有很多未开放但是已经实现的能力。需要加强源码的学习,在了解底层能力之后才能避免重复的造轮子(关键是重复造的轮子还不一定好,哈哈哈哈 ),才能在上层服务中做出来更加符合实际的功能,在想做某些能的时候就可以带着问题去看源码。所以又引出来一个重要的问题,要多想,只有多想下一步才有动力去了解、去学习、去发现、去创造。迈出第一步要敢想,然后才能朝着想的方向去努力,才能实现最开始的想法。