openkruise如何实现容器重启-源码讲解

337 阅读2分钟

实现方式

官方文档介绍:openkruise.io/zh/docs/use…

实现方式.png

源码讲解

通过上面可以看到真正实现容器重启的是在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
}

stop.png

感悟

通过此功能的学习,发现目前对k8s的使用和了解仍只处于表面,其本身有很多未开放但是已经实现的能力。需要加强源码的学习,在了解底层能力之后才能避免重复的造轮子(关键是重复造的轮子还不一定好,哈哈哈哈 ),才能在上层服务中做出来更加符合实际的功能,在想做某些能的时候就可以带着问题去看源码。所以又引出来一个重要的问题,要多想,只有多想下一步才有动力去了解、去学习、去发现、去创造。迈出第一步要敢想,然后才能朝着想的方向去努力,才能实现最开始的想法。