MapReduce 的工作机制
JobTracker 对应于 NameNode
TaskTracker 对应于 DataNode
DataNode 和NameNode 是针对数据存放来而言的
JobTracker和TaskTracker是对于MapReduce执行而言的
参考:https://zhuanlan.zhihu.com/p/55108455
https://juejin.cn/post/6844903687089815565
在 YARN 体系结构中,ResourceManager 作为守护程序运行,作为架构中的全局的 master 角色,通常在专用计算机上运行,它在各种竞争应用程序之间仲裁可用的群集资源。ResourceManager 跟踪群集上可用的活动节点和资源的数量,并协调用户提交的应用程序应获取哪些资源以及事件。ResourceManager 是具有此信息的单个进程,因此它可以以共享,安全和多租户的方式进行调度决策(例如,根据应用程序优先级,队列容量,ACL,数据位置等)。
当用户提交应用程序时,将启动名为 ApplicationMaster 的轻量级进程实例,以协调应用程序中所有任务的执行。这包括监视任务,重新启动失败的任务,推测性地运行慢速任务以及计算应用程序计数器的总值。ApplicationMaster 和属于其应用程序的任务在 NodeManagers 控制的资源容器中运行。
NodeManager 有许多动态创建的资源容器。容器的大小取决于它包含的资源量,例如内存、CPU、磁盘和网络IO。目前,仅支持内存和CPU。节点上的容器数是配置参数和用于守护程序及OS的资源之外的节点资源总量(例如总CPU和总内存)的乘积。
ApplicationMaster 可以在容器内运行任何类型的任务。例如,MapReduce ApplicationMaster 请求容器启动 map 或 reduce 任务,而 Giraph ApplicationMaster 请求容器运行 Giraph 任务。您还可以实现运行特定任务的自定义 ApplicationMaster
在 YARN 中,MapReduce 简单地降级为分布式应用程序的角色(但仍然是非常流行且有用的),现在称为MRv2。
此外,YARN 通过 ReservationSystem 支持资源预留的概念,ReservationSystem 允许用户通过配置文件来指定资源的时间和时间约束(例如,截止日期)的,并保留资源以确保重要作业的可预测执行。ReservationSystem 可跟踪资源超时,执行预留的准入控制,并动态指示基础调度程序确保预留已满。
ARN 总体上是 master/slave 结构,在整个资源管理框架中,ResourceManager 为 master,NodeManager 是 slave。
YARN的基本组成结构,YARN 主要由 ResourceManager、NodeManager、ApplicationMaster 和 Container 等几个组件构成。
- ResourceManager是Master上一个独立运行的进程,负责集群统一的资源管理、调度、分配等等;
- NodeManager是Slave上一个独立运行的进程,负责上报节点的状态;
- ApplicationMaster相当于这个Application的监护人和管理者,负责监控、管理这个Application的所有Attempt在cluster中各个节点上的具体运行,同时负责向Yarn ResourceManager申请资源、返还资源等;
- Container是yarn中分配资源的一个单位,包涵内存、CPU等等资源,YARN以Container为单位分配资源;
ResourceManager 负责对各个 NadeManager 上资源进行统一管理和调度。当用户提交一个应用程序时,需要提供一个用以跟踪和管理这个程序的 ApplicationMaster,它负责向 ResourceManager 申请资源,并要求 NodeManger 启动可以占用一定资源的任务。由于不同的 ApplicationMaster 被分布到不同的节点上,因此它们之间不会相互影响。
Client 向 ResourceManager 提交的每一个应用程序都必须有一个 ApplicationMaster,它经过 ResourceManager 分配资源后,运行于某一个 Slave 节点的 Container 中,具体做事情的 Task。
容器(Container)这个东西是 Yarn 对资源做的一层抽象。就像我们平时开发过程中,经常需要对底层一些东西进行封装,只提供给上层一个调用接口一样,Yarn 对资源的管理也是用到了这种思想。每一个任务对应一个container,且只能在container中运行。
Container 和集群节点的关系是:一个节点会运行多个 Container,但一个 Container 不会跨节点。任何一个 job 或 application 必须运行在一个或多个 Container 中,在 Yarn 框架中,ResourceManager 只负责告诉 ApplicationMaster 哪些 Containers 可以用,ApplicationMaster 还需要去找 NodeManager 请求分配具体的 Container。
如上所示,Yarn 将CPU核数,内存这些计算资源都封装成为一个个的容器(Container)。需要注意两点:
- 容器由 NodeManager 启动和管理,并被它所监控。
- 容器被 ResourceManager 进行调度。
NodeManager 和 ResourceManager 这两个组件会在下面讲到。
2.2 三个主要组件
再看最上面的图,我们能直观发现的两个主要的组件是 ResourceManager 和 NodeManager ,但其实还有一个 ApplicationMaster 在图中没有直观显示。我们分别来看这三个组件。
ResourceManager
我们先来说说上图中最中央的那个 ResourceManager(RM)。从名字上我们就能知道这个组件是负责资源管理的,整个系统有且只有一个 RM ,来负责资源的调度。它也包含了两个主要的组件:定时调用器(Scheduler)以及应用管理器(ApplicationManager)。
定时调度器(Scheduler):从本质上来说,定时调度器就是一种策略,或者说一种算法。当 Client 提交一个任务的时候,它会根据所需要的资源以及当前集群的资源状况进行分配。注意,它只负责向应用程序分配资源,并不做监控以及应用程序的状态跟踪。
应用管理器(ApplicationManager):同样,听名字就能大概知道它是干嘛的。应用管理器就是负责管理 Client 用户提交的应用。上面不是说到定时调度器(Scheduler)不对用户提交的程序监控嘛,其实啊,监控应用的工作正是由应用管理器(ApplicationManager)完成的。
ApplicationMaster
每当 Client 提交一个 Application 时候,就会新建一个 ApplicationMaster 。由这个 ApplicationMaster 去与 ResourceManager 申请容器资源,获得资源后会将要运行的程序发送到容器上启动,然后进行分布式计算。
这里可能有些难以理解,为什么是把运行程序发送到容器上去运行?如果以传统的思路来看,是程序运行着不动,然后数据进进出出不停流转。但当数据量大的时候就没法这么玩了,因为海量数据移动成本太大,时间太长。但是中国有一句老话山不过来,我就过去。大数据分布式计算就是这种思想,既然大数据难以移动,那我就把容易移动的应用程序发布到各个节点进行计算呗,这就是大数据分布式计算的思路。
可以说,ApplicationMaster 与 ResourceManager 之间的通信是整个 Yarn 应用从提交到运行的最核心部分,是 Yarn 对整个集群进行动态资源管理的根本步骤,Yarn 的动态性,就是来源于多个Application 的 ApplicationMaster 动态地和 ResourceManager 进行沟通,不断地申请、释放、再申请、再释放资源的过程。
NodeManager
NodeManager 是 ResourceManager 在每台机器的上代理,负责容器的管理,并监控他们的资源使用情况(cpu,内存,磁盘及网络等),以及向 ResourceManager/Scheduler 提供这些资源使用报告。 NodeManager作为资源管理系统YARN的一个重要服务,它的主要功能包括节点健康状况检测、分布式缓存机制、目录结构管理、状态机管理、Container生命周期、资源隔离机制等机制。NM管理的是Container而不是任务,一个Container中可能运行着各种任务,但是对NM而言是透明的,它只负责Container相关操作,比如管理Container的生命周期,即启动Container、监控Container和清理Container等
5.1 剖析 MapReduce 作业运行机制
客户端:提交 MapReduce 作业。 jobtracker: 协调作业的运行。 jobtracker 是一个 Java 应用程序,tasktracker: 运行作业划分后的任务。 tasktracker Java 应用程序,分布式文件系统(一般为 HDFS),用来在其他实体间共享作业 文件。
maperd.job.tracker: 如果被设置为local,则在本地测试,如果设置为IP:端口号,运行器则将作业提交给该地址的jobtracker
1. jobtracker 请求一个新的作业 ID( 通过调用 JobTracker getNewJobld() 方法获取)。参见步骤 2
2. 检查作业的输出说明。例如,如果没有指定输出目录或输出目录已经存在,作业就不提交,错误抛回给 MapReduce 程序。
3. 计算作业的输入分片。如果分片无法计算,比如因为输入路径不存在,作业就不提交,错误返回给 MapReduce 程序。
4. 将运行作业所需要的资源(包括作业 JAR 文件、配置文件和计算所得的输入分片)复制到一个以作业 ID 命名的目录下 jobtracker 的文件系统中。作业 JAR副本较多(由 mapred.submit.replication 属性控制,默认值为 10) ,因此在运行作业的任务时,集群中有很多个副本可供 tasktracker 访问
5. 告知 jobtracker 作业准备执行 步骤4
6. 当JobTracker 接收到对其 submitJob( )方法的调用后,会把此调用放入一个内部队列中,交由作业调度器。job scheduler) 进行调度,并对其进行初始化。初始化包括创建一个表示正在运行作业的对象一一封装任务和记录信息,以便跟踪任务的状态和进程(步骤5)。
7. 作业调度器首先从共享文件系统中获取 Job Client 己计算好的输入分片信息(步骤 6)
8. tasktracker 运行一个简单的循环来定期发送"心跳" (heartbeat) jobtracker,判断是否给tasktracker新的任务(步骤7)
9. 通过从共享文件系统把作业的 JAR 文件复制到 tasktracker 所在的文件系统,从而实现作业的 JAR 文件本地化。同时, tasktracker 将应用程序所需要的全郁文件从分布式缓存(详见第 253 页的"分布式缓存"小节)复制到本地磁盘(步骤 8) 。
10. tasktracker 新建一个 TaskRunner、实例来运行该任务。 TaskRunner 启动一个新的 JVM( 步骤的来运行每个任务(步骤 10)
为了选择一个 reduce 任务, jobtracker 简单地从待运行的 reduce 任务列表中选取下一个来执行,用不着考虑数据的本地化。然而,对于一个 map 任务, jobtracker 考虑 tasktracker 的网络位置,并选取一个距离其输入分片文件最近的 tasktracker。我认为主要是map任务需要的是原始数据,数据流较大,所以需要尽可能的在本地节点上运行,而reduce是接受处理后的数据,相对而言不需要考虑距离的远近。
tasktracker 每隔五秒发送"心跳"到 jobtracker(5 秒这个间隔是 最小值,因为"心跳"间隔是实际上由集群的大小来决定的:对于一个更大的集群,间隔会更长一些)
YARN MapReduce2
client node 从资源管理器获取ID(步骤2),计算分片,并将作业资源复制到HDFS上(步骤3),调用资源管理器上的submitApplication 函数提交作业(步骤4),收到调用的消息后,调度器分配一个容器,然后资源管理器在节点管理器的管理下在容器中启动master的进程(5a,5b),MRAppmaster 对作业进行初始化,创建对象对作业进行跟踪,接受来自任务的进度和完成报告(步骤6);MRAppmaster接受来自共享文件系统的输入分片(客户端计算完成)(步骤7),对每个分片创建一个map任务及多个reduce任务。如果作业不是小作业任务(并行速度大于串行速度的任务),Application master 会向资源管理器申请容器(步骤8);一旦资源管理器的调度器为任务分配了容器,application master 就通过与节点管理器通信来启动容器(9a,9b),启动任务前,会将任务需要的资源本地化(配置,JAR,缓存)(步骤10),最后运行MAP和Reduce任务
Application在Yarn中的执行过程,整个执行过程可以总结为三步:
- 应用程序提交
- 启动应用的ApplicationMaster实例
- ApplicationMaster 实例管理应用程序的执行
具体提交过程为:
- 客户端程序向 ResourceManager 提交应用并请求一个 ApplicationMaster 实例;
- ResourceManager 找到一个可以运行一个 Container 的 NodeManager,并在这个 Container 中启动 ApplicationMaster 实例;
- ApplicationMaster 向 ResourceManager 进行注册,注册之后客户端就可以查询 ResourceManager 获得自己 ApplicationMaster 的详细信息,以后客户端就直接和 ApplicationMaster 交互了(这个时候,客户端主动和 ApplicationMaster 交流,应用先向 ApplicationMaster 发送一个满足自己需求的资源请求);
- 在平常的操作过程中,ApplicationMaster 根据
resource-request协议
向 ResourceManager 发送 resource-request请求
;
- 当 Container 被成功分配后,ApplicationMaster 通过向 NodeManager 发送
container-launch-specification信息
来启动Container,container-launch-specification信息
包含了能够让Container 和 ApplicationMaster 交流所需要的资料;
- 应用程序的代码以 task 形式在启动的 Container 中运行,并把运行的进度、状态等信息通过
application-specific协议
发送给ApplicationMaster;
- 在应用程序运行期间,提交应用的客户端主动和 ApplicationMaster 交流获得应用的运行状态、进度更新等信息,交流协议也是
application-specific协议
;
- 一旦应用程序执行完成并且所有相关工作也已经完成,ApplicationMaster 向 ResourceManager 取消注册然后关闭,用到所有的 Container 也归还给系统。
1. MAP REDUCE 失败
首先考虑子任务失败的情况。最常见的情况是 map reduce 任务中的用户代码抛出运行异常。对于 Streaming 任务,如果 Streaming 进程以非零退出代码退出,则被标记为 failed 。任务挂起的处理方式则有不同。一旦 tasktracker 注意到已经有一段时间没有收到进度的更新,便会将任务标记为 failed 。在此之后, JVM 子进程将被自动杀死。如果超时(timeout) 设置(mapred.task.timeout)为0, 将关闭超时判定,所以长时间运行的任务永远不会被标记为 failed. 在这种情况下,被挂起的任务永远不会释放它的任务槽,井随着时间的推移最终降低整个集群的效率。jobtracker 知一个 task attempt 失败后(通过 tasktracker 的"心跳"调用),它将重新调度该任务的执行。 jobtracker 会尝试避免重新调度失败过的 tasktracker 上的任务。此外,如果一个任务的失败次数超过4次,它将不会再被重试。tasktracker 失败是另一种失败模式。如果一个 tasktracker 由于崩愤或运行过于缓慢而失败,它将停止向 jobtracker 发送"心跳,jobtracker 会注意到已经停止发送"心跳"的 tasktracker,
并将它从等待任务调度的 tasktracker 池中移除。如果是未完成的作业, jobtracker 会安排此 tasktracker 上已经运行并成功完成的 map 任务重新运行,因为 reduce 任务无陆访问。jobtracker 失败在所有失败中是最严重的一种。
2. YARN MAP Reduce 失败
application master 失败会进行多次尝试,我们可以设置yarn.resourcemanager.am.max-retries可以设置尝试的次数,默认设置为一次,当AM(application master) 发生故障时,资源管理器会检测到错误并新建一个容器,重新开始任务。
如果节点管理器失败,就会停止向资源管理器发送心跳信息并被移出可用的节点资源管理器池。
5.3 作业的调度
早期版本的 Hadoop 使用作业提交的顺序,使用 FIFO( 先进先出)调度算法来运行作业,随后,加入设置作业优先级的功能,优先级并不支持抢占 (preemption) ,所以高优先级的作业仍然会被那些在高优先级作业被调度之前已经开始的、长时间运行的低优先级的作业所阻塞。
公平调度器的目标是让每个用户公平地共享集群能力。如果只有一 个作业在运行,它会得到集群的所有资源。随着提交的作业越来越多,空闲的任务槽会以"让每个用户公平共享集群"这种方式进行分配。针对多用户调度, Capacity Scheduler 采用的方法稍有不同。集群由很多队列组成 (类似于 Fair Scheduler 的任务地,这些队列可能是层次结构的(因此,一个队列可能是另一个队列的子任务) ,每个队列有一个分配能力。
5.4 shuffle 和排序
MapReduce 确保每个 reducer 的输入都按键排序。系统执行排序的过程一一将 map输出作为输入传给 reducer 一一称为 shuffle。map 函数开始产生输出时,并不是简单地将它写到磁盘。这个过程更复杂,它利用缓冲的方式写到内存,井出于效率的考虑进行预排序。 在写磁盘之前,线程首先根据数据最终要传送到的 reducer 把数据划分成相应的分 (partition) 。在每个分区中,后台线程按键进行内排序,如果有一个 combiner它会在排序后的输出上运行。 写磁盘时压缩 map 输出往往是个很好的主意,因为这样会让写磁盘的速度更快, 节约磁盘空间,并且减少传给 reducer 的数据量。
现在转到处理过程的 reduce 部分。 map 输出文件位于运行 map 任务的 tasktracker 的本地磁盘,reduce 任务需要集群上若干个 map 任务的 map 输出作为其特殊的分区文件。每个map 任务的完成时间可能不同,因此只要有一个任务完成, reduce 任务就开始复制其输出。
map 任务成功完成后,它们会通知其父 tasktracker 状态己更新,然后 tasktracker 进而通知 jobtracker 。对于指定作业, jobtracker 知道 map 输出和 tasktracker之间的映射关系。 reducer 中的一个线程定期询问 jobtracker 以便获取map 输出的位置,直到它获得所有输出位置。
如果 map 输出相当小,会被复制到 reduce tasktracker 的内存,为了合并,压缩的 map 输出(通过 map 任务)都必须在内存中被解压缩。
复制完所有 map 输出被复制期间, reduce 任务进入排序阶段,这个阶段将合并map 输出,维持其顺序排序。这是循环进行的。 在最后阶段,即 reduce 阶段,直接把数据输入 reduce 函数,
5.41 配置调优
总的原则是 给 shuffle 过程尽量多提供内存空间 。有一个平衡问题,也就是要确保 map 函数和 reduce 函数能得到足够的内存来运行。
在map 端,可以通过避免多次溢出写磁盘来获得最佳性能。如果能估算 map 输出 大小,就可以合理地设置 io.sort.*. 属性来尽可能减少溢出写的次数。在 reduce 端,中间数据全部驻留在内存时,就能获得最佳性能。
5.5 任务执行
在一个任务运行比预期慢的时候,它会尽量检测,并启动另一个相同的任务作为备份。这就是所谓的任务的"推测执行"。必须认识到,如果同时启动两个重复的任务,它们会互相竞争,导致推测执行无法工作。这对集群资源是一种浪费。相反,只有在一个作业的所有任务都启动之后才启动推测执行的任务, 推测执行是一种优化措施,它并不能使作业的运行更可靠。默认情况下,推测执行是启用的。
一般都会关闭推测任务,因为这在繁忙的集群中会减少整体的吞吐量,减少冗余任务的执行时间。
处理坏记录的最佳位置在于 mapper reducer 代码。我们可以检测出坏记录井忽 略它,或通过抛出一个异常来中止作业运行,可以使用 Hadoop skipping mode项来自动跳过坏记录。只有在任务失败两次后才会启用 skipping mode,每次 task attempt. skipping mode 都只能检测出一个坏记录,因此这种机制仅适用于检测个别坏记录,如果坏记录多的情况下,可能会增加task attempt的次数,这样才可以检测出所有坏的数据。