【调度器插件专题】Pod State Scheduling 基于节点 pod 状态进行打分的调度器插件

Posted by Hao Liang's Blog on Sunday, October 24, 2021

这是我于2020年在 Kubernetes sig-scheduling 小组的 scheduler-plugin 开源项目贡献的第一个调度器插件。

1、 背景

相关PR:PR103: Pod State Scheduling Plugin

源码地址:Pod State Scheduling

  • 当前 Kubernetes 原生的调度器打分算法(Score)没有考虑节点上已有的 Terminating 状态的 Pod
  • 当前 Kubernetes 原生的调度器打分算法(Score)没有考虑节点上已有的 Nominated 状态的 Pod

2、功能简介

Pod State scheduling 调度器插件实现了一个打分的扩展插件(Score Plugin)

  • 节点上有越多 Terminating 状态的 Pod 在打分阶段会获得越高的分数(Pod 即将和节点解绑释放资源,期望获得更高分数)
  • 节点上有越多 Nominted 状态的 Pod 在打分阶段会获得越低的分数(Pod 即将和节点绑定占用资源,期望获得更低分数)

3、实现原理

打分算法的实现原理:

  • 分别统计每个节点上 Terminating 和 Nominated 状态的 Pod 数量
  • 对每个 Terminating 状态的 Pod 分数加 1
  • 对每个 Nominated 状态的 Pod 分数减 1

func (ps *PodState) score(nodeInfo *framework.NodeInfo) (int64, *framework.Status) {
	var terminatingPodNum, nominatedPodNum int64
	// get nominated Pods for node from nominatedPodMap
	nominatedPodNum = int64(len(ps.handle.PreemptHandle().NominatedPodsForNode(nodeInfo.Node().Name)))
	for _, p := range nodeInfo.Pods {
		// Pod is terminating if DeletionTimestamp has been set
		if p.Pod.DeletionTimestamp != nil {
			terminatingPodNum++
		}
	}
	return terminatingPodNum - nominatedPodNum, nil
}

规整分数范围:

  • 分别统计得分最高和最低节点的分数
  • 用最高分减去最低分确定源分数范围(oldRange)
  • 用打分插件配置的最高分减去最低分确定目标分数范围(newRange)
  • 计算公式:((每个节点的真实得分 - 所有节点的最低分) * newRange / oldRange) + 打分插件配置的最低分

func (ps *PodState) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
	// Find highest and lowest scores.
	var highest int64 = -math.MaxInt64
	var lowest int64 = math.MaxInt64
	for _, nodeScore := range scores {
		if nodeScore.Score > highest {
			highest = nodeScore.Score
		}
		if nodeScore.Score < lowest {
			lowest = nodeScore.Score
		}
	}

	// Transform the highest to lowest score range to fit the framework's min to max node score range.
	oldRange := highest - lowest
	newRange := framework.MaxNodeScore - framework.MinNodeScore
	for i, nodeScore := range scores {
		if oldRange == 0 {
			scores[i].Score = framework.MinNodeScore
		} else {
			scores[i].Score = ((nodeScore.Score - lowest) * newRange / oldRange) + framework.MinNodeScore
		}
	}

	return nil
}

3、使用手册

调度器插件配置样例:

apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
leaderElection:
  leaderElect: false
clientConnection:
  kubeconfig: "REPLACE_ME_WITH_KUBE_CONFIG_PATH"
profiles:
- schedulerName: default-scheduler
  plugins:
    score:
      enabled:
      - name: PodState