剑客
关注科技互联网

docker swarmkit 源码阅读: Scheduler 部分

Scheduler 负责把 task 调度到合适的 node 上,文档中这么描述:

The scheduler assigns tasks to available nodes.

先看个相关的数据结构。

docker swarmkit 源码阅读: Scheduler 部分 docker swarmkit 源码阅读: Scheduler 部分 docker swarmkit 源码阅读: Scheduler 部分

Scheduler 的 unassignedTasks 保存没有 attach node 的 task;

preassignedTasks 保存已经 attach node 但是需要确认资源的 task;

nodeSet 以 node id 保存 node 上的 task 信息,同时会保存 node 自身信息和 node 可用资源信息(包括 CPU 和 MEMORY);

allTasks 记录所有 task(必须是 ALLOCATED 状态,也就是必须先收集网络资源才会被调度);

pipeline 记录 Filter 信息,用于判断过滤 node。

处理过程:

1. 首先生成 nodeSet 数据,从 store 中获取所有 task,对每一个 task 处理逻辑如下:

1). 如果 task 状态小于 TaskStateAllocated(必须先收集网络资源),或者大于 TaskStateRunning,忽略此 task;

2). 把 task 记录到 allTasks;

3). 如果 task 没有 attach node,放入 unassignedTasks,继续下一个;

4). 如果 task 状态是 TaskStateAllocated,放入 preassignedTasks,继续下一个;

这四步之后剩下的 task 是有效的 task,这些 task 会被记录到以 node id 为 key 的临时数据结构中。然后对所有 node 遍历,对每一个 node 生成 NodeInfo,生成 NodeInfo 的时候会计算可用资源,node.Description.Resources 减去 node 上每个 task 使用的资源是可用的资源,然后把 NodeInfo 保存到 nodeSet 中(以 node id 为 key,value 为 NodeInfo)。

补充一点:对于 service 的 task 来说,除了使用资源(叫做 Reservations ),是 task 最低被分配的资源,还有一个 Limits,定义 task 使用多少资源之后被 kill。

2. 处理 preassignedTasks 中的 task。

这里的 task 需要确认资源,也就是通过 Filter 的检测,如果通过检测,task 状态被改成 TaskStateAssigned,allTasks 中的 task 被覆盖,task 也会被加入到 nodeSet。

对于检测通过的 task,因为修改了状态,需要保存到 strore,保存成功的 task 从 preassignedTasks 删掉,保存失败的需要回滚 allTasks 信息 和 nodeSet 信息。

这里再补充一下 Filter 的机制,默认有三个 Filter:ReadyFilter、ResourceFilter 和 ConstraintFilter,ReadyFilter 表示此 node 是否可用,ResourceFilter 表示是否满足资源条件(CPU 和 MEMORY),ConstraintFilter 用于自定义 Label,node 需匹配 Label 定义的条件才算可用。

每个 Filter 都声明两个接口:SetTask 和 Check,SetTask 用于判断是否需要过滤,输入是 task,ReadyFilter 返回 true;ResourceFilter 如果 task 需要资源(Reservations 不为空)则返回 true,会把 Reservations 赋给 ResourceFilter;ConstraintFilter 如果 task 有 Spec.Placement.Constraints,解析出来赋给 ConstraintFilter,然后返回 true。

Check 才真正用于判断是否满足过滤条件,如果 SetTask 返回 false,就不用执行 Check,Check 的输入是 node,也就是判断此 node 是否满足 SetTask 设定的条件, 满足返回 true。

3.执行 tick,也就是对 unassignedTasks 进行调度,以 ServiceID 和 Spec 把 task 分组,然后分组调度,调度成功把 task 报存到 store 并从 unassignedTasks 删除,失败重新放入 unassignedTasks。

先为每一个组分配 Filter(执行 SetTask 逻辑,组中的 task 的 Filter 相同),然后调用 Filter 的 Check 逻辑,判断所有 node 是否满足 Filter 条件,对于满足条件的 node,按优先级排序,优先级按照 node 上的 task 数量,数量越少优先级越高。

然后对组中的 task,从优先级排序结果进行 attach,如果第 i 个 node 中的 task 数量小于第 i+1 个 node 上的 task 数量,那么还是使用第 i 个 node(所以一个 node 可以绑定一个 service 的多个 task),由于组中的 task 数量可能比排序结果 node 数量多,所以当 node 轮训一遍之后,会继续从开始轮训。

而且,每 attach 一个 node,会检查下一次要 attch 的 node 是否满足 Filter,如果不满足,那么继续下一个 node,如果「循环一圈」所有 node 都不满足,则把剩下所有没 attach 的 task 重新放入 unassignedTasks。

总结一下调度的策略:尽量先保持 node 上的 task 数量均衡,尽量保持 task 数量均衡的同时尽量做到所有 task 都分配到资源。这个策略下,存在多个 task 分配到一个 node 上的可能性。

4. 监控集群的 Event,对不同的 Event 做出不同的反应,对某些 Event 需要修改 unassignedTasks 和 preassignedTasks,这两者的修改会由一个调度器来执行。事实上,当 EventCommit 来临时才会执行,但不是立即执行,最快要等 50 ms 执行,最慢要等 1s,这段的代码写的挺好的。

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址