YARN调度器详解:容量调度器与公平调度器在多租户集群中的配置实践

发布时间:2026/10/20 5:25:29
YARN调度器详解:容量调度器与公平调度器在多租户集群中的配置实践
1. 整体设计与选型思路解构大数据领域的资源管理本质上是“多租户下的资源博弈”问题。一个集群里同时跑着定时报表、即席查询、实时流计算、模型训练等一系列任务如果没一个靠谱的调度器来统筹全局分分钟会出现资源饿死、队列堆积、任务互相踩踏的情况。我参与过不少集群的治理项目发现很多团队对YARN调度器的理解停留在“能跑就行”的层面直到线上出现“某个部门的任务把集群占满其他部门干瞪眼”的故障后才开始重视。YARN调度器解决的核心矛盾用一句话概括是在有限资源下让不同优先级、不同租户、不同时延要求的任务获得相对合理且可预期的资源分配。它做的事情不是加速单个任务而是决定“谁先用、谁后用、各用多少”。调度器本身不执行计算它只负责给ApplicationMaster分配Container后续的资源使用由任务自身与NodeManager协同完成。我在实际选型时通常先问自己三个问题团队规模多大、业务线之间是否有明确的资源隔离需求集群资源是否紧张、是否需要精细化的权重配置延迟敏感任务与批处理任务的比例如何这三点基本决定了在FIFO、容量调度器、公平调度器之间怎么选。我在多个项目中测试过三种调度器的表现它们的差异不在“谁能执行的更快”而在于“资源紧张时的分配倾向”。FIFO简单粗暴但容易饿死旁人容量调度器是“先到先得 队列上限”的组合适合多租户隔离公平调度器则追求长时间维度的资源均分适合多用户共享集群的场景。顺带一提现在很多新入行的同学都在问“是不是只用容量调度器就够了”。我的答案是容量调度器确实覆盖了绝大多数生产场景但公平调度器在多用户、多队列、任务长短混合的场景下往往能给出更平滑的资源保障体验。你在做技术选型的时候最好把业务场景摸透再动手别一上来就照搬网上模板。2. 三种调度器核心机制与关键参数解析2.1 FIFO调度器最简单的“先来后到”FIFO调度器First In First Out是Hadoop早期的默认调度器核心思想就是一个全局队列所有提交的Application按提交时间排成一个先进先出的队列。队头的应用拿到集群全部可用资源执行完释放后下一个应用才能开始获取资源。这种设计的优势相当明显实现极度简单、没有配置成本、任务执行的可预期性最强。在单用户、单业务线的场景下FIFO实际上是最优选择既不浪费调度开销也无需考虑资源争抢。但也有致命缺陷我举一个曾经遇到的真实场景某团队用FIFO跑一个大型MapReduce任务需要数小时才能完成期间另一个团队提交了一个只需要几分钟的Hive查询这个查询必须等大任务完全结束后才能启动。这个“长尾任务阻塞短任务”的问题在共享集群中几乎不可接受因此FIFO逐渐被后两者取代。FIFO的可调参数极少核心只有yarn.scheduler.fifo.maximum-applications限制同时可运行的Application数量防止提交过多任务打爆内存。yarn.scheduler.fifo.user-limit-factor控制单个用户可占用的最大资源倍数默认值1.0。由于FIFO没有队列概念也就没有ACL控制、没有优先级抢占它更适用于测试环境或单业务场景。现在几乎不会有新项目选用FIFO作为生产调度器但理解它的行为逻辑有助于理解后两种调度器的设计动机。2.2 容量调度器多租户隔离与资源上限并重容量调度器Capacity Scheduler是当前Apache Hadoop发行版默认使用的调度器也是我在生产环境中用得最多的调度器。它的设计目标是在多个队列之间配置资源容量百分比每个队列使用资源不能超过其容量上限除非其他队列闲置每个队列内部又支持弹性伸缩。容量调度器的核心数据结构是“队列树”。根队列下面可以挂多个子队列子队列下还可以继续挂孙队列层级关系形成一棵树。每个叶子队列是实际接收任务的队列每个队列可以配置容量、最大容量、用户权限、状态RUNNING或STOPPED等。举个例子我参与治理某公司的集群时设计了三层队列结构根队列root生产队列production容量60%跑核心报表与实时计算实验队列experiment容量25%跑算法调优与模型试验临时队列temp容量15%跑即席查询、数据探查每个子队列内部又按团队做了细分比如生产队列下面分实时组、离线组实验队列下面分推荐组、风控组、NLP组。这种分层的好处是不同团队虽然共用一个集群但资源配额清晰互不干扰同时如果某个队列空闲其他队列可以临时借用弹性等空闲队列重新提交任务后再归还通过抢占或下次调度时收敛。容量调度器的核心参数我建议重点关注以下这些capacity队列保证的资源百分比所有同级子队列之和应为100%。maximum-capacity队列资源使用上限防止某个队列在借用资源后“赖着不还”。user-limit-factor单个用户在队列内可获取的最大资源倍数默认1.0表示一个用户最多只能用队列容量的1倍。maximum-applications队列中最大同时运行的Application数量。yarn.scheduler.capacity.maximum-am-resource-percent集群中用于ApplicationMaster的资源比例默认0.1如果任务提交非常密集可以适当调大。我在实际配置中发现一个高频问题很多初学者只设置了capacity却没设maximum-capacity导致某个队列在消费其他队列资源时拖到集群整体排队。合理做法是给每个队列设置maximum-capacity比如生产队列设为80%实验队列设为40%这样可以避免单个队列失控。2.3 公平调度器资源平权与最小共享保证公平调度器Fair Scheduler的设计哲学和容量调度器有明显差异。容量调度器强调的是“每个队列有保证容量”公平调度器强调的则是“所有运行中的应用或队列在一段时间内能获得大致均等的资源份额”。这里的“公平”并非严格的绝对平均而是基于权重weight的加权均分。可以理解为默认情况下权重均为1那么两个应用平分资源如果一个应用权重为2那么它获得的资源是另一个的2倍。公平调度器还支持“最小共享”minimum share配置每个队列可以声明一个资源下限调度器会优先满足所有队列的最小共享再按权重分配剩余资源。公平调度器的层级模型是这样的队列Queue是资源分配的基本单元支持层级结构。每个队列有独立的weight、minResources、maxResources。每个队列内部的应用之间也按权重分配资源。系统支持“抢占”preemption机制当某些队列资源低于最小共享且持续一段时间时可以触发抢占杀掉其他队列中超额分配的任务容器。我用公平调度器做过一个实验集群场景是两类任务混合提交A类是长时运行的流式计算任务如Spark StreamingB类是短时高并发的即席查询。公平调度器在面对这种混合负载时表现出了很平滑的调度曲线短任务不会被长任务完全阻塞长任务也不会被短任务频繁打断。公平调度器的关键参数包括yarn.scheduler.fair.preemption总开关开启抢占机制。yarn.scheduler.fair.preemption-threshold队列资源低于该阈值默认0.8时允许抢占。yarn.scheduler.fair.preemption-interval抢占检测周期默认15秒。yarn.scheduler.fair.sizebasedweight是否基于任务大小动态调整权重。yarn.scheduler.fair.assignmultiple一次心跳是否分配多个Container提升调度吞吐。需要注意的是公平调度器的配置文件为fair-scheduler.xml和容量调度器的capacity-scheduler.xml结构不同。从Hadoop 3.x开始公平调度器也成为了可选的默认调度器之一但CDH等发行版默认仍然使用容量调度器。3. 实操过程与核心环节实现这一部分我会以“某跨平台系统”的资源治理项目为例完整演示如何配置容量调度器与公平调度器。整个过程基于Hadoop 3.x环境但绝大多数参数在Hadoop 2.x同样适用。3.1 容量调度器的配置与队列设计实操第一步确认当前调度器类型。在yarn-site.xml中设置property nameyarn.resourcemanager.scheduler.class/name valueorg.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler/value /property第二步编辑capacity-scheduler.xml。这是容量调度器最核心的配置文件我拿一个双队列的例子来说明configuration property nameyarn.scheduler.capacity.root.queues/name valuedefault,production/value /property property nameyarn.scheduler.capacity.root.default.capacity/name value30/value /property property nameyarn.scheduler.capacity.root.production.capacity/name value70/value /property property nameyarn.scheduler.capacity.root.production.maximum-capacity/name value80/value /property property nameyarn.scheduler.capacity.root.default.maximum-capacity/name value50/value /property /configuration这里的含义是根队列下有两个子队列default队列容量30%上限50%production队列容量70%上限80%。当production队列资源紧张时它可以借用default队列的空闲资源但最多不能超过80%default队列也同理。第三步配置队列访问控制。在capacity-scheduler.xml中添加property nameyarn.scheduler.capacity.root.production.acl_submit_applications/name valueadmin_group,data_team/value /property property nameyarn.scheduler.capacity.root.production.acl_administer_queue/name valueadmin_group/value /propertyacl_submit_applications控制哪些用户或用户组可以往该队列提交任务acl_administer_queue控制哪些用户可以管理该队列比如停止队列、更新配置。生产环境建议严格配置这两个参数避免有人误提交任务到生产队列导致资源被挤占。第四步通过命令行测试队列配置是否生效yarn queue -status production如果配置正确可以看到队列的容量、最大容量、当前运行的应用数等信息。也可以使用yarn rmadmin -refreshQueues在不重启ResourceManager的情况下刷新队列配置。注意修改capacity-scheduler.xml后无需重启集群只需执行yarn rmadmin -refreshQueues即可动态生效。3.2 提交任务到指定队列的方法代码中提交任务到指定队列有几种方式。以Spark为例可以在提交命令中指定spark-submit \ --master yarn \ --deploy-mode cluster \ --queue production \ --num-executors 20 \ --executor-cores 4 \ --executor-memory 8g \ your_job.jar也可以在代码中设置spark.yarn.queue参数spark SparkSession.builder \ .appName(demo) \ .config(spark.yarn.queue, production) \ .getOrCreate()MapReduce作业则通过mapreduce.job.queuename参数指定hadoop jar your_job.jar \ -Dmapreduce.job.queuenameproduction \ your_input your_output我在某个模拟项目X中遇到过一个问题业务方明明指定了队列但任务始终提交到default队列。排查后发现是代码中硬编码了队列名导致命令行参数不生效。这种情况非常常见建议在代码规范层面就统一队列名配置。3.3 公平调度器的配置与多租户策略在yarn-site.xml中将调度器切换为公平调度器property nameyarn.resourcemanager.scheduler.class/name valueorg.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler/value /property然后创建fair-scheduler.xml放在etc/hadoop/目录下。一个典型的配置如下allocations defaultQueueSchedulingPolicydrf/defaultQueueSchedulingPolicy queue nameroot weight1.0/weight minResources10000 mb, 4 vcores/minResources /queue queue nameroot.etl weight3.0/weight minResources20000 mb, 8 vcores/minResources /queue queue nameroot.adhoc weight1.0/weight /queue user nameadmin maxRunningApps50/maxRunningApps /user userMaxAppsDefault20/userMaxAppsDefault /allocations这个配置表示root队列下有etl和adhoc两个子队列etl权重为3相当于最多可获取3/4资源adhoc权重为1。defaultQueueSchedulingPolicy设置为drf主导资源公平意味着在多资源维度内存/CPU下按主导资源份额进行公平分配。公平调度器配置完成后同样可以动态加载yarn rmadmin -refreshQueues需要注意的是公平调度器的队列是“懒创建”的。如果某个用户向一个未定义的队列提交任务调度器会自动创建该队列取决于yarn.scheduler.fair.allow-undeclared-pools参数。在生产环境中我建议将该参数设为false避免用户随意创建队列导致管理混乱property nameyarn.scheduler.fair.allow-undeclared-pools/name valuefalse/value /property3.4 多租户场景下的容量规划与资源估算配置调度器之前资源规划是必不可少的一步。我在某公司做过一次集群资源体检发现很多队列配置完全凭感觉没有做容量测算。下面分享一个我常用的计算框架。假设集群总资源为512GB内存、128核。业务线有三类实时计算需要稳定资源、离线数仓白天高峰、夜间低峰、即席查询时间不定、波动大。第一步估算每类业务的基础资源需求。实时计算通常要预留40%的资源因为流式任务不能等资源必须常驻离线数仓在高峰期可能需要50%的资源但夜间可以降到10%即席查询按高峰并发估算比如同时跑20个查询每个查询2GB/2核那就是40GB/40核。第二步把以上需求折算为容量百分比。假设实时计算预留200GB/50核离线数仓预留200GB/50核即席查询预留112GB/28核。注意总容量需要留一部分缓冲不要直接把100%分完。我最终设计的容量配置如下实时队列realtimecapacity40maximum-capacity60离线队列offlinecapacity40maximum-capacity60即席队列adhoccapacity15maximum-capacity30默认队列defaultcapacity5maximum-capacity20这样配置的原因有两点每个队列都有maximum-capacity上限防止某个队列因为资源抢占过于激进影响其他队列的SLA。留出5%的缓冲资源给default队列避免未分类任务无处可去。在实际运营中这套配置运行了三个月集群的队列资源利用率保持在70%左右没有再出现“队列饿死其他队列”的问题。4. 常见问题与故障排查速查手册调度器相关的线上问题我见过不少这里把高频问题总结成一张速查表现象可能原因排查思路与解决方式任务一直处于ACCEPTED状态队列中没有可用资源查看队列状态yarn queue -status检查maximum-capacity是否设置过小某个用户提交任务过多挤占他人资源user-limit-factor设置过大调低该参数例如设为1.0或2.0队列之间资源抢占不生效未开启抢占或占抢阈值不合理容量调度器需配置yarn.resourcemanager.scheduler.monitor.enable为true并配置抢占策略任务被反复杀掉重启NodeManager内存紧张或Container过大查看NodeManager日志确认yarn.nodemanager.resource.memory-mb与实际机器内存是否匹配调度器配置刷新后不生效配置文件路径错误或者语法错误检查XML格式确认ResourceManager日志中是否有配置解析异常短任务等长任务时延恶化错误使用了FIFO调度器切换为容量调度器或公平调度器配置多队列任务提交到了错误的队列代码中硬编码队列名统一使用命令行参数避免代码中写死队列名除了上面的速查表我再分享三个我踩过的比较隐蔽的坑。第一个坑是资源计算的“虚拟核”与实际CPU绑定问题。YARN默认只按内存调度ContainerCPU核数只是逻辑约束。如果机器是超线程架构虚拟核数可能大于物理核数会导致多个Container在同一物理核上争抢。解决办法是开启yarn.nodemanager.resource.cpu-vcores的精确配置并设置为物理核数。第二个坑是Spark任务的动态资源分配和调度器的配合。Spark开启动态资源分配spark.dynamicAllocation.enabledtrue后Executor会根据执行阶段动态申请和释放Container。如果调度器配置了严格的maximum-capacitySpark可能会频繁申请资源失败表现为任务运行极慢或者报资源不足。解决办法是给Spark任务所在队列预留足够弹性空间或者关闭动态资源分配。第三个坑是Container大小与任务并行度的匹配。调度器只负责分配Container但如果提交任务时设置的Container内存超过了队列的maximum-capacity任务一样无法启动。我遇到过某个任务申请8GB内存但生产队列剩余可用只有6GB的情况。建议在提交任务前先通过yarn application -list查看队列资源余量或者使用CapacityScheduler的yarn.scheduler.capacity.root.production.pending-timeout-seconds参数设置排队超时。5. 资源调度器选型建议与个人经验选型这件事很多文章喜欢列一堆对比表格但真正落地的时候你会发现业务场景远比理论复杂。我的建议是按以下思路来做决策如果你的集群是单团队专用任务类型单一、提交频率不高直接用FIFO即可省心省力。如果集群多团队共享、有明确的资源隔离需求首选容量调度器配置简单、社区文档丰富、排查问题方便。如果集群任务大小差异悬殊、时延要求不同、用户数多公平调度器更能体现资源平权和动态调整的优势。另外提一点无论选择哪种调度器都要定期检查队列配置和任务运行情况。调度器配置不是一次到位就完事的业务负载会变化资源需求也会漂移。我习惯每季度做一次资源使用回看看看各队列的峰值利用率和排队率再针对性地调整容量和权重参数。还有一个容易被忽略的点调度器只是资源分配层面的事情它不能解决所有性能问题。比如某个Spark任务本身存在数据倾斜调度器给了再多资源也无法加速完成。所以我的建议是先保证调度器配置合理再深入任务级别的调优。前者是基础后者是进阶两者顺序反了会浪费大量排查时间。我个人在实际操作中还有一个小技巧每次调整完调度器配置我会用yarn application -list抓取所有运行中的应用结合Ganglia或Prometheus面板观察集群资源曲线持续至少24小时确认没有异常波动后再固化配置。这个习惯帮我多次避免了“调完配置后第二天线上出问题”的尴尬。调度器这块内容静态看是几个配置参数的事动态跑起来才见真章。希望这篇梳理能帮你在配置多租户资源时少踩几个坑。