一、先说说咱们平时遇到的“抢资源”打架现场

大家在生产上跑数据任务,是不是经常碰到这种情况:凌晨跑报表的时候,突然有同事提交一个超级大的MapReduce作业,把整个队列的资源全吃光了;接着你的核心业务作业就只能排队等着,眼巴巴看着考核时间一点一点逼近。有时候更气人,一些临时的测试作业混在里面,调度器一视同仁,结果重要作业被劣后了。

这种情况在YARN集群里特别常见。YARN负责给各个作业分配CPU和内存,但它的默认规则不一定对你有利。咱们要做的,就是通过设置合适的调度优先级,让“老板关心的活儿”先跑,让“随便玩玩的活儿”靠边站。

今天这篇文章就用大白话把YARN里的优先级怎么配置讲清楚。咱们会聊调度器的基本概念、常见配置技巧,再用手把手敲过的命令和配置,把核心作业给“插队”到前面去。

二、YARN里管分资源的“大管家”:调度器

在YARN里,真正决定谁先拿资源、谁后拿资源的,是“调度器”。你可以把它想象成食堂打饭的师傅:每个队列就是一个窗口,每个作业就是排队的人,资源就是碗里的菜。师傅按什么规则打菜,全看调度器的策略。

2.1 三种常见调度器

YARN自带三种调度器,分别是FIFO、容量调度器(Capacity Scheduler)和公平调度器(Fair Scheduler)。

  • FIFO(先来先服务):谁先来谁就先吃,简单粗暴。但前面一个大作业排队,后面所有小作业都得饿肚子。
  • 容量调度器:把食堂分几个固定窗口,每个窗口保证起码多少菜。一个窗口用不完,可以借给别的窗口。生产环境用得很多。
  • 公平调度器:更讲究“平均主义”,大家的作业轮流拿资源,但是咱们可以通过配置权重,给核心业务“加餐”。

我们要调优优先级,主要是在容量调度器和公平调度器上动手脚。

2.2 队列和资源池是怎么回事

不管哪种调度器,都离不开“队列”这个概念。集群管理员会划分出多个队列,比如“核心业务队列”“报表队列”“测试队列”。每个队列有单独的容量上限,互不干扰。

但队列之间如何共享资源、队列里的作业如何排队,就涉及到权重和优先级了。下面咱们就进入正题:优先级到底怎么设置。

三、优先级设置在哪个环节用力?

很多刚接触YARN的同学,会把“优先级”想得特别复杂。其实咱们能动手的地方主要有三个:作业提交时指定优先级、运行中动态调优先级、以及调度器层面给队列调权重。

3.1 作业级优先级(应用优先级)

最简单直接的方式,就是在提交作业的时候,告诉YARN“我这个作业非常重要,请优先给我资源”。YARN中的作业都有一个叫“优先级”的属性,默认是NORMAL。

MapReduce作业提交时,可以通过参数 -Dmapreduce.job.priority=HIGH 来指定。优先级可以填VERY_HIGH、HIGH、NORMAL、LOW、VERY_LOW,也可以用数字表示,数字越大越优先。

这里有个容易忽略的点:优先级只影响同一个队列里的“插队顺序”,不保证一定能抢到别的队列的资源。如果队列本身已经排满了高优先级作业,你再怎么提优先级也得等着。

3.2 队列权重(公平调度器里的“偏心”)

如果你们用的是公平调度器,那么队列级权重就是“大杀器”。通过给核心业务队列设置更高的权重,比如权重是2,普通队列权重是1,那么在资源紧张的时候,核心队列拿到的资源就是普通队列的两倍。

这个权重的设置,不用改代码,只要修改公平调度器的配置文件就好。配置文件叫什么、怎么写,咱们在后面的实战示例里详细展示。

3.3 动态调整运行中的作业优先级

有些时候作业都已经跑起来了,你才意识到“坏了,这个作业得提前出结果”。不用慌,YARN支持直接修改运行中作业的优先级。

用的命令是 yarn application -updatePriority <applicationId> <priority>。这招特别适合临时救急,比如老板下午三点要数据,你发现上午启动的作业还在排队,就可以直接把它提升到HIGH,让它插队。

当然,动态调整也有前提:调度器得支持。容量调度器和公平调度器都支持作业优先级,所以照做就行。

四、实战示例:用Shell把核心作业“插队”到最前面

好,光说不练假把式。下面所有操作命令,咱们统一使用Shell技术栈。在正式开始之前,先明确一下环境:假设咱们已经安装好Hadoop和YARN,并且能正常执行 yarn 命令。

4.1 提交作业时指定优先级

技术栈:Shell(YARN命令行)

# 技术栈:Shell(YARN命令行)

# 这是一个普通的MapReduce作业,使用wordcount示例
# 注意:核心业务作业请使用HIGH或VERY_HIGH优先级
yarn jar /opt/hadoop-examples.jar wordcount \
  -Dmapreduce.job.priority=HIGH \
  /input/core_biz/ /output/core_biz_result/

上面的-Dmapreduce.job.priority=HIGH就是指定优先级。如果不想用英文单词,也可以直接写数字:

# 技术栈:Shell(YARN命令行)

# 用数字5代表非常高,数字越大优先级越高
# 注意:MapReduce的优先级映射范围一般是0-4,5可能无效
# 所以咱们这里只用常规的HIGH或VERY_HIGH
yarn jar /opt/hadoop-examples.jar wordcount \
  -Dmapreduce.job.priority=VERY_HIGH \
  /input/urgent_report/ /output/urgent_result/

有一点需要提醒,不同版本的Hadoop对数字范围定义不太一样,为了避免出错,建议直接用单词来设置。

4.2 动态修改运行中作业的优先级

如果作业已经提交,咱们可以先查一下它的状态,然后再改优先级。下面是完整的步骤:

# 技术栈:Shell(YARN命令行)

# 第一步:查看当前正在运行的作业列表
# 从结果里找到你想改优先级的那个ApplicationId
yarn application -list -appStates RUNNING

# 第二步:假设找到的ApplicationId是application_1699999999999_0001
# 查看这个作业的详细信息,里面会显示Priority当前值
yarn application -status application_1699999999999_0001

# 第三步:把它的优先级改成HIGH
yarn application -updatePriority application_1699999999999_0001 HIGH

改完之后,再用yarn application -status看一次,确认Priority已经变成HIGH。

如果作业还在队列里排队,还没开始跑,也可以用同样的命令。调度器会重新排队,把高优先级的作业排到前面。

4.3 公平调度器的队列权重配置

上面说的作业级优先级,管的是队列内部的“排队顺序”。但如果集群有多个队列,不同队列之间怎么分配资源,就要靠队列权重了。

假设咱们有两个队列:core(核心业务)和dev(开发测试)。我们希望核心业务拿到两倍于开发测试的资源。这时候就需要修改公平调度器的配置文件。

技术栈:Shell(用于创建和修改配置文件)

# 技术栈:Shell(YARN公平调度器配置)

# 假设fair-scheduler.xml文件位于 /etc/hadoop/conf/fair-scheduler.xml
# 下面这条命令会覆盖该文件(注意备份原文件)
cat > /etc/hadoop/conf/fair-scheduler.xml <<'EOF'
<?xml version="1.0"?>
<allocations>
  <!-- 默认调度行为,不配置的话还会有一个default队列 -->
  <queue name="core">
    <!-- 权重设为2,核心业务拿双份资源 -->
    <weight>2</weight>
    <!-- minimum: 最少保证20%资源 -->
    <minResources>2048 MB, 2 vcores</minResources>
  </queue>

  <queue name="dev">
    <!-- 权重设为1,普通队列 -->
    <weight>1</weight>
    <!-- minimum: 最少保证10%资源 -->
    <minResources>1024 MB, 1 vcores</minResources>
  </queue>
</allocations>
EOF

注意,配置文件里的weight就是资源优先级的核心。权重越高,在资源分配时占的比例就越大。配置完之后,需要重启YARN的ResourceManager才生效。有些版本支持热加载,但为了图省事,重启最稳妥。

# 技术栈:Shell(YARN服务控制)

# 重启ResourceManager(不同发行版命令有差异,这是通用写法)
# 先停服务
yarn-daemon.sh stop resourcemanager

# 再启动服务
yarn-daemon.sh start resourcemanager

4.4 如何在提交任务时绑定到指定队列

光有优先级还不够,如果核心业务作业跑错了队列,权重就没用了。所以提交作业时,还要指定落到哪个队列里。

# 技术栈:Shell(YARN命令行)

# 将作业提交到core队列,并设置优先级HIGH
# 注意:队列必须在调度器配置里存在,否则会报错
yarn jar /opt/hadoop-examples.jar wordcount \
  -Dmapreduce.job.queue.name=core \
  -Dmapreduce.job.priority=HIGH \
  /input/online_order/ /output/online_order_result/

这样,咱们既固定了优先进入core队列,又保证了在队列内部也是“高优先级插队”的状态。

4.5 结合示例:把“查订单量”作业调成最高优先级

下面来一个完整的综合示例。假设咱们每隔十分钟要统计一次实时订单量,这个作业不能等,必须一提交就得资源。

# 技术栈:Shell(YARN命令行)

# 定义变量,方便复用
QUEUE_NAME="core"
PRIORITY="VERY_HIGH"
INPUT_PATH="/data/orders/current"
OUTPUT_PATH="/data/orders/stat/$(date +%Y%m%d%H%M)"

# 执行统计作业
yarn jar /opt/bigdata-jobs.jar OrderStat \
  -Dmapreduce.job.queue.name=${QUEUE_NAME} \
  -Dmapreduce.job.priority=${PRIORITY} \
  ${INPUT_PATH} ${OUTPUT_PATH}

# 如果发现作业运行了10分钟还没结束,而且有更紧急的报表进来,
# 可以直接再提高它的优先级(但VERY_HIGH已经是最高了)
# 下面是另一个作业从NORMAL提到HIGH的例子
yarn application -updatePriority application_1699999999999_0002 HIGH

上面的例子简简单单,但已经足够覆盖日常使用。核心思路就两句话:一让作业进对队列,二给作业设高优先级。

五、这些设置都用在什么场景?优缺点是什么?

咱们讲了这么多配置技巧,那它到底适合用在哪?有什么好处,又有什么坑?下面仔细捋一捋。

5.1 适用场景

  • 关键时刻保障:比如电商大促、每日结算、生成财务月报,这些作业需要准点完成,不能被临时任务干扰。给它们设置HIGH或VERY_HIGH,能明显减少排队等待。
  • 多部门混合使用集群:一个集群里业务线多,有实时计算的、有离线清洗的、有算法调参的。用队列权重把核心业务队列抬高,防止算法同学跑实验把生产资源挤爆。
  • 突发性紧急任务:临时要出一个数据报告,希望马上出结果,动态修改优先级是最快的办法。

5.2 好的一面

  • 配置简单,一条命令或一段XML就能搞定,不需要改代码。
  • 见效快,提交作业时参数一加,作业排队顺序立刻变化。
  • 灵活,运行中的作业也能动态调整,非常方便救火。

5.3 不太好的一面

  • 过度使用会让“所有作业都觉得自己重要”,人人都设VERY_HIGH,优先级又拉不开差距,等于没设。
  • 队列权重设置后,低优先级队列可能长期“饿饭”。比如核心队列权重10,开发队列权重1,开发作业可能老拿不到资源。
  • 动态修改优先级只能改作业排队顺序,如果作业已经在跑,改优先级不能抢占正在运行的其他作业。除非你开启了抢占功能,否则还得等别人跑完。

六、注意事项再叮嘱几句

  • 先确认调度器类型:如果你用的是FIFO调度器,上面的权重配置完全不生效。在yarn-site.xml里检查yarn.resourcemanager.scheduler.class,如果是CapacityScheduler或FairScheduler,再做相应操作。
  • 队列名称不要写错:提交作业时指定的队列名,必须已经存在于调度器配置中。否则作业会直接报错,连队列都进不去。
  • 不要盲目调高所有作业优先级:优先级是用来“区分重要程度的”,不是用来“讨好领导的”。如果所有作业都高优先级,调度器还是只能按提交时间排,你的核心作业依然不能保证快。
  • 注意优先级与抢占的配合:在某些极端场景下,即使你的作业优先级很高,但队列中的资源全被低优先级作业占着,你得开启抢占功能(yarn.resourcemanager.scheduler.monitor.enable=true)并配置抢占策略,才能强行把资源抢回来。这个功能需要谨慎使用,因为抢占会造成已运行作业失败。
  • 修改公平调度器配置后要重启或刷新:虽然某些支持热加载,但生产环境建议在低峰期重启或使用yarn resourcemanager的刷新命令,避免影响到正在运行的作业。

七、总结

YARN作业调度优先级的核心,说白了就是“分资源是先来后到,还是按亲疏远近”。咱们通过设置作业级优先级、队列权重和动态调整,可以让核心业务作业在资源竞争中获得明显优势。

从操作上看,提交作业时加一行-Dmapreduce.job.priority=HIGH,或者修改公平调度器的weight,就能达到效果。从策略上看,咱们得想清楚哪些作业真的重要,把“好钢用在刀刃上”。

别忘了,调度器永远试图给每个作业分到资源,但资源总是有限的。优先级设置是一把双刃剑,用好了,核心业务稳如泰山;用滥了,资源分配乌烟瘴气。建议大家在测试环境多试试不同的配置,观察作业的排队时间和资源使用情况,找到最适合你们集群的那套“偏心”方案。

希望这篇文章对你有帮助,下次集群里再有人抢资源,你就能从容不迫地掏出yarn application -updatePriority,把重要作业稳稳地托到前面来。