你肯定去餐厅点过菜,假如当时有很多顾客都在点菜,你会发现饭店有自己的策略来安排有限的厨师来为大家服务。让每个人的请求都得到响应,不至于等太久。这就是一个资源调度问题,在Yarn里如果多个客户端同时提交Job,Yarn需要怎么处理呢?这就是我们这篇文章需要分析的内容。
首先我们看一下最简单的资源调度策略,先进先出(FIFO),意思就是说所有资源只能同时为一个job所有,当这个job执行完成后才可以分配给其他人。所有job按照提交顺序排队:

这样就有一个问题,比如上图中的job1,它运行时间非常长,但是job2的运行时间很短,但是因为job2比job1提交晚了那么一点而不得不等待很长时间。
这就好比你去超市排队结账,你只买个口香糖,但是先你一步有个人推着满满一购物车的人排在你前面。这样的集群处理时间让提交job的人捉摸不透,有时候很快,有时候很慢。这对一些追求时效性的job就不合适了。有的超市有五件以下快速通道来解决这个问题。它实际上就是结账这个job进行了分组。5件以上是大任务,以下是小任务。不同的任务有不同的队列来处理。这样保证了小任务的快速响应。
在Yarn里,通过分组,保证每个组的资源,并且可以保证分给每个组的资源不会被其他组占用。这个策略就是Capacity Scheduler

容量调度器 Capacity Scheduler

容量调度器允许把集群里的资源划分到不同的组,用户可以将job提交到一个组。同一个组的job按照FIFO排序执行。

它看起来像这样来分配资源。资源被分配到了Group A,Group B里,它们彼此隔离。但是带来一个问题就是,在Group A里的任务Job2,Job3都执行完成,Group B里的Job1还没有执行完成,但是也不能使用Group A里空闲的资源。这个问题容量调度器也考虑到了,默认情况下如果一个Group有job运行,它会占用其他Group的空闲资源,等到其他Group有Job需要运行,再释放资源。资源的释放也分为两种模式,默认的方式是温和的,就是等到占用的资源自然释放。也可以通过配置来达到强制释放,就是在规定时间内如果不能释放就kill掉。同时你也可以配置一个队列的默认和最大资源使用量。默认使用量是集群可以保证的资源量。最大是当其他组空闲时,最大可以扩张到的资源使用量。

同时每个Group是支持子Group的。子Group只能分配自己父Group的资源。比如我们把集群资源分配到两个group,dev和test,它们各站50%的系统资源。在dev这个group里还有两个子的group,team1,team2。team1和team2分别占dev这个组资源的40%和60%,也就是对应整个集群的20%和30%,

通过这些配置,从而实现了一个弹性的队列。保证了不同组的job都可以得到预期的相应时间,也保证了集群资源的高效使用。
容量调度器是Yarn里默认的调度器,你也可以在yarn-site.xml里显式的指定或者检查是否有人修改过:


        <property>
                <name>yarn.resourcemanager.scheduler.class</name>
                <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
        </property>

接着我们看如何配置队列,我们需要修改hadoop安装目录下etc/hadoop/capacity-scheduler.xml文件


<property>
  <name>yarn.scheduler.capacity.root.queues</name>
  <value>dev,test</value>
  <description>The queues at the root level.
  </description>
</property>

<property>
  <name>yarn.scheduler.capacity.root.dev.queues</name>
  <value>team1,team2</value>
  <description>The queues at the root.dev level.
  </description>
</property>
<property>
    <name>yarn.scheduler.capacity.root.dev.capacity</name>
    <value>50</value>
</property>
<property>
    <name>yarn.scheduler.capacity.root.test.capacity</name>
    <value>50</value>
</property>
<property>
    <name>yarn.scheduler.capacity.root.test.maximum-capacity</name>
    <value>70</value>
</property>
<property>
    <name>yarn.scheduler.capacity.root.dev.team1.capacity</name>
    <value>40</value>
</property>
<property>
    <name>yarn.scheduler.capacity.root.dev.team2.capacity</name>
    <value>60</value>
</property>

默认是有一个root作为根队列,我们通过yarn.scheduler.capacity.root.queues属性来指定我们的队列dev和test。它们之间用逗号分隔。然后我们在root.dev队列下继续指定dev的子队列team1,team2。在指定队列属性的时候,我们是通过一个队列的全路径来指定的,比如team2的全路径是root.dev.team2。之前我们提到过,一个队列通过arn.scheduler.capacity..capacity只指定了默认容量的时候,如果其他所有队列空闲,是可以扩展到整个资源全部的资源的。但是如果指定了maximum-capacity,那么它只能分得它父队列最大资源乘以maximum-capacity的百分数。

如果你提交job时没有指定queue name,那么会被加入default queue。所以你可以在你的capacity-scheduler.xml留下对default queue的资源分配。

当你提交一个map-reduce job的时候,你需要在jvm 的SystemProperites里设置 mapred.job.queue.name 为你期望提交任务的队列。
比如这样:

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.2.0.jar pi -Dmapred.job.queue.name=root.example_queue 10 10000

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: