调度器启动后最终是调用 下面的 函数来开始调度 ,如下所示代码:
// pkg/scheduler/scheduler.go
// scheduleOne 为单个 Pod 完成整个调度工作流程
func (sched *Scheduler) scheduleOne(ctx context.Context) {
......
// 同步尝试调度 Pod 找到适合的节点
start := time.Now()
state := framework.NewCycleState()
state.SetRecordPluginMetrics(rand.Intn(100) < pluginMetricsSamplePercent)
schedulingCycleCtx, cancel := context.WithCancel(ctx)
defer cancel()
// 执行 Schedule 调度函数进行真正的调度
scheduleResult, err := sched.Algorithm.Schedule(schedulingCycleCtx, prof, state, pod)
if err != nil {
// Schedule() 函数执行可能会失败,因为 Pod 无法在任何主机上运行
// 因此我们尝试进行抢占,并期望下一次尝试Pod进行调度时,因为抢占而可以正常调度
// 但也有可能不同的 Pod 会调度到被抢占的资源中,但这没有什么影响
nominatedNode := ""
if fitError, ok := err.(*core.FitError); ok {
if !prof.HasPostFilterPlugins() {
// 没有注册 PostFilter 插件
klog.V(3).Infof("No PostFilter plugins are registered, so no preemption will be performed.")
} else {
// 运行 PostFilter 插件,尝试在未来的调度周期中使 Pod 可调度
result, status := prof.RunPostFilterPlugins(ctx, state, pod, fitError.FilteredNodesStatuses)
......
if status.IsSuccess() && result != nil {
nominatedNode = result.NominatedNodeName // 被提名的节点名
}
}
}
// recordSchedulingFailure 为 pod记录一个事件,表示 pod 调度失败
// 另外,如果设置了pod condition 和 nominated 提名节点名称,也要更新
// 这里最重要的是要将调度失败的 Pod 加入到不可调度 Pod 的队列中去
sched.recordSchedulingFailure(prof, podInfo, err, v1.PodReasonUnschedulable, nominatedNode)
return
}
// 告诉 cache 暂时绑定的 Pod 现在正在指定节点上运行,即使尚未绑定
// 这样我们就可以保持调度,而不必等待真正的绑定发生
assumedPodInfo := podInfo.DeepCopy() // 拷贝现在调度的 Pod 信息
assumedPod := assumedPodInfo.Pod
// assume 是通过设置 NodeName=scheduleResult.SuggestedHost 来修改 `assumedPod` 的
err = sched.assume(assumedPod, scheduleResult.SuggestedHost)
if err != nil {
......
}
// 运行 reserve 插件的 Reserve 方法(预留)
if sts := prof.RunReservePluginsReserve(schedulingCycleCtx, state, assumedPod, scheduleResult.SuggestedHost); !sts.IsSuccess() {
// 触发 un-reserve 插件以清理与 reserve 的 Pod 相关联的状态
prof.RunReservePluginsUnreserve(schedulingCycleCtx, state, assumedPod, scheduleResult.SuggestedHost)
// 从缓存中移除临时绑定的 Pod
if forgetErr := sched.Cache().ForgetPod(assumedPod); forgetErr != nil {
klog.Errorf("scheduler cache ForgetPod failed: %v", forgetErr)
}
......
}
// 运行 "permit" 插件
runPermitStatus := prof.RunPermitPlugins(schedulingCycleCtx, state, assumedPod, scheduleResult.SuggestedHost)
if runPermitStatus.Code() != framework.Wait && !runPermitStatus.IsSuccess() {
// 当 permit 插件结果不是 Wait 并且没有执行成功的时候,进行错误处理
var reason string
if runPermitStatus.IsUnschedulable() {
reason = v1.PodReasonUnschedulable
} else {
reason = SchedulerError
}
// 其中一个插件返回的状态不等于 success 或 wait 的状态
// 触发 un-reserve 插件
prof.RunReservePluginsUnreserve(schedulingCycleCtx, state, assumedPod, scheduleResult.SuggestedHost)
// 从缓存中移除临时绑定的 Pod
if forgetErr := sched.Cache().ForgetPod(assumedPod); forgetErr != nil {
klog.Errorf("scheduler cache ForgetPod failed: %v", forgetErr)
}
// 记录调度失败事件,主要我们关心的是如何加入到另外两个不可调度队列中去
sched.recordSchedulingFailure(prof, podInfo, err, v1.PodReasonUnschedulable, nominatedNode)
return
}
......
}
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
# generate-groups.sh <generators> <output-package> <apis-package> <groups-versions>
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \\
k8s.io/sample-controller/pkg/generated k8s.io/sample-controller/pkg/apis \\
samplecontroller:v1alpha1 \\
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \\
--go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt
调度器启动后最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用最终是调用
活动队列(activeQ)是存储当
平时我们在创建 Pod 后如果执行失败了最终是调用最终是调用最终是调用