Kruise-daemon 是运行在每个 node 上的 pod,是一些功能实现的底层逻辑
系列教程: Openkruise
目录: pkg/daemon
-
Container-Recreate
上层逻辑 ContainerRestart
调用层级
Run
|- processNextWorkItem
|- sync // 只有当 crr 处于 active 状态的时候才会进入下一步,这里也会等待 UnreadyGracePeriodSeconds 参数
|- manage // do re-create
-
通过 crr 拿到 pod:
pod := convertCRRToPod(crr) -
再通过函数
runtimeManager.GetPodStatus(pod.UID, pod.Name, pod.Namespace)拿到 pod 里每个 container 的状态podStatus -
根据 podStatus 调用函数
getCurrentCRRContainersRecreateStates(crr, podStatus)生成newCRRContainerRecreateStates -
遍历
newCRRContainerRecreateStates里每一个元素的 Phase-
如果 phase == Success,意味着重建成功,则只是把 completedCount++
-
如果 phase == Failed,意味着重建失败,此时要根据失败策略(crr.Spec.Strategy.FailurePolicy)进行选择
- 如果失败策略为 Ignore,意味着忽略失败,那么直接继续处理其他的 status
- 如果失败策略不为 Ignore,则直接报错
-
如果 phase == Recreating,意味着该 container 正在重建,此时需要根据参数 crr.Spec.Strategy.OrderedRecreate 来调整并发度
- 如果 OrderedRecreate == true, 意味着需要按序进行,则直接跳出当前循环处理当下的 container
- 如果 OrderedRecreate == false,意味着可以批处理,继续循环处理即可
-
如果以上状态都不是,那么就 kill container:
runtimeManager.KillContainer(pod, kubeContainerStatus.ID, state.Name, msg, nil),然后将 container 状态设置为 Recreating-
killContainer 位于 pkg/daemon/kuberuntime/kuberuntime_container.go
整体步骤分为两步
- 如果可以的话尝试运行 pre-stop lifecycle hooks
- Stop container
- 根据 pod.DeletionGracePeriodSeconds 或 pod.Spec.TerminationGracePeriodSeconds 设置优雅退出时间 gracePeriod,其中 DeletionGracePeriodSeconds 具有更高的优先级;另外优雅退出时间的最小值为 minimumGracePeriodInSeconds(2), 这是代码里写死的
- 通过调用
m.runtimeService.StopContainer(containerID.ID, gracePeriod)触发 container 的重建,该函数是 kubelet 的功能,位于 k8s.io/kubernetes/pkg/kubelet/cri/remote/remote_runtime.go
-
-
-
ImagePull (NodeImage)
pkg/daemon/imagepuller
用于在指定 node 去拉镜像
Controller
Controller 主要用于管理 NodeImage,核心逻辑都在 sync 函数里面
- 调用
c.puller.Sync(nodeImage.DeepCopy(), ref)来调用访问 worker 的 sync 函数以实现 image pull - 刷新 NodeImage 的 status 来反应当前 job 的状态
Worker
调度逻辑
调度的主要逻辑都在 sync 里,所以下面详述 sync 的逻辑
-
reset workerPools(map[string]workerPool): 把不在 spec 里的 work 停止并删除; 把 pools 里没有的worker 创建并加入; 最终使得 workerPools 里每个元素都与 NodeImage.Spec.Images 一一对应
-
对每个 worker(wokerPools[imageName]) 调用
pool.Sync(&imageSpec, imageStatus, ref)函数- 遍历 NodeImage.Spec.Tags 字段(用于管理多版本的 image),并检查 status 中是否存在对应的 tag,并根据 status 的结果来将 tags 分为 allTags 和 activeTags
-
apiVersion: apps.kruise.io/v1alpha1 kind: NodeImage metadata: name: my-app-image spec: image: my-registry/my-app tags: - v1.0 - v1.1 - latest - 清理 workerpool 中 non-active | version 不对的 worker:
delete(w.pullWorkers, tag), 这里 w 就是上一层函数的 pool - 对于 activeTags 中存在但 worker pool 里找不到的对象,起新的 worker
newPullWorker(w.name, tagSpec, secrets, w.runtime, w, ref, w.eventRecorder) -
func newPullWorker(name string, tagSpec appsv1alpha1.ImageTagSpec, secrets []v1.Secret, runtime runtimeimage.ImageService, statusUpdater imageStatusUpdater, ref *v1.ObjectReference, eventRecorder record.EventRecorder) *pullWorker { o := &pullWorker{ name: name, tagSpec: tagSpec, secrets: secrets, runtime: runtime, statusUpdater: statusUpdater, ref: ref, eventRecorder: eventRecorder, active: true, stopCh: make(chan struct{}), } go o.Run() return o }
工作逻辑
这里介绍 pullWorker Run 函数的逻辑
-
参数初始化阶段
- timeout = tagSpec.PullPolicy.TimeoutSeconds, 默认值 10min
- backoffLimit = tagSpec.PullPolicy.BackoffLimit, 默认值 3(最大重试次数)
- deadline = now + tagSpec.PullPolicy.ActiveDeadlineSeconds, 默认值为 nil (不设上限)
-
会根据上述三个参数来决定是否使任务失败,如果在最大重试次数之内且单次任务没超时且没超过deadline,那么会调用函数
doPullImage(pullContext, newStatus)-
调用
getImageInfo(ctx)拿到 image 信息,image 信息如下所示 -
type ImageInfo struct { // ID of an image. ID string `json:"Id,omitempty"` // repository with digest. RepoDigests []string `json:"RepoDigests"` // repository with tag. RepoTags []string `json:"RepoTags"` // size of image's taking disk space. Size int64 `json:"Size,omitempty"` } -
w.runtime.PullImage(ctx, w.name, tag, w.secrets)拉镜像-
该函数位于 pkg/daemon/criruntime/containerd.go
-
imageRef = imageName:tag (e.g.: my-registry/my-app:v1.0)
-
规范化镜像名
namedRef = daemonutil.NormalizeImageRef(imageRef) -
获取镜像解析器
resolver, isSchema1, err := d.getResolver(ctx, namedRef, pullSecrets),其返回值分别为镜像解析器和是否为 Docker Schema 1 格式的镜像还有错误信息- 尝试用传进来的 pullSecrets 解析镜像,如果解析失败则进入下一步
- 尝试用 containerdImageClient 的 accountManager 里记录的默认账户信息或匿名账户信息解析镜像
-
d.doPullImage(ctx, namedRef, isSchema1, resolver)-
创建一个
newFeatchJobs实例ongoing用于跟踪当前的拉取任务 -
创建
pipeR和pipeW, 分别代表管道读取器和管道写入器,pipeR 用于返回 ImagePullStatusReader -
创建一个基于 json 的
stream流,用于将 pullimage 进度信息写入pipeW -
使用
containerd.RemoteOpt选项配置了拉取镜像的参数。其中包括使用Schema1转换、指定解析器、解压缩拉取的镜像、指定快照存储器以及设置镜像处理程序。 -
起一个协程调用
fetchProgress函数使得ongoing的内容能够写入stream -
起一个协程拉镜像
-
首先调用了
img, err := d.client.Pull(pctx, ref.String(), opts...)拉镜像,其中ops 就是步骤 4 里的参数- 这里的 pull 函数是 k8s 原生的,用于将提供的内容(通常是镜像)下载到 container 的内容存储中
-
调用
createRepoDigestRecord更新 container 中的摘要信息(Digest)
-
-
-
-
Select 等待上述函数的信号,根据返回值判断任务执行情况并更新 status
-
-
根据 doPullImage 的结果来处理失败或者正常结束