一、 YARN的运行机制 [面试重点]
- 1. 剖析YARN运行机制
- resource manager : 管理集群资源使用的资源管理器。
- node manager:运行在所有的节点上 且能够启动和监控容器(container)的节点管理器。
- container :用于执行特定应用程序的进程,每个容器都有资源限制(内存、CPU等)。
- 流程图:
- 1 首先,客户端联系resource manager,要求它运行application master进程(步骤1)
- 2 然后,resource manager 找到一个能在 container启动 application master 的节点管理器node namager (注:即在合适nodemanger上面 启动container 然后container中启动application master ) (步骤2a 和 2b)
- 3 application master 一旦运行起来后能做什么,依赖于应用本身。 可能是简单的计算,然后将结果返回给客户端;或者是向resource manager申请更多的容器(步骤3)。
- 4 申请更多的容器后,用于运行一个分布式计算(步骤4a,4b),即是MapReudce Yarn
二、 MapReudce Yarn的运行机制[面试重点-重中之重]
-
1. MapReudce Yarn
-
1. 流程概述
- 在最高层,有以下5个独立的实体(步骤)。
- 1.客户端:client,提交MapReduce作业。
- 2.Yarn资源管理器:Yarn resource manage,负责协调集群上的计算资源的分配。
- 3.Yarn节点管理器:Yarn node Manager,负责启动和监控集群的计算容器(container)
- 4.MapReduce的 application master:负责协调运行MapReduce作业的任务。和 Mapreduce任务运行在容器container中。(container 由 resouce manager 分配,由node manager管理)
- 5.分布式文件系统:一般是HDFS
- 在最高层,有以下5个独立的实体(步骤)。
-
2.作业的提交
- 作业的提交可以使用job对象的submit(),或者调用 waitForCompletion()方法 (底层调用submit()) ,源码如下。 job的submit()方法会创建一个内部的JobSummiter实例,并且调用其submitJobInternal()方法(步骤1)。
- 作业提交后,waitForCompletion()每秒轮询作业的进度,将进度报告打印到控制台
- JobSummiter所实现的作业提交过程如下:
- 向resource manager申请一个新应用id(application id),用于MR作业id(步骤2)
- 检查作业的输出说明。例如,如果没有指定输入目录或者输出目录已经存在,作业就不会再提提交。
- 计算作业的输入分片。如果分片无法计算,比如因为输入路径不存在,作业就不提交。
- 将运行作业所需要的资源(作业jar包文件、配置文件、计算所得的分片信息),复制到一个以作业id命名的目录下的 共享文件系统(一般是HDFS)(步骤3)
- 调用resouce manager的 submitApplication()方法提交作业。(步骤4)
/** * Submit the job to the cluster and return immediately. * @throws IOException */ public void submit() throws IOException, InterruptedException, ClassNotFoundException { ensureState(JobState.DEFINE); setUseNewAPI(); connect(); final JobSubmitter submitter = getJobSubmitter(cluster.getFileSystem(), cluster.getClient()); status = ugi.doAs(new PrivilegedExceptionAction<JobStatus>() { public JobStatus run() throws IOException, InterruptedException, ClassNotFoundException { return submitter.submitJobInternal(Job.this, cluster); } }); state = JobState.RUNNING; LOG.info("The url to track the job: " + getTrackingURL()); } /** * Submit the job to the cluster and wait for it to finish. * @param verbose print the progress to the user * @return true if the job succeeded * @throws IOException thrown if the communication with the * <code>JobTracker</code> is lost */ public boolean waitForCompletion(boolean verbose ) throws IOException, InterruptedException, ClassNotFoundException { //底层调用的submit() if (state == JobState.DEFINE) { submit(); } if (verbose) { monitorAndPrintJob(); } else { // get the completion poll interval from the client. int completionPollIntervalMillis = Job.getCompletionPollInterval(cluster.getConf()); while (!isComplete()) { try { Thread.sleep(completionPollIntervalMillis); } catch (InterruptedException ie) { } } } return isSuccessful(); }
-
3.作业的初始化
- 当resource manager收到调用它的submitApplication() 的消息后,便将请求传递给 YARN的调度器(scheduler)。 调度器分配一个容器(container)。 然后container 在节点管理器node manage的管理下,在container启动 application master 的进程(步骤5a,5b)。
- MapReduce作业的Application Master是一个Java应用程序,它的主类是MRAppMaster。由于将接受来自任务的进度和完成报告(步骤6),因此Application Master对作业的初始化是通过创建多个汇总对象以保持对作业进度的跟踪来完成的。
- Application Maste 接下来,它接受来自共享文件系统的、在客户端计算的输入分片(步骤7,只接受了分片信息)。然后对 每一个分片创建一个map任务 对象以及由mapreduce.job.reduces属性(通过Job的setNumReduceTasks()方法设置)确定的 多个reduce任务对象。任务ID在此时分配
-
4.任务的分配
-
如果作业不适合作为uber任务运行,那么Application Master就会为该作业中的所有map任务和reduce任务向ResourceManager请求容器(步骤8)。首先为map任务发出请求,该请求优先级要高于reduce任务的请求,这是因为所有的map任务必须在reduce的排序阶段能够启动前完成。直到有5%的map任务已经完成时,为reduce任务的请求才会发出
-
Reduce任务能够在集群中任意位置运行,但map任务的请求有着数据本地化局限,这也是调度器所关注的。在理想的情况下,任务是数据本地化的,意味着任务在分片所在的同一节点上运行。可选的情况是,任务可能是本地机架化的,即和分片在同一机架而非同一节点上运行。有一些任务既不是数据本地化,也不是机架本地化,它们会从别的机架,而不是运行所在的机架上获取自己的数据。对于一个特定的作业运行,可以通过查看作业的计数器来确定在每个本地化层次上运行的任务的数量。
-
请求也为任务制定了内存需求和CPU数。在默认情况下,每个map任务和reduce任务都分配到1024MB内存和1个虚拟核,这些值可以在每个作业的基础上进行配置,分别通过4个属性来设置mapreduce.map.memory.mb、mapreduce.reduce.memory.mb、mapreduce.map.cpu.vcores和mapreduce.cpu.vcores。
-
-
5.任务的执行
- 一旦ResourceManager的调度器为任务分配了一个特定节点上的容器,application master就通过与NodeManager通信来启动任务容器(步骤9a、9b)。
- 该任务由主类为YarnChild的一个Java应用程序执行。在它运行任务之前,首先将任务需要的资源本地化,包括作业的配置、Jar文件和所有来自分布式缓存的文件。(步骤10)
- 最后,运行map任务或reduce任务(步骤11)
-
三、 YARN的调度
- YARN中由三种调度器可用:FIFO调度器(FIFO Scheduler),容量调度器(Capacity Scheduler)和公平调度器(Fair Scheduler)。
-
1. FIFO调度器
-
FIFO调度器将应用放置在一个队列中,然后按照提交的顺序(先进先出)运行应用。首先为队列中第一个应用的请求分配资源,第一个应用的请求被满足后再依次为队列中下一个应用服务。
-
FIFO调度器的优点是,简单易懂,不需要任何配置,但是不适合共享集群。大的应用会占用集群中的所有资源,所以每个应用必须等待直到轮到自己运行。
-
-
2. 容量调度器
-
容量调度器允许多个组织共享一个Hadoop集群,每个组织可以分配到全部集群资源的一部分。每个组织被配置一个专门的队列,每个队列被配置为可以使用一定的集群资源。队列可以进一步按层次划分,这样每个组织内的不同用户能够共享该组织队列所分配的资源。在一个队列内,使用FIFO调度策略对应用进行调度。
-
如图所示,单个作业使用的资源不会超过其队列容量。然而,如果队列中有多个作业,并且队列资源不够用了呢?这时如果仍有可用的空闲资源,那么容量调度器可能会将空余的资源分配给队列中的作业,哪怕这会超出队列容量。这称为“弹性队列”(queue elasticity)。
-
正常操作时,容量调度器不会通过强行中止来抢占容器(container)。因此,如果一个队列一开始资源够用,然后随着需求增长,资源开始不够用时,那么这个队列就只能等着其他队列释放容器资源。缓解这种情况的方法是,为队列设置一个最大容量限制,这样这个队列就不会过多侵占其他队列的容量了。当然,这样做是以牺牲队列弹性为代价的,因此需要在不断尝试和失败中找到一个合理的折中。
-
使用容量调度器时,一个独立的专门队列保证小作业一提交就可以启动,由于队列容量是为那个队列中的作业所保留的,因此这种策略是以整个集群的利用率为代价的。这意味着与使用FIFO调度器相比,大作业执行的时间要长。
-
-
3. 容量调度器
-
公平调度器旨在为所有运行的应用公平分配资源 。使用公平调度器,不需要预留一定量的资源,因为调度器会在所有运行的作业之间动态平衡资源。第一个(大)作业启动时,它也是唯一运行的作业,因而获得集群中所有的资源。当第二个(小)作业启动时,它被分配到集群的一半资源,这样每个作业都能公平共享资源。
-
接下来解释资源是如何在队列之间公平共享的。
想象两个用户A和B,分别拥有自己的队列(参见图4-4)。A启动一个作业,在B没有需求时A会分配到全部可用资源;当A的作业仍在运行时B启动一个作业,一段时间后,按照我们先前看到的方式,每个作业都用到了一半的集群资源。这时,如果B启动第二个作业且其他作业仍在运行,那么第二个作业将和B的其他作业(这里是第一个)共享资源,因此B的每个作业将占用四分之一的集群资源,而A仍继续占用一半的集群资源。最终的结果就是资源在用户之间实现了公平共享。
-
-