源码解析
注册过程
根据上一篇文章的代码:
1 | func (r Registry) Register(name string, factory PluginFactory) error { |
上述代码是Scheduler的实例化过程,我们可以看到它注册了内置插件,然后合并了自定义插件,然后放到profile中。
frameworkplugins.NewInTreeRegistry
我们先来看看Register:
1 | // Register是一个可用插件的集合,框架使用registry来启用并且初始化插件的配置。 |
可以看到,Register是一个map,value为插件的名称(Name),值为插件的工厂方法。
上文调用了一个frameworkplugins.NewInTreeRegistry()方法:
1 | // Features被不同的插件使用,携带features gate值 |
- Features结构体是Kubernetes调度器中的一些特性开关。每个字段对应一个开关,代表该特性是否启用。关于每个字段的说明已经在上述代码中标注。
- featureGate结构体内有一个enabled属性,类型为
*atomic.Value,*atomic.Value主要是用来对其中的值进行原子化写入操作,具体可以看看这篇文章,其值为一个map类型,因为map不是线程安全的。 registry := runtime.Registry实例化了一个Registry结构体,runtime.FactoryAdapter主要是为了兼容旧版本插件的New方法
profile.NewMap()
1 | type cfgValidator struct { |
newProfile解析配置,然后将KubeSchedulerConfiguration的配置加载到新建的Framework结构体(可以视为一个Scheduler,包含了拓展点及其插件列表),最后返回一个key为SchedulerName,value为Scheduler的结构Map。在newProfile内通过,实例化了一个Framework结构体,并调用了Register的工厂方法,实例化插件。
一些调度器
内置的代码可以在kubernetes的源代码库找到,其他的一些外置的插件,可以在这里找到。
NodeName
它是一个非常常见的插件,它的作用是指定Pod所要调度的节点的名称,即spec.NodeName。他的代码很短,我们来看看:
1 | type NodeName struct{} |
- 首先,NodeName是一个空的结构体,它主要是在Filter拓展点实现了Filter方法。
- Filter方法调用了Fits方法,该方法用
pod.Spec.NodeName和nodeInfo.Node().Name相比较,如果相等,则返回true - 当
pod.Spec.NodeName和nodeInfo.Node().Name不想等,则返回一个Status状态,说明Node不匹配的原因 - 当Pod匹配时,返回nil
NodeAffinity
NodeAffinity是比较常见的一个插件,我们知道,Affinity有两种类型,一种表示偏好,即preferredDuringSchedulingIgnoredDuringExecution,一种表示必须满足,即requiredDuringSchedulingIgnoredDuringExecution。
所以我们来看NodeAffinity的源码,我们可以看到:
1 | type NodeAffinity struct { |
其中addedNodeSelector代表requiredDuringSchedulingIgnoredDuringExecution,addedPrefSchedTerms代表requiredDuringSchedulingIgnoredDuringExecution。
NodeAffinity实现了PreFilter,Filter,PreScore,Score拓展点,我们按顺序来看PreFilter:
1 | func (pl *NodeAffinity) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) { |
- 这个方法的实现类似于NodeSelector,首先是判断是否定义了
RequiredDuringSchedulingIgnoredDuringExecution字段,如果没有则进入下一个拓展点 - 接着,将标签选择和节点选择的信息写到cycleState方便后续的拓展点处理
- 最后,判断是否存在节点选择的信息,即Key为meta.name,匹配模式为IN,如果有则返回选择的节点列表,如果没有则直接进入下一步处理
然后是Filter:
1 | func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status { |
- 首先查找
RequiredDuringSchedulingIgnoredDuringExecution是否能找到相应的节点 - 然后获取
PreFilter的cycleState结构体,这个是已经在PreFilter计算过并保存的结构体,这个结构体中保存了NodeSelector和NodeAffinity的信息 - 最后判断
NodeSelector和NodeAffinity的信息是否冲突
同样的,PreScore也是对Score进行预处理,将preScoreState存入cycleState结构中:
1 | func (pl *NodeAffinity) PreScore(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status { |
Score:
1 | // 当matchExpressions和matchFields匹配的时候,将weight加入计算,最终返回weight之和 |
- 该方法对匹配的节点规则进行weight的累加
- AddedAffinity是在调度规则中指定的,对指定的Pod生效(通过
schedulerName),可以看这个文章 - preferredNodeAffinity规则在Pod的yaml中指定