NYC's Blog - Hadoop 2018-05-20T13:42:00+08:00 Typecho http://niyanchun.com/feed/atom/tag/hadoop/ <![CDATA[Oozie任务死锁解决方案]]> http://niyanchun.com/oozie-deadlock-solution.html 2018-05-20T13:42:00+08:00 2018-05-20T13:42:00+08:00 NYC https://niyanchun.com Oozie是Apache下面的一个用于流程调度(workflow scheduler)的系统,主要用于管理Hadoop生态圈中的各种任务,目前支持丰富的任务类型:Java MR、Streaming MR、Pig、Hive、Sqoop、Spark、Shell等。如果想详细了解Ooize强大的调度功能,可参考其官方(http://oozie.apache.org/)文档。本文主要讨论使用Oozie来调度任务时可能出现的死锁问题。

严格来说并不是Ooize导致的死锁,而是YARN的调度机制导致的死锁。我们先来解释一下何时会产生死锁,以及原因。之前在《Hadoop系列六——YARN调度策略》一文中我们已经了解到YARN目前主要有三种调度策略,而最常使用的是Capacity Scheduler和Fair Scheduler,这两种策略都是基于队列的,而且默认只有一个default队列,也就是所有的任务都是放在这个队列中的;同一队列内使用FIFO、Fair、DRF三者之一。而产生死锁的原因和我们进程死锁道理其实是一样的,YARN支持一个任务(在YARN里面一般叫Application,Oozie里面叫Job,本文统一用任务来指代这两个概念)里面可以产生新的子任务,这样父任务会一直等待所有子任务完成后自己才会完成退出。这样如果某一时刻提交了很多任务,这些任务也会产生若干子任务,而资源是有限的,如果这些父任务占光了所有资源,那产生的子任务就只能一直等待,无法运行。而父任务却一直在等待子任务的返回,这样便产生了死锁。

可见,产生死锁的必要条件就是任务会产生子任务,而Ooize的机制恰好是这样的:Oozie拉起一个YARN应用的机制是先拉起一个MapReduce任务(称为oozie launcher任务),然后该MR任务拉起真正的任务(文章刚开始提到的那些任务)。举个死锁的例子:某一时刻我们通过Oozie提交了n个Spark任务(通过Oozie的Spark Action或Shell Action),这样Oozie会向YARN提交n个MapReduce任务(oozie launcher),假设m(m≤n)个MR任务获得了资源并且创建了spark任务,但此时队列内的资源都被这m个MR任务占用了,所以spark任务一直在等待资源,而那m个MR任务却在等待spark任务完成返回,这样便产生了死锁。

目前我还没有发现有比较完美的方案可以完全杜绝这种死锁的情况,但通过一些手段可以极大的避免死锁:

  • 多任务队列。可以看到死锁主要是因为任务抢间占同一资源导致,所以我们可以通过划分多个队列,将父子任务分到不同的队列里面去,各自使用各自队列的资源。比如我们可以专门划分一个队列(假设队列名为root.oozie_launcher)用于放oozie launcher任务,Oozie提供了一个oozie.launcher.mapred.job.queue.name这个配置来设置oozie launcher任务要放置的队列名,目前没有全局配置,只能在每一个workflow.xml里面去配置该选项。当然,单单这样做还是不能比较好的解决这个问题,因为这样只是解决了父子任务竞争同一资源的问题,子任务之间的竞争还没有解决。比如父任务特别多,拉起了非常多的子任务,这些子任务之间因为相互抢占资源,导致都不能返回,那父任务也就会一直等待下去。但我们不可能为每个子任务分配一个单独的队列,而且也无法预估每个子任务到底需要多少资源。这个时候我们就需要另外一种辅助手段了。
  • 并发任务数限制。通过多任务队列的方式我们避免了父子任务的竞争,通过限制队列内并发任务数来限制同一队列内任务的竞争。限制的方式有很多种,在《Hadoop系列六——YARN调度策略》一文中我们已经提到了很多配置项,这里以Fair Scheduler为例(Capacity Scheduler有对应的配置)列几个比较常用且有效的:

    • maxRunningApps:这个配置是最直观的,限制队列内可以同时运行的任务个数。
    • maxAMShare:这个配置比较隐晦一点,用于限制队列内有多少比例的资源可以用来创建AM(Application Master),默认值为0.5,-1表示不检查AM占用的资源,实质就是不限制。为什么这个也可以限制并发数呢?因为每个YARN应用启动时第一件事情就是申请资源创建AM,我们限制了这个值,就相当于限制了AM的个数,从而也就限制了任务的个数。
    • maxResources:这个配置也比较隐晦,需要我们比较了解YARN的调度机制。YARN的elastic queue特性使得队列之间可以相互抢占资源,所以我们的多任务队列方式并不能完全隔离父子任务的竞争(或者说队列之间的竞争),而该配置限制了某个队列资源的上限(自身分配的资源+从别的空闲队列抢占的资源)。举个例子,比如A队列(运行父任务)的资源配额为a,B队列(运行子任务)的资源配额为b,如果某一时刻父任务特别多,而子任务还没有创建或者运行,即子任务队列B资源是空闲的,那父任务就会从B队列中抢占资源,等到子任务后面再去申请的时候,已经被抢走了,而默认子任务只有在自己被抢占的资源释放后,才会获得,所以这样也就产生了死锁。所以我们可以通过该选项设置每个队列资源上限,保证任何情况下都不要把别的队列的资源全部抢占(但为了提高资源使用率,要适当的允许抢占)。同时,为了避免最坏的情况,我们最好也要开启YARN的Preemption特性(开启及配置方法可参见我之前的文章),保证极端情况下,本队列的任务可以强制拿回属于自己队列的资源。

当然,通过上述两种手段只能降低风险,但无法完全杜绝(除非我们不考虑系统资源的利用率,每个队列同一时刻只允许一个任务运行)。举个极端例子,比如我们有两个队列:父任务队列和子任务队列。某一时刻同时上来了两个父任务,并且他们同时创建了两个子任务,这两个子任务开始的时候只需要少量资源(比如MR任务是边运行边根据情况申请资源的),所以他们都在子任务队列运行起来了,但随着不断运行,一直申请资源,某个时刻资源不够用了(不管是自己队列的资源,还是抢占别的队列之后的),那这两个子任务就只能等待了,这样就又产生了死锁了。不过,从系统稳定性角度来说,一般我们要保证系统的(平均)负载低于某个阈值,典型的比如80%或50%(根据具体场景不同),而不是一味的追求太高的资源使用率(比如之前参加阿里菜鸟网络的一个技术分享会的时候,他们说他们的云平台如果检测到CPU使用率超过50%就会预警。其依据是现在的CPU都是一个物理核再虚拟一个核出来)。所以我认为对于YARN中的资源使用也一样,资源使用一直很高并非一件好事,我觉得资源平均使用率能超过50%对许多系统来说已经是一件非常不错的事情了。

上述的这些方案也只是个人的一些观点和解决方案,如果你有更好的避免死锁的方案,欢迎讨论指正。

]]>
<![CDATA[YARN调度策略]]> http://niyanchun.com/yarn-scheduler.html 2018-05-05T23:16:00+08:00 2018-05-05T23:16:00+08:00 NYC https://niyanchun.com 在之前的《Hadoop系列三——YARN简介》中我们已经介绍了YARN的基本架构,但对于其调度策略只是简单提了一下,本文稍微深入一点,作为补充。

YARN总共有三种调度策略:FIFO、Capacity Scheduler、Fair Scheduler。FIFO就是先进先出,最简单,实际中用的也比较少,这里就不再赘述了。Capacity Scheduler比Fair Scheduler出现的早,但随着慢慢的发展和改进,这二者的差异也越来越小了(个人觉得以后这两个合并为一个也是有可能的)。使用情况的话目前CDH(版本为5.8.2)默认使用Fair Scheduler,HDP(版本为2.6)默认使用Capacity Scheduler。下面我们分别介绍。

Capacity Scheduler

Capacity Scheduler主要是为了解决多租户(multiple-tenants)的问题,也就是为了做资源隔离,让不同的组织使用各自的资源,不相互影响,同时提高整个集群的利用率和吞吐量。Capacity Scheduler里面的核心概念或者资源就是队列(queue)。概念理解上很简单,分多个队列,每个队列分配一部分资源,不同组织的应用运行在各自的队列里面,从而做到资源隔离。但为了提高资源(主要是CPU和内存)利用率和系统吞吐量,在情况允许的情况下,允许队列之间的资源抢占。

开启Capacity Scheduler调度策略的方法是在yarn-site.xml中进行如下设置:

yarn.resourcemanager.scheduler.class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler

该策略的配置文件为$HADOOP_HOME/etc/hadoop/capacity-scheduler.xml(目录可能变更)。

下面看一下官方给的Capacity Scheduler的一些特点:

  • Hierarchical Queues:支持分层队列,或者说嵌套队列。在YARN中,队列的组织有点像文件系统的路径,内置了一个根root和一个默认的队列default。现在假设我们的生产(需分配40%的资源)和开发(需分配60%的资源)共用一个集群,而开发又分为team1和team2两个组(两个组资源对半分),那我们可以这样划分队列:
root
    - prod
    - dev
        - team1
        - team2

对应的配置文件为(部分内容):


<property>
    <name>yarn.scheduler.capacity.root.queues</name>
    <value>prod,dev</value>
    <description>for production and development.</description>
</property>

<property>
    <name>yarn.scheduler.capacity.root.dev.queues</name>
    <value>team1,team2</value>
    <description>queue for team1 and team2.</description>
</property>

<property>
    <name>yarn.scheduler.capacity.root.prod.capacity</name>
    <value>40</value>
    <description>40% of all resource.</description>                
</property>

<property>
    <name>yarn.scheduler.capacity.root.dev.capacity</name>
    <value>60</value>
    <description>40% of all resource.</description>                
</property>

<property>
    <name>yarn.scheduler.capacity.root.dev.team1.capacity</name>
    <value>50</value>
    <description>half of dev.</description>        
</property>

<property>
    <name>yarn.scheduler.capacity.root.dev.team2.capacity</name>
    <value>50</value>
    <description>half of dev.</description>                
</property>

这里只简单分了队列,并设置了队列所绑定的资源量,还有很多其他的配置。通过这种嵌套的多层队列机制,我们可以根据需求非常灵活的设置队列。

  • Capacity Guarantees:容量保证,这个很简单,像上面我们已经给每个队列绑定了一定的容量。当然还有很多针对容量的配置项。
  • Security:给以给不同的队列设置不同的权限,甚至每个队列都可以设置一个管理员。总之,权限都是可以针对不同队列去设置的,所以可以提供了相当出色的安全控制手段。
  • Elasticity:弹性。为了提高系统利用率和吞吐量,队列之间支持资源抢占。继续以上面的例子来说:假设刚开始prod队列是空闲的,而dev队列已经满了,那dev里面的应用是可以从prod里面抢占资源的,也就是说dev队列所使用的资源是可以超过预先分配的60%的,但当prod里面的应用页需要资源的时候,dev抢占的部分就需要归还给prod了,正常是需要dev里面的应用释放资源后,prod里面的应用才可以拿回资源。YARN提供了preemption机制,可以强制拿回资源,后面再介绍。
  • Multi-tenancy:多租户很容易理解了,通过不同队列实现资源隔离,从而实现多租户支持。
  • Operability:可操作性主要体现在两个方面:(1)修改队列的很多属性(比如增加队列、修改队列的容量、ACL等)是不需要重启的,直接在应用运行期间就可以直接修改生效。(2)队列可以启停,比如我们可以STOP一个队列,STOP之后,队列里面已有的应用会继续运行完,但新的应用是无法提交到该队列中来的。之后我们也可以重新启动该队列。操作非常方便。
  • Resource-based Scheduling:我们可以给一个应用指定它所需要的资源量,这个量可以比它真正使用的多,目前只支持指定内存。
  • Queue Mapping based on User or Group:我们可以实现指定一个用户/用户组到队列的映射,这样后续可以根据用户/用户组来判断一个应用该提交到哪个队列里面去。比如下面的示例:

    <property>
        <name>yarn.scheduler.capacity.queue-mappings</name>
        <value>u:user1:queue1,g:group1:queue2,u:%user:%user,u:user2:%primary_group</value>
        <description>
        Here, <user1> is mapped to <queue1>, <group1> is mapped to <queue2>, 
        maps users to queues with the same name as user, <user2> is mapped 
        to queue name same as <primary group> respectively. The mappings will be 
        evaluated from left to right, and the first valid mapping will be used.
        </description>
     </property>

映射语法为:[u or g]:[name]:[queue_name]. u表示用户,g表示组。name表示用户名或组名,queue_name表示队列名。u:%user:%user表示队列名与提交应用的用户名相同,u:user2:%primary_group表示队列名与user2这个用户所属的主组组名相同。

  • Priority Scheduling:提交应用的时候可以指定优先级,数字越大优先级越高。目前只有当队列内使用FIFO策略时才支持该特性。
  • Absolute Resource Configuration:上面分配资源量的时候我们使用的是百分比,也支持直接使用指定具体的资源量,比如memory=10240,vcores=12
  • Dynamic Auto-Creation and Management of Leaf Queues:这个特性和上面用户组映射到队列的特性对应,当映射的队列不存在时,可以动态的自动创建。目前只支持创建叶子队列。

这里虽然介绍的是Capacity Scheduler调度策略的特点,但文章刚开始的时候我们就说过Capacity Scheduler和Fair Scheduler已经非常类似了,所以上面的列的这些特性其实在较新的YARN版本里面Fair Scheduler也都是具备的。

在这一节的最后,我们再讲一下Capacity Scheduler和Fair Scheduler的Preemption。细心的人在看前面关于Elasticity特点的讲解时可能就已经有个疑问了:

如果dev队列抢占了prod队列的所有资源,而其运行的应用都是非常耗时的那种,那prod里面应用岂不是将一直“拿不回”资源?

是的,针对这种问题YARN提供了两种解决方案,Preemption就是其中之一。其实原理也很简单,当设置了Preemption之后,prod不会等待dev里面的应用主动释放资源,而是会再等待一定时间之后强制拿回资源。这个强制就是dev里面的一些应用会被杀掉以释放抢占的prod的资源。Capacity Scheduler和Fair Scheduler都支持Preemption,但设置稍有不同,对于Capacity Scheduler策略,开启Preemption需要做做如下配置:

# yarn-site.xml中

yarn.resourcemanager.scheduler.monitor.enable=true
yarn.resourcemanager.scheduler.monitor.policies=org.apache.hadoop.yarn.server.resourcemanager.monitor.capacity.ProportionalCapacityPreemptionPolicy

# capacity-scheduler.xml中
yarn.scheduler.capacity.<queue-path>.disable_preemption=false

除了Preemption之外,还有另外一种解决方案:我们可以设置一个队列最大可分配的资源。对应的两个配置项全局配置项(在yarn-site.xml中设置)为yarn.scheduler.maximum-allocation-mbyarn.scheduler.maximum-allocation-vcores,Capacity Scheduler自己的配置项为(在capacity-scheduler.xml中配置,可覆盖全局配合)yarn.scheduler.capacity..maximum-allocation-mbyarn.scheduler.capacity..maximum-allocation-vcores,前者用于限制内存,后者用于限制CPU,都既支持百分比,也支持具体的值。当我们设置了这两个值以后,队列所能拥有的资源就有了一个上限,当达到这个上限之后,即使其它队列有空闲资源,你也无法再抢占了。

更多Capacity Scheduler配置选项请查看CapacityScheduler Configuration.

Fair Scheduler

其实上面讲的除了具体的配置项Capacity Scheduler和Fair Scheduler不一样以为,特性基本是相同的。相比于Capacity Scheduler设计的初衷是为了支持多租户,Fair Scheduler的设计目标是为了更好的更公平的分配资源。举个例子,在Fair Scheduler调度策略下,假设刚开始系统中有两个队列Q1和Q2,刚开始我们往Q1中提交了一个应用A,此时整个系统中只有A一个应用,所以它可以使用系统的全部资源;然后我们再往Q2中提交一个应用B,此时B将从A那里拿去50%的资源(没有开Preemption的情况下,需要等A内的应用释放后,B才能拿回)。接着,我们再往Q2中提交一个应用C,那C将从B那里再拿去50%的资源,此时,A占系统一半的资源,B、C分别占四分之一的资源。这就是所谓的Fair。

不过,现在Capacity Scheduler和Fair Scheduler相互同化,已经不再那么纯粹了。在Fair Scheduler中也是queue(有时称为pool,即资源池)。特性就不再介绍了,基本和Capacity Scheduler相同,这里主要介绍一下Fair Scheduler比较重要的一些配置项。

开启Fair Scheduler调度策略的方法是在yarn-site.xml中进行如下设置:

yarn.resourcemanager.scheduler.class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler

该策略的配置文件为$HADOOP_HOME/etc/hadoop/capacity-scheduler.xml(目录可能变更)。

假设现在我们要配置如下的一个队列:

root
    - default
    - q
        - sub-q1
        - sub-q2

default是系统默认的,我们只需要配置q、sub-q1、sub-q2即可,示例的配置文件capacity-scheduler.xml如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<allocations>
    <queue name="root">
        <weight>1.0</weight>
        <schedulingPolicy>drf</schedulingPolicy>
        <aclSubmitApps> </aclSubmitApps>
        <aclAdministerApps>*</aclAdministerApps>
        <queue name="default">
            <minResources>512 mb, 1 vcores</minResources>
            <maxResources>24576 mb, 24 vcores</maxResources>
            <weight>1.0</weight>
            <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
            <schedulingPolicy>drf</schedulingPolicy>
            <aclSubmitApps>*</aclSubmitApps>
            <aclAdministerApps>*</aclAdministerApps>
            <maxAMShare>0.6</maxAMShare>
        </queue>
        <queue name="q">
            <minResources>512 mb, 1 vcores</minResources>
            <maxResources>55296 mb, 54 vcores</maxResources>
            <weight>4.0</weight>
            <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
            <schedulingPolicy>drf</schedulingPolicy>
            <aclSubmitApps>*</aclSubmitApps>
            <aclAdministerApps>*</aclAdministerApps>
            <maxAMShare>-1.0</maxAMShare>
            <queue name="sub-q1">
                <minResources>512 mb, 1 vcores</minResources>
                <maxResources>45056 mb, 44 vcores</maxResources>
                <weight>3.0</weight>
                <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
                <schedulingPolicy>drf</schedulingPolicy>
                <aclSubmitApps>*</aclSubmitApps>
                <aclAdministerApps>*</aclAdministerApps>
                <maxAMShare>0.5</maxAMShare>
            </queue>
            <queue name="sub-q2">
                <minResources>512 mb, 1 vcores</minResources>
                <maxResources>16384 mb, 16 vcores</maxResources>
                <weight>1.0</weight>
                <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
                <schedulingPolicy>drf</schedulingPolicy>
                <aclSubmitApps>*</aclSubmitApps>
                <aclAdministerApps>*</aclAdministerApps>
                <maxAMShare>0.6</maxAMShare>
            </queue>
        </queue>
    </queue>
    <defaultQueueSchedulingPolicy>drf</defaultQueueSchedulingPolicy>
    <queuePlacementPolicy>
        <rule name="specified" create="false"/>
        <rule name="default"/>
    </queuePlacementPolicy>
</allocations>

这里我们介绍几个重点配置项:

  • weight:在Fair Scheduler里面不使用百分比,而是用weight,它和百分比很像,但不是百分比。系统默认的default队列的weight默认是1. 如果我们再增加一个q队列,weight设为3,那default将占系统总资源的1/(1+3),q占3/(1+3)。
  • schedulingPolicy:只一个队列内的调度策略,目前支持fifo、fair(默认)、drf,前两者很好理解,这里主要说一下DRF(Dominant Resource Fairness)。fifo先进先出,fair策略仅以内存作为资源判定标准,而DRF高级一些会根据应用对CPU和内存的需求来判断该应用是内存型(首要资源是内存)的还是CPU型(首要资源是CPU)的,然后根据首要资源来公平调度。举个例子比如系统总资源为100个CPU,10TB内存,应用A需要2个CPU、300GB内存,应用B需要6个CPU,100GB内存,和系统总资源求一下百分比,则应用A需要的资源为2%的CPU和3%的内存,3% > 2%,所以应用A是内存型的,同理B需要的资源为6%的CPU和1%的内存,所以B是CPU型的。根据DRF的调度规则,应用A的首要资源为3%,应用B的首要资源为6%,所以给B分配资源比给A分配的资源多一倍。
  • minResources:其实这个最小资源不是用来做限制的,而主要是用来做调度的。举个例子,当若干个队列的资源都被别人抢占,都低于这个最小值时,那如果有新释放的资源,会优先给差的最多(和队列的minResources相比)的队列分配。配置格式为:X mb, Y vcores
  • maxResources:队列所能拥有的资源的最大值,即使抢占别的队列也不能超过该值。和Capacity Scheduler中的yarn.scheduler.capacity..maximum-allocation-mbyarn.scheduler.capacity..maximum-allocation-vcores作用相同。
  • maxAMShare:Capacity Scheduler中类似的设置项。我们知道YARN里面的应用运行的时候首先会拉起来一个ApplicationMaster(AM),而这个选项就是设置我们给队列分配的资源中有多少可以用来创建AM。该选项设置百分比,默认为0.5,表示队列中有一半的资源可以用来创建AM。设置为-1禁用该选项,表示不检查AM占用的资源,实质也就是不限制。该选项可以用来限制队列内的应用数目。
  • maxRunningApps:允许队列内同时运行的应用个数。
  • queuePlacementPolicy:配置应用放置到哪个队列的策略,里面写一系列规则,逐个匹配,类似于编程语言里面的switch语句。合法的rule有:specified,user,primaryGroup,secondaryGroupExistingQueue,nestedUserQueue,default,reject。每个rule都支持 create,表示当该条rule匹配成功之后,如果队列不存在,是否创建,默认为true。如果设为false,当队列不存在时,则匹配不成功,接着往下匹配。一定要保证最后一条规则是肯定可以成功的,类似于switch要有一个default一样。

其他更多选项说明请查看FairScheduler Configuration.

PS: 配置为队列后,我们可以在Resource Manager的Web界面上的schedule一栏中查看这些队列的资源及使用情况。

References:

  1. Hadoop The Definitive Guide(4th Edition)
  2. https://hadoop.apache.org
]]>
<![CDATA[YARN简介]]> http://niyanchun.com/YARN-introduction.html 2018-02-02T20:18:00+08:00 2018-02-02T20:18:00+08:00 NYC https://niyanchun.com YARN(Yet Another Resource Negotiator)是Hadoop 2中开发的一个资源管理框架,主要用于改善之前Hadoop版本中的一些问题。和Mesos类似,是一个比较通用的分布式集群资源管理框架,目前主要用在Hadoop生态圈中。不过YARN一般主要是配合一些其他计算框架使用(比如spark、MapReduce 2(Hadoop 2)、Tez等),用户一般无需关注YARN管理资源的细节,也无需使用YARN提供的API,这些细节都在计算框架中处理好了,我们只需要使用计算框架提供的功能即可。所以本文主要介绍一下YARN的基本架构和一些关键点。

架构解析

和HDFS类似,YARN也是典型的Master-Slave架构:一个集群由一个Master节点和若干个Slave节点组成。在YARN中,Master节点称为Resource Manager,Slave节点称为Node Manager,或者说Node ManagerResource Manager的代理(agent),运行在每一个Slave节点上。在YARN中一个应用称为一个Application,一个Application可能有一个job或者多个job组成的DAG构成。

Resource Manager主要由两部分组成:SchedulerApplicationManager

Scheduler负责集群中Application的调度,实质就是负责为应用分配资源。在YARN中,资源的存在形式是Container(容器),这个Container和云计算中的容器类似,一个容器是内存、CPU、磁盘、网络等资源的一个抽象,应用就运行在容器中。需要注意的是,分配资源(Container)的时候都是分配在集群中的某个Slave节点上的。

调度策略是插件式的,用户可以开发自己的调度策略,目前系统内置了三种调度策略:

  • FIFO Scheduler:最简单的策略,只有一个队列,先来的任务先执行。好处是简单,劣势是大任务可能执行时间很久,导致小任务要等很久。
  • Capacity Scheduler:有多个队列,不同队列分配不同的资源,用户可以选择将任务提交到哪一个队列中。最简单的就是分两个队列,一个队列分配少量资源,专门用来执行可以迅速返回的小任务;另外一个队列分配较多的资源,专门用于执行耗时耗资源的大任务。Hadoop默认使用该策略。
  • Fair Scheduler:动态平衡每个任务所使用的资源。比如当系统内只有一个任务时,就分给他所有资源,如果有第二个任务提交了,就从第一个任务的资源中回收一半,分给第二个任务,以此类推。CDH默认使用该策略。

Scheduler只负责分配资源,应用的管理是由ApplicationManager做的。当我们提交一个应用的时候,就是提交到了ApplicationManager这里,然后等Scheduler为该应用分配了资源(Container)后,ApplicationManager就会在该容器上拉起一个特殊的进程(process):ApplicationMaster,后续ApplicationManager只负责管理ApplicationMaster(比如当ApplicationMaster失败后,重启它)。对于一个简单的应用,可能所有的工作都由ApplicationMaster去做就行,这样改应用就只有一个ApplicationMaster,但是对于复杂的应用,ApplicationMaster还会再申请一些资源,再起一些进程,这些新起的进程也可能再起进程,那对于后续的这些进程的管理都是由这个应用的ApplicationMaster去管理,管理的具体内容就是跟踪记录进程状态,监控进程,失败的时候重启等。也就是说,ApplicationManager是ResourceManager的一部分,而ApplicationMaster则是每个应用都有一个,运行在NodeManager上面的一个容器中,作为ApplicationManager的代理,去管理每一个应用的进程。

正如前面所说,NodeManager作为ResourceManager的代理,运行在每一个Slave节点上,负责接受ResourceManager的命令分配容器,监控进程等工作。

最后附一张YARN官方的架构图:

yarn_architecture

我们以红色的客户端为例,描述一下完整的流程。客户端向ResourceManager(RM)提交了一个MapReduce应用(在MR里面称之为Job),RM(RM里面的Scheduler)选择在右边中间的NodeManager上面为该应用分配容器(分配过程由NodeManager执行),然后RM(RM里面的ApplicationManager)在该容器里面启动了ApplicationMaster,后面ApplicationMaster又多次向RM请求资源,启动了多个进程(右上的NodeManager上有一个,右下的NodeManager上有两个),这些进程都由该应用的ApplicationMaster管理。

MR1 vs MR2

这里的MR1指Hadoop 2之前的MapReduce,MR2指Hadoop 2及之后的MapReduce,也就是YARN。在MR1中,对应的Master是Jobtracker,Slave是Tasktracker

Jobtracker负责应用的调度,进程的管理,对应到YARN里面,就是ResourceManager和ApplicationMaster;同时Jobtracker还要记录已经完成的历史应用,在YARN里面也独立出来由Timeline Server去存储记录。Tasktracker对应的就是YARN里面的NodeManager。在MR1中,资源的抽象不是Container,是Slot,其灵活性和Container相比也很差。

所以可以看到,MR2中将原来Jobtracker需要做的事情分到了好几个进程上面,这样Jobtracker就不会成为集群的瓶颈,集群就可以有更大的规模,官方给的数据是MR1的上限是4000个节点和40000个任务(task),MR2是10000个节点和100000个任务。同时,集群的资源利用率也提高了。另外,MR2的设计更贴近Google发布的MapReduce论文。

对于一个资源管理框架,调度和资源分配策略是非常核心的功能,YARN也提供了非常多的功能,有很多细节本文都没有提到,比如在资源分配上,会尽量的利用Hadoop在存储上的locality特性;在调度上,使用一些延迟调度等小策略,尽可能多的提高集群的资源利用率。有兴趣的同学可以去官网查看相关文档或者参考一些权威的书籍。

References:

]]>
<![CDATA[HDFS简介]]> http://niyanchun.com/hdfs-introduction.html 2018-01-31T16:21:00+08:00 2018-01-31T16:21:00+08:00 NYC https://niyanchun.com 在前文《Hadoop部署》中,我们已经提到过HDFS(Hadoop Distributed File System),它是Hadoop核心的一部分,是Hadoop默认使用的一套分布式文件系统。这里之所以说默认,是因为Hadoop项目其实有一层比较通用的文件系统抽象层,这使得它可以使用多种文件系统,比如本地文件系统、Amazon S3等。当然本文主要介绍HDFS。

设计目标

优势

我们知道Hadoop是为了处理大数据而诞生的一个系统,而HDFS是为了存储大数据而生的一个分布式文件系统,所以他在设计上就考虑了很多大数据处理存储的一些特点,下面我们介绍HDFS在设计上就做的一些假设前提和目标:

  1. 硬件错误。硬件错误是常态而不是异常。HDFS可能由成百上千的服务器所构成,每个服务器上存储着文件系统的部分数据。我们面对的现实是构成系统的组件数目是巨大的,而且任一组件都有可能失效,这意味着总是有一部分HDFS的组件是不工作的。因此错误检测和快速、自动恢复是HDFS最核心的架构目标。
  2. 流式数据访问。运行在HDFS上的应用和普通的应用不同,需要流式访问它们的数据集。HDFS的设计中更多的考虑到了数据批处理,而不是用户交互处理。比之数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。POSIX标准设置的很多硬性约束对HDFS应用系统不是必需的。为了提高数据的吞吐量,在一些关键方面对POSIX的语义做了一些修改。
  3. 大规模数据集。运行在HDFS上的应用具有很大的数据集。HDFS上的一个典型文件大小一般都在G字节至T字节。因此,HDFS被调节以支持大文件存储。它应该能提供整体上高的数据传输带宽,能在一个集群里扩展到数百个节点。一个单一的HDFS实例应该能支撑数以千万计的文件。
  4. 简单的一致性模型。HDFS应用需要一个“一次写入多次读取”的文件访问模型。一个文件经过创建、写入和关闭之后就不需要改变。这一假设简化了数据一致性问题,并且使高吞吐量的数据访问成为可能。Map/Reduce应用或者网络爬虫应用都非常适合这个模型。目前还有计划在将来扩充这个模型,使之支持文件的附加写操作。
  5. “移动计算比移动数据更划算”。一个应用请求的计算,离它操作的数据越近就越高效,在数据达到海量级别的时候更是如此。因为这样就能降低网络阻塞的影响,提高系统数据的吞吐量。将计算移动到数据附近,比之将数据移动到应用所在显然更好。HDFS为应用提供了将它们自己移动到数据附近的接口。

:上面5个特点的描述主要引用自Hadoop 1.0.4官方文档,但是Hadoop至今3.x的beta版本都已经出了,所以文档中有些地方已经过时了,上面已经修正了。

我们再简单总结一下:HDFS设计用来存储海量大文件,但是它对硬件并不挑剔,普通的服务器就可以,而且是支持异构的;HDFS所专注的场景主要是流式的,即写一次,但读很频繁的模式;HDFS专注的是提高整体系统的吞吐量,而不是访问数据的实时性。

劣势

下面我们再介绍一下HDFS目前不太适合(以后可能会改善)的几个场景:

  1. 低延迟访问的场景。如前所述,HDFS主要是针对提高系统吞吐量做了很多设计优化,而不是数据访问的实时性。目前来看,如果对于访问延迟有要求的场景,可以选用HBASE等数据库。
  2. 大量小文件存储。HDFS是用来存储海量大文件的,不太适合存储大量小文件。原因是HDFS的Namenode(后面马上会介绍,专门存储HDFS上面的元数据信息,管理HDFS)是将HDFS上面的元数据存储在内存中的,从而限制HDFS存储量的就是机器内存的大小。根据经验值,一个文件、目录或者块(block)的元数据大概会占用150个字节的内存,也就是说,如果现在有100万个文件,每个文件占一个块,那就需要大概300MB的内存。大量小文件的存储目前来看还是用一些对象存储系统比较好。
  3. 多个写入者以及随机修改。现在的HDFS不支持同时有多个写入者(虽然没有强制限制,但如果有多个写入者,写的结果是未知的),而且现在仅支持追加写(append-only)模式,不支持从任意地方进行数据修改。这个场景后续可能会支持,但就算支持了,相对来说,也不会非常高效。

架构解析

先来张官网的架构图:

架构图

HDFS是传统的Master-Slave架构:一个集群由一个Master节点和若干个Slave节点组成。在HDFS中,Master节点称为Namenode,Slave节点称为Datanode。下面我们详细说明。

Block

首先说一个概念:块(block)。在普通的磁盘文件系统中也有block的概念,它是我们读写数据的最小单元,对用户来说一般是透明的。同样,在HDFS中也有block的概念,但它和传统的文件系统的block有以下两个显著的区别:

  • 块大小。普通的文件系统的块大小一般都是KB级别的,比如Linux一般是4KB;而HDFS的block现在默认大小128MB,而且实际中往往可能会设置的更大一些,设置比较大的主要原因是为了缩短寻址(seek)的时间。如果block足够大,那我们从磁盘上传输数据所耗费的时间将远远大于寻找起始block的时间。这样我们传输一个由多个block组成的数据所花费的时间基本就是由传输速率决定。有一个经验上的快速计算公式,如果寻址时间是10ms,数据传输速率是100MB/s,那如果想让寻址时间是传输时间的1%,block的大小就需要是100MB。
  • 在普通磁盘文件系统中,当一个文件不足一个block大小的时候,它也会独占这个block,其他文件不可以再使用该block。但在HDFS中,一个比block小的文件不会独占一个block,比如一个1MB的文件只会占1MB的空间,而不是128MB,其他文件还可以使用该block的其他空间。

Filesystem Namespace

HDFS的文件系统命名空间与传统文件系统结构类似,由目录和文件组成,支持创建、删除、移动、重命名文件和目录等操作。HDFS支持用户配额(users quotas)和权限控制,权限控制与Linux的权限控制类似。用户配额主要包含两个维度:

  • Name Quotas:限制用户根目录所能包含的文件和目录总数。
  • Space Quotas:限制用户根目录的最大容量(字节)。

目前HDFS还不支持软连接和硬链接。

Namenode和Datanode

Namenode保存着HDFS中所有文件和目录的元数据信息,这些元数据信息以namespace imageedit log的形式存在Namenode所在节点的本地硬盘上面。Namenode还记录着一个文件的所有block在哪些Datanode上面,以及具体的位置,这些信息保存着内存里面,不落盘。因为这些信息每个Datanode会周期性的发给Namenode。Datanode是真正存储数据的节点,它会周期性的将自己上面所存储的block列表发给Namenode。当我们要获取一个文件时,从Namenode处查到这个文件的所有block在哪些Datanode上面,然后去这些Datanode上面查出具体的block。

从上面的描述可以看出,如果Namenode节点挂掉,所有的元数据将丢失,导致整个HDFS不可用。针对这个问题,Hadoop提供了两种机制来避免该问题:

  1. 备份所有的元数据信息。我们可以配置HDFS同时向多个文件系统写这些元数据信息,这个写是同步的、原子的。常用的配置策略是本地写一份,远程的NFS文件系统写一份。
  2. 运行一个Secondary NameNode。之前我们安装Hadoop的时候,已经看到有一个进程叫SecondaryNameNode了。需要注意的是这个SecondaryNameNode并不是Namenode的一个热备(standby),它的作用其实是周期性的将namespace imageedit log合并,产生新的image,防止edit log变的非常大。同时会保留一份合并后的image文件。这样当Namenode挂掉后,我们可以从这个保留的image文件进行恢复。但SecondaryNameNode的操作较Namenode还是有一定延迟的,所以这种方式还是会丢一些数据。

当然,上面的两种机制都只能保证Namenode出现故障时数据不丢或者丢失的少,但无法保证服务继续可用。Hadoop 2目前也提供了热备的方式来实现HA,当一个Namenode故障后,另外一个热备的Namenode马上会接替故障的Namenode对外提供服务。当然要实现这种热备需要做一些配置:

  • 两个Namenode使用高可用的共享存储来共享edit log
  • Datanode需要周期性的将自己上面的block信息同时发送给两个Namenode,因为这个信息是存在Namenode的内存里面的。
  • 客户端需要能够在一个Namenode故障后,自动切换到热备Namenode上。

如果配了热备后,就不需要SecondaryNameNode了,因为standby的Namenode会包含SecondaryNameNode的功能,去做合并。

Data Replication

为了保证一些Datanode挂掉的情况下数据不丢失,HDFS和许多分布式文件系统一样做了数据冗余:默认情况下,一份数据会有三份(即Replication Factor为3,可通过dfs.replication配置项更改)。也就是说,我们在HDFS上面存储一份数据的时候,实际存储了Replication Factor份。那这三份数据的存放位置如何选择呢?目前Hadoop的选择机制如下:

  • 第一份数据存储在发起写操作的客户端所在的Datanode上面。如果该客户端不在集群中Datanode节点上,则随机选择一个负载不是很重的Datanode。
  • 如果第一份数据存储在机架A上面的某一个Datanode节点上,那第二份数据就随机找一个非A机架上的一个Datanode节点存储。
  • 第三份数据找一个与第二份数据同机架,但不同节点的Datanode存储。
  • 如果Replication Factor大于3的话,其他的副本在集群内随机找一些节点存储,同时会尽量避免很多个副本存储于同一机架。

Read&Write

这里借Hadoop The Definitive Guide上的读写图。

先看读的过程:

hdfs read

HDFS的读操作相对比较简单,客户端先去Namenode获取block的信息,然后去Datanode获取block。这里有两个注意点:

  1. 一个block有多个副本,具体获取的时候有优先从离客户端比较近的Datanode获取数据。
  2. 从Namenode拿到block信息后,具体获取block只需要和Datanode交互,这样降低了Namenode的负担,不会使之成为读的瓶颈,并且把所有的度分到了各个Datanode上面,达到了负载均衡。

写的过程:这里假设我们创建一个新文件,然后写数据,最后关闭。

hdfs write

写的过程涉及两个队列和一个pipeline:

  • data queue:写数据的时候,其实就是将数据拆成多个packet写到该队列里面。
  • Datanode pipeline:实质就是一个由多个Datanode组成的列表,Datanode的个数由Replication Factor决定。
  • ack queue:该队列里面存放的也是数据packet。

完整的过程如下:

  1. DistributedFileSystem先向Namenode发一个创建的请求,如果Namenode会做一些检查(如同名文件是否存在、是否有创建权限等)。如果检查通过,就会创建该文件的元数据,此时没有任何block与之关联。接下来就是写具体数据。
  2. FSDataOutputStream把数据拆成多个packet写到data queue里面。然后DataStreamer向Namenode申请新的block来存储这些数据,Namenode会返回一个Datanode pipeline,然后DataStreamer把数据发给pipeline里面的第一个Datanode,该Datanode存储数据后,再把数据转发给第二个Datanode,这样一直到pipeline里面的最后一个Datanode。
  3. FSDataOutputStream同时维护着一个ack queue,里面是各Datanode已经存储了去写,但还未确认的packet。等收到pipeline里面所有Datanode数据写成功的ack后,就删掉ack queue队列里面的对应的packet。如果某个Datanode失败的话,就会被Namenode从pipeline中删除,并重新选择一个Datanode加到pipeline里面去,然后重新写数据到新加的Datanode上面。失败的Datanode上面写的部分不完整数据会在该Datanode恢复后删除。

这里需要注意的是并不是等所有副本都写成功后才向客户端返回,而是只要有dfs.namenode.replication.min(默认值是1)个副本写成功,就向客户端返回成功。其他的副本会异步的去写。

References:

]]>
<![CDATA[Hadoop安装部署]]> http://niyanchun.com/hadoop-cluster-deploy.html 2017-11-21T17:43:00+08:00 2017-11-21T17:43:00+08:00 NYC https://niyanchun.com Hadoop介绍

Hadoop是Apache下面的一个开源的、可靠地、可扩展的分布式计算存储系统。Hadoop项目中主要包含四个部分:

  • Hadoop Common:用于支持Hadoop其他模块的一些公共程序。
  • Hadoop Distributed File System (HDFS):Hadoop使用的一个高吞吐量的分布式文件系统。
  • Hadoop YARN:用于Hadoop任务调度和集群资源管理的框架。
  • Hadoop MapReduce:基于YARN的并行处理系统,用于处理大数据,MapReduce也是一种编程模型。

当我们从Hadoop官网下载二进制包安装好一个Hadoop集群后,就包含上面四个部分。当然,Hadoop更加是一个生态,基于Hadoop产生了很多的其他软件系统,比如Ambari、 Avro、Hive、Hbase、Spark、Zookeeper等。这些都需要在部署好Hadoop后再单独部署,也不属于Hadoop项目的原生部分。而本文后面介绍的安装部署也只针对Hadoop自身项目,即刚开始列举的四个部分;同时本文主要介绍部署,一些原理以及为什么那样配置也不会做详细说明。后续博客会分别介绍Hadoop生态中的各个项目的一些知识,可持续关注。

Hadoop有三种部署方式:

  1. 单机模式(Local (Standalone) Mode)。hadoop的默认配置模式,特点如下:

    • hadoop使用本地文件系统而非分布式文件系统(HDFS)。
    • 不会启动任何hadoop守护进程,map任务和reduce任务作为同一个进程的不同部分来执行。
    • 仅用于开发或调试MapReduce应用程序。
    • 当首次解压hadoop的源码包时,默认选择了最低的配置。这时,三个xml文件均为空,当配置文件为空时,hadoop会完全运行在本地。
  2. 伪分布式模式(Pseudo-Distributed Mode)。 特点如下:

    • 所有的进程都运行在同一台机器上。
    • 使用分布式文件系统,每个hadoop守护进程都是一个独立的进程。
    • 每个job都是由JobTracker服务管理的独立的进程。
    • 集群中只有一个节点,故HDFS的块复制将限制为单个副本。
    • secondary-master和slaves也都将运行于本地主机。
    • 程序的执行逻辑和分布式模式下的执行逻辑一样,因此,常用于测试。
    • 该模式在本地模式的基础上添加了代码的调试功能,允许你检查内存的使用情况、HDFS输入输出、其它的守护进程的交互。
  3. 分布式模式(Fully-Distributed Mode)。运行于多台机器上的真实环境。

以上三种部署方式的差异对比来自http://mojijs.com/2016/11/221130/index.html

当集群中的节点比较多,组件比较多的时候,部署和运维一套Hadoop系统是比较复杂的,所以在生产中或者节点数较多的系统中往往不会手动去一个一个搭建技术组件,而是通过Hadoop的一些集群管理工具去搭建,这些管理工具往往可以简化部署过程,而且带有一些WebUI,也可以很方便的做后期运维,最常见的两个就是Cloudera Manager和Apache Ambari。而对于学习Hadoop的人来说,这些工具屏蔽了太多的细节,往往不是首选。还有另外两种方式,一种是Hadoop支持通过一些Linux的包管理工具直接安装,比如yumapt-get等,对于Mac,也可以直接使用brew安装;另外一种就是通过Hadoop提供的二进制压缩包(当然也有源码包)去安装。如果我们是为了学习Hadoop而去安装的,那最后一种方式当是首选。

本文就讲述如何通过Hadoop的二进制文件包在单台Linux系统上面搭建伪分布式的Hadoop系统。

注意:

  1. 仅用于学习的话,对系统配置基本没有什么要求。
  2. Hadoop是用Java开发的,所以是跨平台的,可以在Windows、Mac、*nix上面安装,本文在Linux上面安装,具体发行版为Ubuntu 16.04.3 LTS.
  3. 本文的所有操作都在root用户下操作,如果是其他非root用户,对于一些操作需要加sudo。Ubuntu默认没有开root用户,可以通过在其他用户下执行sudo passwd root,为root设置密码来开启root用户,设置完之后就可以使用root登录了。

Hadoop集群搭建

第1步: 安装Java

因为Hadoop使用Java开发,所以需要先安装Java环境(JDK),具体JDK版本可参考HadoopJavaVersions。本文使用的是jdk1.8.0_144。从Oracle官网下载JDK包,解压到/opt目录(目录可自己规划,本文所有安装都在/opt目录下),然后在~/.bashrc中配置如下环境变量:

# for java
export JAVA_HOME=/opt/jdk1.8.0_144
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

第2步:创建用户(可选)

一般而言,在生产环境中我们最好为Hadoop单独创建用户,而不是使用系统已有用户,甚至是使用root。官方的推荐是创建一个hadoop用户和hadoop组;再创建三个用户hdfsmapredyarn,这三个用户都属于hadoop这个组。然后hdfs这个用户用来运行HDFS,mapred这个用户用来运行MapReduce,yarn这个用户用来运行YARN。

但是对于我们搭建本地测试学习环境来说,这一步可以省略,可以直接使用你系统已有的用户,本文使用root用户。

第3步:安装Hadoop

  1. 下载Hadoop的binary包,本文下载为是hadoop-2.7.3.tar.gz,解压到/opt目录下:

    tar xf hadoop-2.7.3.tar.gz -C /opt

    注意:如果使用了多用户安装,那解压以后,需要将目录权限改为hadoop:hadoop,即用户和组都改为hadoop

  2. ~/.bashrc中配置如下环境变量:

    export HADOOP_HOME=/opt/hadoop-2.7.3
    export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH

    执行source ~/.bashrc使之生效。

第4步:配置SSH

因为Hadoop的一些管理脚本会通过ssh登录到其他节点上面去执行一些命令,所以需要集群内可以相互免密登录(YARN和HDFS)。虽然我们是单台服务器,但还是需要执行ssh localhost命令时不需要输入密码。配置方式如下:

ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

注意:如果使用了多用户安装,需要分别在hdfsyarn用户下面执行上面3条命令。

第5步:HDFS配置

Hadoop的配置文件和配置参数参数非常多,基本都在$HADOOP_HOME/etc/hadoop目录下,这里先只列举伪分布集群必须配置的项。

$HADOOP_HOME/etc/hadoop/hadoop-env.sh中找到注释掉的JAVA_HOME,取消注释并配置为你的JDK路径:

export JAVA_HOME=/opt/jdk1.8.0_144

$HADOOP_HOME/etc/hadoop/core-site.xml中配置如下内容(配置HDFS文件系统的URI):

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

$HADOOP_HOME/etc/hadoop/hdfs-site.xml中配置如下内容(因为是单机,所以配置HDFS的复制数设置为1,即不复制,默认是3):

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

Tips

默认Hadoop会把数据存储在/tmp/hadoop-${user.name}目录下,涉及的两个配置项为:hadoop.tmp.dirdfs.datanode.data.dir,默认值分别为/tmp/hadoop-${user.name}file://${hadoop.tmp.dir}/dfs/data。如果要修改数据存储位置的话,可以修改这两个参数。前者在core-site.xml中设置,后者在hdfs-site.xml中设置。

配置完以后,第一次使用前需要先格式化HDFS文件系统,执行如下命令:

$HADOOP_HOME/bin/hdfs namenode -format

如果出现类似如下打印,则表示成功:

17/11/21 16:07:45 INFO util.ExitUtil: Exiting with status 0
17/11/21 16:07:45 INFO namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at ubuntu/127.0.1.1
************************************************************/

第6步:Hadoop启停

Hadoop的所有操作命令和控制脚本都在$HADOOP_HOME/bin(命令)和$HADOOP_HOME/sbin(控制脚本)下面:

~ ls $HADOOP_HOME/bin
container-executor  hadoop  hadoop.cmd  hdfs  hdfs.cmd  mapred  mapred.cmd  rcc  test-container-executor  yarn  yarn.cmd
~ ls $HADOOP_HOME/sbin
distribute-exclude.sh  hdfs-config.sh           refresh-namenodes.sh  start-balancer.sh    start-yarn.cmd  stop-balancer.sh    stop-yarn.cmd
hadoop-daemon.sh       httpfs.sh                slaves.sh             start-dfs.cmd        start-yarn.sh   stop-dfs.cmd        stop-yarn.sh
hadoop-daemons.sh      kms.sh                   start-all.cmd         start-dfs.sh         stop-all.cmd    stop-dfs.sh         yarn-daemon.sh
hdfs-config.cmd        mr-jobhistory-daemon.sh  start-all.sh          start-secure-dns.sh  stop-all.sh     stop-secure-dns.sh  yarn-daemons.sh

虽然$HADOOP_HOME/sbin下面有很多脚本,但我们常用的不多,很多脚本都是在其他脚本里面调用的。在新版本里面,start-all.shstop-all.sh脚本已经不推荐使用了,后续版本也可能废弃掉。现在主要使用start-dfs.shstop-dfs.sh来启动和停止HDFS,使用start-yarn.shstop-yarn.sh来启动和停止YARN。

默认Hadoop的日志在$HADOOP_HOME/logs目录,可通过HADOOP_LOG_DIR修改。

执行start-dfs.sh来启动HDFS:

# 执行start-dfs.sh启动
~ start-dfs.sh
Starting namenodes on [localhost]
localhost: starting namenode, logging to /opt/hadoop-2.7.3/logs/hadoop-root-namenode-ubuntu.out
localhost: starting datanode, logging to /opt/hadoop-2.7.3/logs/hadoop-root-datanode-ubuntu.out
Starting secondary namenodes [0.0.0.0]
0.0.0.0: starting secondarynamenode, logging to /opt/hadoop-2.7.3/logs/hadoop-root-secondarynamenode-ubuntu.out

# 使用jps命令来查看进程
~ jps
21283 Jps
21060 SecondaryNameNode
20649 NameNode
20795 DataNode

可以看到HDFS包含三个进程:NameNodeSecondaryNameNodeDataNode。而

执行start-yarn.sh启动YARN(为了更清楚的看清楚进程,我们先执行stop-dfs.sh停掉HDFS):

~ stop-dfs.sh
Stopping namenodes on [localhost]
localhost: stopping namenode
localhost: stopping datanode
Stopping secondary namenodes [0.0.0.0]
0.0.0.0: stopping secondarynamenode

~ start-yarn.sh
starting yarn daemons
starting resourcemanager, logging to /opt/hadoop-2.7.3/logs/yarn-root-resourcemanager-ubuntu.out
localhost: starting nodemanager, logging to /opt/hadoop-2.7.3/logs/yarn-root-nodemanager-ubuntu.out

~ jps
21988 ResourceManager
22184 NodeManager
23118 Jps

可以看到YARN包含两个进程:ResourceManagerNodeManager

第7步:创建用户目录

经过前面6步,我们已经搭建起来了一个基本的Hadoop集群,如果需要让其他用户也可以访问系统,那我们要为每个用户创建一个家目录,并赋予权限,可参考如下命令(比如我使用root用户):

~ hadoop fs -mkdir -p /user/root
~ hadoop fs -chown -R root:root  /user/root

# 也可以限制每个用户可以使用的空间,比如限制为1T
~ hdfs dfsadmin -setSpaceQuota 1t /user/username

集群验证

上面我们已经搭建好了一个单机版的伪分布式Hadoop集群,下面我们来验证一下集群的基本功能是否正常。首先确保已经执行过start-dfs.shstart-yarn.sh,各进程都已经正常启动(可使用jps查看)。

测试1:访问NameNode

HDFS启动后,Namenode默认监听50070端口,我们可以通过浏览器去访问http://<你的IP>:50070

HDFS NameNode

测试2:执行MapReduce程序

首先保证已经创建root用户目录(参照第7步),然后依次执行下面的命令:

# 进入Hadoop安装目录
cd $HADOOP_HOME/
 
# 拷贝例子用到的文件 
bin/hdfs dfs -put etc/hadoop  input

# 执行安装包里面带的例子程序(注意改成你自己下载的版本号)
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar grep input output 'dfs[a-z.]+'

# 查看执行结果
bin/hdfs dfs -get output output
cat output/*

测试3:访问YARN

默认YARN监听8088,我们可以访问http://<你的IP>:8088查看:

YARN

结束语

至此,伪分布式Hadoop集群就算搭建完了,里面涉及到了一些Hadoop的概念配置等都没有解释(比如NameNode、DataNode、ResourceManager等)。后续文章会对Hadoop生态进行介绍,可持续关注。

本文参考自Hadoop官方文档(我认为官方文档永远是优先级最高的文档,不光是Hadoop),完整的Hadoop集群部署方案可参考官方文档Fully-Distributed Operation Setup

]]>