资源调度

源代码在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的原始论文

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::ReserveOffer::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

只是输出一些信息,未有实际动作

results matching ""

    No results matching ""