程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

YARN 资源调度器 CapacityScheduler 原理

balukai 2025-05-10 19:56:30 文章精选 5 ℃

CapacityScheduler 使用了树的结构来管理资源的使用,树的节点类型为 Queue,Queue 你可以理解为就是一个可伸缩的资源池,任务提交申请资源和任务销毁时释放资源都由 Queue 统一管理。

1. 队列类型:

  • ParentQueue:非叶子结点包含子节点,管理子队列的状态信息,子队列之间资源的共享等;
  • LeafQueue:叶子结点,任务只能提交到叶子节点,校验当前队列状态,运行的任务是否达到最大,用户提交任务数量的校验。

2. 通用队列属性,适用于所有队列

  • yarn.scheduler.capacity.maximum-applications:处于 running 和 pending 状态任务的最大数量,默认 10000;
  • yarn.scheduler.capacity.max-parallel-apps:任务运行的并发度,默认 int 最大值;
  • yarn.scheduler.capacity.resource-calculator:可以限定的资源类型,默认值 org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator 只能用来限定内存,无法限定 CPU,如需同时限定 CPU 和内存需使用 org.apache.hadoop.yarn.util.resource.DominantResourceCalculator;
  • yarn.scheduler.maximum-allocation-vcores:每个 container 可申请的最大 vcore,默认 4;
  • yarn.scheduler.maximum-allocation-mb:每个 container 可申请的最大内存,默认 8192。

3. 队列私有属性

  • yarn.scheduler.capacity.<queue-path>.state:队列状态,RUNNING = 上线的队列,可用来分配任务资源,STOPPED = 下线状态的队列,之前可能上过线现在下线了;
  • yarn.scheduler.capacity.<queue-path>.maximum-applications:必须小于等于对应的通用队列属性;
  • yarn.scheduler.capacity.<queue-path>.max-parallel-apps:必须小于等于对应的通用队列属性;
  • yarn.scheduler.capacity.<queue-path>.capacity:当前队列容量在父队列中的容量占比,这里比较特殊的是 root 根节点,容量始终等于 100 表示 Yarn 可分配的总资源,对某个父队列其所有子队列占比之和为 100,例如 root 子队列事业部1占 20%、事业部2占 50% 和事业部3占 20%。
  • yarn.scheduler.capacity.<queue-path>.maximum-capacity:当前队列的容量超过默认容量时,还可以用多少其他队列的容量,如果不配置默认100即可使用父队列容量的全部;

3.1 限定 Container

  • yarn.scheduler.capacity.<queue-path>.maximum-allocation-vcores:当前队列中每个 container 可申请的最 大 vcore,这个值必须 <= yarn.scheduler.maximum-allocation-vcores;
  • yarn.scheduler.capacity.<queue-path>.maximum-allocation-mb:当前队列中每个 container 可申请的最大内存,这个值必须 <= yarn.scheduler.maximum-allocation-mb。

3.2 限定用户资源

  • yarn.scheduler.capacity.<queue-path>.minimum-user-limit-percent:限制用户使用的最小资源占比;
  • yarn.scheduler.capacity.<queue-path>.user-limit-factor:用户可扩容的倍数,例如原有占比 10%,如果 该值为 2 那么用户可用的最大资源为 20%。

3.3 限定用户权限

  • yarn.scheduler.capacity.root.<queue-path>.acl_submit_applications:可提交任务的用户或用户组;
  • yarn.scheduler.capacity.root.<queue-path>.acl_administer_queue:队列的管理权限。

ACL 控制语法:用户之间逗号分隔 + 空格 + 用户组之间逗号分隔。

3.4 用户、用户组和队列的映射

  • yarn.scheduler.capacity.queue-mappings:用户提交任务的时候,可以根据用户名称和用户所在用户组限定用户提交的队列,支持多个规则,规则之间以逗号分隔,排在前面的规则优先执行;
    • 用户和队列的匹配:u:username:queue;
    • 用户组和队列的匹配:g:userGroup:queue;

例如用户 user1 和 user2,user1 所在的用户组 userGroup1,和队列的映射规则:

  • user1 -> q1,匹配优先级高
  • user2 -> q2
  • userGroup1 -> q3

那么配置如下:

<property>
  <name>yarn.scheduler.capacity.queue-mappings</name>
  <value>u:user1:q1,u:user2:q2,g:userGroup1:q3</value>
</property>

4. 环境部署

配置 CapacityScheduler 队列,要求如下:

  1. 事业部1队列 q1 资源:20% ~ 30%;
    1. 子队列 user:40% ~ 50%;
    2. 子队列 spark:60% ~ 80%;
  2. 事业部2队列 q2 资源:50% ~ 80%;
    1. 子队列 query:30% ~ 50%;
    2. 子队列 export:50% ~ 80%;
    3. 子队列 merge:20% ~ 30%;
  3. 事业部3队列 q3 资源:30% ~ 50%;
    1. 子队列 import:20% ~ 30%;
    2. 子队列 ai:80% ~ 100%。

4.1 环境搭建

wget https://downloads.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz
tar -xvf hadoop-3.3.6.tar.gz
cd /home/ice/hadoop-3.3.6/etc/hadoop
 
# 1. 配置 core-site.xml
cat << EoF > core-site.xml
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>
EoF
 
# 2. 配置 hdfs-site.xml
cat << EoF > hdfs-site.xml
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>
EoF
 
# 3. 配置 mapred-site.xml
cat << EoF > hdfs-site.xml
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>mapreduce.application.classpath</name>
      <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
    </property>
</configuration>
EoF
 
# 4. 配置 yarn-site.xml,这里需要用到 CapacityScheduler,同时限制下 Yarn 整体资源为 10GB,主要是好测试
cat << EoF > yarn-site.xml
<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>10240</value>
    </property>
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
    </property>
    <property>
        <name>yarn.resourcemanager.scheduler.class</name>
        <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
    </property>
</configuration>
EoF

echo "export JAVA_HOME=/home/ice/jdk1.8.0_191" >> hadoop-env.sh
echo 'export HDFS_NAMENODE_OPTS="-XX:+UseParallelGC -Xmx2g"' >> hadoop-env.sh
echo 'export HDFS_DATANODE_OPTS="-XX:+UseParallelGC -Xmx4g"' >> hadoop-env.sh
 
# 这里为了可以远程 debug
echo 'export YARN_RESOURCEMANAGER_OPTS="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999"' >> yarn-env.sh
 
# 生成密钥一路回车
ssh-keygen
# 设置免密登录
ssh-copy-id localhost
 
# 加入到环境变量里面
vim ~/.bash_profile
export HADOOP_HOME=/home/ice/hadoop-3.3.6
export PATH=$HADOOP_HOME/bin:$PATH:$HOME/.local/bin:$HOME/bin
 
source ~/.bash_profile

4.3 配置队列:capacity-scheduler.xml

<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->
<configuration>
    <property>
        <name>yarn.scheduler.capacity.root.queues</name>
        <value>q1,q2,q3</value>
    </property>
    <!-- q1 及其子队列-->
    <property>
        <name>yarn.scheduler.capacity.root.q1.queues</name>
        <value>user,spark</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.capacity</name>
        <value>20</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.maximum-capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.user.capacity</name>
        <value>40</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.user.maximum-capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.user.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.spark.capacity</name>
        <value>60</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.spark.maximum-capacity</name>
        <value>80</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.spark.state</name>
        <value>RUNNING</value>
    </property>

    <!--q2 及其子队列-->
    <property>
        <name>yarn.scheduler.capacity.root.q2.queues</name>
        <value>query,export,merge</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.maximum-capacity</name>
        <value>80</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.query.capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.query.maximum-capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.query.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.export.capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.export.maximum-capacity</name>
        <value>80</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.export.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.merge.capacity</name>
        <value>20</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.merge.maximum-capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.merge.state</name>
        <value>RUNNING</value>
    </property>

    <!--q3 及其子队列-->
    <property>
        <name>yarn.scheduler.capacity.root.q3.queues</name>
        <value>import,ai</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.maximum-capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.import.capacity</name>
        <value>20</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.import.maximum-capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.import.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.ai.capacity</name>
        <value>80</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.ai.maximum-capacity</name>
        <value>100</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.ai.state</name>
        <value>RUNNING</value>
    </property>
</configuration>

4.4 启动

bin/hdfs namenode -format
sbin/start-dfs.sh
sbin/start-yarn.sh

查看具体配置:hadoop queue -list

5. 场景测试

编写用于测试的脚本:

# 1. 先创建 HDFS 目录
hdfs dfs -mkdir /input
 
# 2. 放点测试文件
hdfs dfs -put etc/hadoop/*.xml /input
 
# 3. 运行个原有的样例
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar grep /input /output 'dfs[a-z.]+'
 
# 4. 编写脚本 start_test.sh
# 可以设定提交用户和提交队列
queue=${1:-root.q2}
export HADOOP_USER_NAME=${2:-ice}

bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi \
-Dyarn.app.mapreduce.am.resource.mb=2048 \
-Dmapreduce.job.queuename=$queue \
100 100

将测试的队列变得简单一些,复现问题也会容易些:

<!--
       Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->
<configuration>
    <property>
        <name>yarn.scheduler.capacity.user.max-parallel-apps</name>
        <value>100</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.queues</name>
        <value>q1,q2,q3</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.capacity</name>
        <value>20</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q1.user-limit-factor</name>
        <value>5</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.capacity</name>
        <value>50</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.user-limit-factor</name>
        <value>2</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q2.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.capacity</name>
        <value>30</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.state</name>
        <value>RUNNING</value>
    </property>
    <property>
        <name>yarn.scheduler.capacity.root.q3.user-limit-factor</name>
        <value>4</value>
    </property>
</configuration>

通过设置 user-limit-factor,用户提交的任务可使用集群所有资源:

  • root.q1:0.2 * 5 = 100%;
  • root.q2:0.5 * 2 = 100%;
  • root.q3:0.3 * 4 = 120% 只能使用 100%。

5.1 单队列单任务

直接启动任务即可:./start_test.sh,默认提交到 root.q1 队列

5.2 单独列多任务

改为后台任务多次执行:

for i in `seq 1 3`; do sh start_test.sh; done;


执行过程中发现同一时刻只能有一个任务执行,主要原因是最开始提交的任务
application_1694433897361_0002
占用了所有资源,导致后续的
application_1694433897361_0003

application_1694433897361_0004
只能阻塞。

通过上面的测试可以发现,设定用户使用容量的上线很有必要。

5.3 多队列多任务同时执行

for queue in root.q1 root.q2;do sh start_test.sh $queue;done

运行中发现,不同队列的任务没有发生阻塞:

两个任务执行的状态:

5.4 多队列多任务先后执行

我们让 root.q1 队列任务先执行一会,等其占用到整体资源 100% 时,这时候再提交 root.q2 队列任务:

测试过程中发现,root.q2 等待一段时间后获取到资源,并没有强制的对 root.q1 队列任务 kill,CapacityScheduler 其实不同队列发生资源竞争时,会减少正在运行任务的资源需要等到再次申请资源时,还是很友好的:

所有设置队列的最大上限也是很有必要的,起码不会影响其他队列的执行。

最近发表
标签列表