免费开通大数据服务:https://www.aliyun.com/product/odps
转载自boyan
概述
ServiceModeJob(又名:OnlineJob)是fuxi提供的一套准实时计算框架,通过毫秒级的调度开销和网络Shuffle模式为小Job提供更高的性能。目前ODPS对内生产集群约1/3的Job通过ServiceModeJob进行处理,对其中小Job比较多的集群,这个占比会提高到70%。
由于同一套ServiceMode服务会有多个Project的Job共用。需要对各个Project的Job进行资源隔离和弹性调度,ServiceMode提供了一套面向多租户(Quota Group)的完整解决方案。
用户场景
ServiceModeJob多租户功能需要解决以下几个用户场景:
- 不同Project作业之间的资源隔离和分组计费功能
- 在不同时段,各个Project对ServiceMode服务的使用需求不同,需要提供分时Quota的功能
- ServiceMode服务的Worker数可能会预先指定,构成一个SharedWorkerPool,各个Quota组的配置情况需要与Worker数保持对应。
- 根据当前服务的剩余资源和各Quota组的使用情况,对各组Job进行弹性调度,并支持配置基线作业优先级,保证生产基线Job。
- 鉴于ServiceMode服务All-or-Nothing的调度策略,在Quota组作业调度过程中需要尽可能减小资源调度碎片,提高资源使用率。
针对上述的几个用户场景,我们逐一对功能点进行展开。
分时段的资源隔离和分组计费
每个租户根据自己的需要,配置相应的Quota Group。一个Quota Group包含{MaxQuota,MinQuota}两个配置。MaxQuota表示这个组最大能够持有的资源,MinQuota表示这个组最低期望获得的资源。由于各租户在不同时段对ServiceMode的使用需求不同,因此租户的配置是随时段改变的。
各租户的配置信息将组合成一张出资表,ServiceMode服务将根据出资表中的各组设置进行Job调度和资源分配。一个典型的出资表如下所示:
{
"default":
{
"group1":
{
"GroupId":1,
"MaxQuota":5000,
"MinQuota":3000
},
"group2":
{
"GroupId":2,
"MaxQuota":5000,
"MinQuota":2000
}
},
"0-9":
{
"group1":
{
"GroupId":1,
"MaxQuota":15000,
"MinQuota":10000
},
"group3":
{
"GroupId":3,
"MaxQuota":3000,
"MinQuota":3000
}
}
}
整个出资表是一个多级Map结构。第一级的Key是时段,“0-9”表示0点到9点的配置,"default"表示默认配置。第二级的Key是各个QuotaGroup的名字。最后是各个组在不同时段的配置。"GroupId‘是QuotaGroup的标识ID,用于和FuxiMaster角色同步校验QuotaGroup的配置。提交到ServiceMode上的Job,只有所属的Group在该时段有对应的出资才会得到调度。如例子中的group3仅在"0-9"时段有出资,因此在其他时段group3的作业不会得到调度。
一个组的MinQuota配置的越大,调度过程中越容易拿到资源;MaxQuota配置的越大,在空闲资源比较多时调度弹性会越大。在调度过程中,会保证每个组MinQuota的资源需求和MaxQuota的弹性收缩。具体的调度算法将在后续Section进一步展开。
线上集群在ServiceMode服务打开Quota模式以后,可以根据每个QuotaGroup的UsedQuota情况进行分组的计费。如下图所示:
SharedWorker Pool
线上集群的ServiceMode服务Worker数目一般都进行了预先指定。这也是一个分时段的配置,我们称之为WorkerSpans配置,例如下面的配置表示0-9点的Worker数是15000个(WorkerSpansNum=15000WorkerSpansNum=15000),其他时段是5000个。
default:5000,0-9:15000
出资表中每个时段的TotalMinQuota=∑Nn=1(MinQuota)TotalMinQuota=∑n=1N(MinQuota),在设置时必须保证WorkerSpansNum>=TotalMinQuotaWorkerSpansNum>=TotalMinQuota,而WorkerSpansNum−TotalMinQuotaWorkerSpansNum−TotalMinQuota即是该时段的SharedWorkerPool。这批SharedWorker将在调度过程中作为额外的弹性资源分配给需要的QuotaGroup,因此WorkerSpansNum<=TotalMaxQuota=∑Nn=1(MaxQuota)WorkerSpansNum<=TotalMaxQuota=∑n=1N(MaxQuota)。
在实际的场景里,SharedWorkerPool是一个动态grow/shrink的池子。比如大部分组的资源请求都很少,只有一个组的资源请求>MinQuota且<=MaxQuota,这个时候所有的空闲资源都可以认为是SharedWorkerPool。
Quota资源弹性调度算法
每个Quota Group实际使用的资源称为UsedQuota,根据它和MaxQuota,MinQuota的关系,会有以下几种组状态。
enum QuotaState
{
Normal, /* UsedQuota <= MinQuota */
Overused, /* UsedQuota > MinQuota && UsedQuota < MaxQuota */
Drawback, /* UsedQuota >= MaxQuota */
}
当一个组的状态处于Normal时,系统会优先对该组进行调度。如果当前没有来自于Normal组的请求,会调度处于Overused状态的组Job。而对于Drawback组,系统不会响应该组的任何资源请求,直到该组的的Job进入终态释放资源后,组状态迁移回Overused或Normal,才会重新开始调度。
每个Quota Group在调度过程中会对应一个调度队列,每个调度队列内部会基于组状态,作业优先级和提交时间等规则进行排序(排序的规则后面一节会具体展开)。调度规则的伪代码如下:
while (true)
{
while (true)
{
1. 选择一个topJob:
1.1 如果有EmergentJob,即作为topJob,并reset EmergentJob为空;
1.2 否则,从各个group的队列(pri, FIFO)选择队列头job,如果队列头job有基线任务(prodLevel)则优先分配;如果没有则按(usedQuota<minQuota, pri, FIFO)规则进行遍历,可以认为usedQuota越小,作业优先级越高,提交越早的即为topJob:
2. 调度topJob:
a. freeRes >= topJob.size,则直接assign给topJob,并从对应的group队列中删除。如果在assign时所在group已经是usedQuota > minQuota,则该job加入到toPreemptList(可被抢占的)。freeRes可能有剩余的,continue进入下一轮调度。
b. freeRes < topJob.size,如果没开抢占,本轮调度失败,topJob作为EmergentJob。如果开抢占:
- 如果topJob所在group是usedQuota < minQuota,就获得抢占资格,去toPreemptList中从前往后抢占直到资源足够topJob的请求。
- 否则没有抢占资格,本轮调度失败,topJob作为EmergentJob。
}
}
SubmitWindow
无论是Quota Group组内的排序还是挑选topJob都依赖在上文中提到的排序规则。排序规则首先应该考虑的是作业的优先级。但在作业优先级相同的情况下,由于ServiceMode系统的All-or-Nothing调度特性,如果仅仅考虑提交时间可能会产生比较大的调度碎片,使一些规模比较小的Job出现等资源超时的现象(等资源时间超过10秒钟)。
因此,我们结合每个job的规模和提交的时间,提出了SubmitWindow的方法来解决这一问题。根据每个Job提交的时间,以5秒(可配参数)为一个时间间隔,将Job划分到不同的SubmitWindow中。SubmitWindow靠前的Job会优先被调度,而对于SubmitWindow相同的Job,Job的Request资源数目越少的调度优先级越高。
struct SchItemPerJobPriorityComparer
{
// priority > submitWindow > aonReqCnt > createTime
bool operator()(const SchItemPerJobPtr &job1, const SchItemPerJobPtr &job2) const
{
if (job1->priority < job2->priority) return true;
else if (job1->priority > job2->priority) return false;
else if (job1->submitWindow < job2->submitWindow) return true;
else if (job1->submitWindow > job2->submitWindow) return false;
else if (job1->mTotalAonReq < job2->mTotalAonReq) return true;
else if (job1->mTotalAonReq > job2->mTotalAonReq) return false;
else if (job1->submitTime < job2->submitTime) return true;
else return false;
}
};
总结
目前,ServiceMode多租户功能已经上线了多个生产集群。从实际的上线效果看,多租户功能达到了资源隔离和分组计费等重要作用。我们统计上线前后一个月时间内ServiceMode服务上作业的“等资源超时次数”(即等待资源的时间超过10秒钟),如下图。数据显示quota有助于保障各个租户能更及时地拿到资源,11日升级后等资源超时次数大幅下降,从升级前平均2363次下降到1636次,下降幅度31%。从等资源超时Job/总提交Job数的占比情况分析,也能得出这一结论。
蓝色线:等资源超时Failed的Job数目
红色线:等资源超时Failed的Job数/总提交Job数 占比值
欢迎加入“数加·MaxCompute购买咨询”钉钉群(群号: 11782920)进行咨询,群二维码如下: