阅读本节前请了解 BroadcastJob 的使用和功能
系列教程: Openkruise
关键词: each nodo one job
Reconcile
预处理
-
打 lables(
addLabelToPodTemplate(job)) broadcastjob-name = job.Name broadcastjob-controller-uid = job.UID -
通过函数
IsJobFinished(job)和pastTTLDeadline(job)检测 job 是否超时,相关字段: TTLSecondsAfterFinished -
获取所对应的 pods 以及 node,生成一个 (node, pod) map
existingNodeToPodMap, 该 map 一定是一对一关系 -
根据 pod 的 Status.Phase 对这些 pods 进行分类,分别放在三个数组里:
activePods, failedPods, succeededPods := filterPods(job.Spec.FailurePolicy.RestartLimit, pods) -
将 nodes 也分类
desiredNodes, restNodesToRunPod, podsToDelete := getNodesToRunPod(nodes, job, existingNodeToPodMap)desiredNodes: 目标态,即需要在哪些 nodes 上运行 podrestNodesToRunPod: 应该有 running pod 但还没有的podsToDelete: pod 和 node 不匹配,但 pod 还挂在上面的- 通过函数
checkNodeFitness(pod *corev1.Pod, node *corev1.Node)检测包括但不限于 名字/affinity/taint 等是否匹配 - 对于未存在的 pod,通过先 mock 再验证,最后放入
restNodesToRunPod来实现 -
mockPod := NewMockPod(job, node.Name) canFit, err = checkNodeFitness(mockPod, &node)
- 通过函数
状态流转
通过预处理我们知道了各个pod应有的分布和当前的状态,那么接下来就需要根据当前的状态和策略来分叉
失败处理
必须是 jobFailed 的时候进行,进入该流程有以下两个途径
-
failedPods > 0 && completionPolicy == FailFast
-
jobFailed, failureReason, failureMessage = isJobFailed(job, pods) 第一个返回值为 true
- 该函数对于 Never 失败策略永远返回 (false, "", "")
- 如果 job 执行时间超过 ActiveDeadlineSeconds,也会被标记为失败
失败处理流程主要与失败策略(FailurePolicyType)字段有关, 该字段有三个可选值
-
Continue: 什么都不做继续流程
-
FailFast: 立即失败
- 删除 active pods:
failed, active, err = r.deleteJobPods(job, activePods, failed, active),这里删除失败的 pod 数也会被记入 failed(int) - Job 状态流转为 failed
- 通过
finishJob(job, appsv1alpha1.JobFailed, failureMessage)来获取 job 的结束时间,如果标记了TTLSecondsAfterFinished,则返回该值 + 1 second,否则返回 0
- 删除 active pods:
-
Pause: job 状态流转为 paused,且标记 job.Spec.Paused = true
调整 pod 分布
根据 restNodesToRunPod 和 podsToDelete 来使得 pod 分布在正确的 node 上
-
首先删除 podsToDelete 中的 pod:
failed, active, err = r.deleteJobPods(job, podsToDelete, failed, active) -
尝试创建 pod:
active, err = r.reconcilePods(job, restNodesToRunPod, active, desired)-
创建受参数 Parallelism 限制( if exist ),如果当前 active pod > parallelism, 则跳过;否则尝试将尝试创建 min(parallelism-active, rest) 个 pod
- 这里创建仍然采用慢启动算法,即最开始 1 个,然后二倍增长, 每一个批次起多个协程来创建
-
for batchSize := integer.Int32Min(diff, kubecontroller.SlowStartInitialBatchSize); diff > 0; batchSize = integer.Int32Min(2*batchSize, diff) {......}
-
Job 结束条件判断
尝试结束 job isJobComplete(job, desiredNodes)
- 如果完成策略是 Never,则 job 用不结束
- 如果 desiredNodes 数组是空的,则 job 会 pending
- 如果每个 pod in desiredNodes 都 active 了,则顺利完成