资源调度
源代码在src/master/allocator目录,处理资源分配。
概述
mesos资源
有四种资源:
- cpu:表示有多少个CPU核可用。任务可以使用非整数的CPU资源。若任务有1.5CPU资源,则其进程在每一秒CPU时间中可以占用1.5秒CPU时间,若是两个CPU核,则每个核每秒占用750毫秒。若负载很低时,任务可能使用超出其额度的CPU时间。
- mem:能够使用多少空间内存,是不允许超过的。
- disk:能够使用多少磁盘空间。缺省可以超过,若启动agent时有
--enforce_container_disk_quota
参数,则不可超过。 - port:若资源offer中的port不满足要求,framework可以拒绝该资源。也可以指定port范围,缺省是31000-32000。
可以指定不同角色分配资源的不同权重,例如--roles=dev,qa,prod
和--weights='dev=2,qa=1,prod=3
,这样dev得到的资源是qa的两倍,而prod得到的是qa的三倍。
资源分配流程
DRF算法
DRF的目标是确保每一个用户,即Mesos中的Framework,在异构环境中能够接收到其所需资源的公平份额。为了掌握DRF,我们需要了解主导资源(dominant resource)和主导份额(dominant share)的概念。
Framework的主导资源是其所需的资源类型(CPU、内存等),在资源请求中以可用资源百分比的形式展示。
例如,对于计算密集型的任务,它的Framework的主导资源是CPU,而依赖于在内存中计算的任务,它的Framework的主导资源是内存。因为资源是分配给Framework的,所以DRF会跟踪每个Framework拥有的资源类型的份额百分比;Framework拥有的全部资源类型份额中占最高百分比的就是Framework的主导份额。DRF算法会使用所有已注册的Framework来计算主导份额,以确保每个Framework能接收到其主导资源的公平份额。
假设我们总的资源,包含9核CPU和18GB的内存。Framework 1运行任务需要(1核CPU、4GB内存),Framework 2运行任务需要(3核CPU、1GB内存) Framework 1的每个任务会消耗CPU总数的1/9、内存总数的2/9,因此Framework 1的主导资源是内存。同样,Framework 2的每个任务会CPU总数的1/3、内存总数的1/18,因此Framework 2的主导资源是CPU。DRF会尝试为每个Framework提供等量的主导资源,作为他们的主导份额。在这个例子中,DRF将协同Framework做如下分配:Framework 1有三个任务,总分配为(3核CPU、12GB内存),Framework 2有两个任务,总分配为(6核CPU、2GB内存)。
此时,每个Framework的主导资源(Framework 1的内存和Framework 2的CPU)最终得到相同的主导份额(2/3或67%),这样提供给两个Framework后,将没有足够的可用资源运行其他任务。需要注意的是,如果Framework 1中仅有两个任务需要被运行,那么Framework 2以及其他已注册的Framework将收到的所有剩余的资源。
DRF是怎样计算而产生上述结果的呢?如前所述,DRF分配模块跟踪分配给每个Framework的资源和每个框架的主导份额。每次,DRF以所有Framework中运行的任务中最低的主导份额作为资源offer发送给Framework。如果有足够的可用资源来运行它的任务,Framework将接受这个offer。通过前面引述的DRF论文中的示例,我们来贯穿DRF算法的每个步骤。为了简单起见,示例将不考虑短任务完成后,资源被释放回资源池中这一因素,我们假设每个Framework会有无限数量的任务要运行,并认为每个资源offer都会被接受。
回顾上述示例,假设有一个资源offer包含9核CPU和18GB内存。Framework 1运行的任务需要(1核CPU、4GB内存),Framework 2运行的任务需要(3核CPU、2GB内存)。Framework 1的任务会消耗CPU总数的1/9、内存总数的2/9,Framework 1的主导资源是内存。同样,Framework 2的每个任务会CPU总数的1/3、内存总数的1/18,Framework 2的主导资源是CPU。
上面表中的每一行提供了以下信息:
- Framework chosen——收到最新资源offer的Framework。
- Resource Shares——给定时间内Framework接受的资源总数,包括CPU和内存,以占资源总量的比例表示。
- Dominant Share(主导份额)——给定时间内Framework主导资源占总份额的比例,以占资源总量的比例表示。
- Dominant Share %(主导份额百分比)——给定时间内Framework主导资源占总份额的百分比,以占资源总量的百分比表示。
- CPU Total Allocation——给定时间内接受的所有Framework的总CPU资源。
- RAM Total Allocation——给定时间内接受的所有Framework的总内存资源。
最初,两个Framework的主导份额是0%,我们假设DRF首先选择的是Framework 2,当然我们也可以假设Framework 1,但是最终的结果是一样的。
- Framework 2接收份额并运行任务,使其主导资源成为CPU,主导份额增加至33%。
- 由于Framework 1的主导份额维持在0%,它接收共享并运行任务,主导份额的主导资源(内存)增加至22%。
- 由于Framework 1仍具有较低的主导份额,它接收下一个共享并运行任务,增加其主导份额至44%。
- 然后DRF将资源offer发送给Framework 2,因为它现在拥有更低的主导份额。
- 该过程继续进行,直到由于缺乏可用资源,不能运行新的任务。在这种情况下,CPU资源已经饱和。
- 然后该过程将使用一组新的资源offer重复进行。
需要注意的是,可以创建一个资源分配模块,使用加权的DRF使其偏向某个Framework或某组Framework。如前面所提到的,也可以创建一些自定义模块来提供组织特定的分配策略。
加权DRF
通常很少用完全公平的DRF,而是在不同用户间设置不同权重、享受不同的资源份额。例如,一个权重为2的role得到的资源offer两倍于权重为1的role。
在master启动时可以使用--weights
和--roles
标志来使用加权的DRF。--weights
标志需要一个role1=weight1
的列表,权重不必一定是整数。
资源预留
资源预留保证重要的服务能够及时得到资源,包括静态预留和动态预留。
静态预留是将资源留给特定的角色(role),这样只有使用该角色的framework能够得到这些资源。预留是通过启动slave时传递--resources标志进行的。例如:
--res
ources="cpus:4;mem:2048;cpus(ads):8;mem(ads):4096"
表明:
slave有8CPU,4096MB内存预留给角色ads,另外的4CPU和2048MB内存未预留给任何人。
动态预留允许操作人员和framework动态预留资源。当收到资源offer时,framework发送Offer::Operations::Reserve
及Offer::Operations::Unreserve
消息来进行预留操作。
例如,假设framework收到这样一个包括32CPU和65536MB内存的资源offer:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "cpus",
"type" : "SCALAR",
"scalar" : { "value" : 32 },
"role" : "*",
},
{
"name" :"mem",
"type" :"SCALAR",
"scalar" : { "value" : 65536 },
"role" : "*",
}
]
}
framework想预留8CPU及4096MB内存,它需要发送一个Operation::Reserve消息,消息中的resources域是以下内容:
[
{
"type" : Offer::Operation::RESERVE,
"resources" : [
{
"name" : "cpus",
"type" : "SCALAR",
"scalar" : { "value" : 8 },
"role" : <framework_role>,
"reservation" : {
"framework_id" : <framework_id>,
"principal" : <framework_principal>
}
}
{
"name" : "mem",
"type" : "SCALAR",
"scalar" : { "value" : 4096 },
"role" : <framework_role>,
"reservation" : {
"framework_id" : <framework_id>,
"principal" : <framework_principal>
}
}
]
}
]
若成功,framework会收到预留的资源offer,下一个offer可能是这样:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "cpus",
"type" : "SCALAR",
"scalar" : { "value" : 8 },
"role" : <framework_role>,
"reservation" : {
"framework_id" : <framework_id>,
"principal" : <framework_principal>
}
},
{
"name" : "mem",
"type" : "SCALAR",
"scalar" : { "value" : 4096 },
"role" : <framework_role>,
"reservation" : {
"framework_id" : <framework_id>,
"principal" : <framework_principal>
}
},
{
"name" : "cpus",
"type" : "SCALAR",
"scalar" : { "value" : 24 },
"role" : "*",
},
{
"name" : "mem",
"type" : "SCALAR",
"scalar" : { "value" : 61440 },
"role" : "*",
}
]
}
还可以通过HTTP的/reserve和/unreserve来进行预留操作,例如,以下命令在slave1上预留4CPU及4096MB内存给角色role1:
ubuntu@master:~ $ curl -d slaveId=slave1 -d resources="{
{
"name" : "cpus",
"type" : "SCALAR",
"scalar" : { "value" : 4 },
"role" : "role1",
"reservation" : {
"principal" : "ops"
}
},
{
"name" : "mem",
"type" : "SCALAR",
"scalar" : { "value" : 4096 },
"role" : "role1",
"reservation" : {
"principal" : "ops"
}
},
}"
-X POST http://master:5050/master/reserve
资源及属性定义
mesos只传递属性,属性定义由framework解释。
--resources='cpus:30;mem:122880;disk:921600;ports:[21000-29000];bugs:{a,b,c}'
--attributes='rack:rack-2;datacenter:europe;os:ubuntuv14.4'
master分配资源给framework
allocator.cpp
是初始化函数。
- 40-43:若创建的allocator名字是DEFAULT_ALLOCATOR,则调用HierarchicalDRFAllocator::create(),这是mesos的缺省分配器
- 44:否则,根据名字创建(新的分配器可以写模块)
modules::ModuleManager::create<Allocator>(name)
mesos/hierarchical.cpp
定义mesos缺省资源分配。
类型定义
mesos/hierarchical.hpp 58-59行:
typedef MesosAllocator<HierarchicalDRFAllocatorProcess>
HierarchicalDRFAllocator
mesos/allocator.hpp定义了模板MesosAllocator
template <typename AllocatorProcess>
class MesosAllocator : public mesos::allocator::Allocator
因此,HierarchicalDRFAllocator是一个基于HierarchicalDRFAllocatorProcess的MesosAllocator
mesos/allocator.hpp 290-297定义了默认的create动作:只是新建一个MesosAllocator
316-647:initialize等则是对process调用MesosAllocatorProcess类相应的函数,这些函数包括:
- initialize
例如,对 HierarchicalDRFAllocator->addFramework的实现就是:
process::dispatch(
process,
&MesosAllocatorProcess::addFramework,
frameworkId,
frameworkInfo,
used);
即是调用MesosAllocatorProcess::addFramework
其中process的定义在allocator.hpp的163行MesosAllocatorProcess* process
HierarchicalAllocatorProcess::allocate
1201-1549
updateWhitelist
只是输出一些信息,未有实际动作