Hadoop与大数据

Hadoop可以说是大数据的代名词。
其实准确来说是Hadoop家族是大数据的代名词,家族成员有:Hadoop、Hive、Pig、HBase、Sqoop、Zookeeper、Avro、Chukwa等。家族成员每个基本都可以独挡一面,但结合他们,就可以架构出一个大数据平台。成员太多了,只能一个个的学,可以先学习Zookeeper,再到Hadoop,再到Hive等。
Hadoop又包含两部分:分别是HDFS和YARN。这篇文章将着重讲解Hadoop的搭建和HDFS。

Hadoop基本结构

clipboard.png

Hadoop搭建(HDFS部分)

使用Hadoop版本是hadoop-2.7.3.tar.gz
假设有6台机器,分别是(角色以逗号分开):

s1.jevoncode.com,zookeeper,namenode,zkfc,
s2.jevoncode.com,zookeeper,namenode,zkfc,
s3.jevoncode.com,zookeeper,
s4.jevoncode.com,datanode,journalnode,
s5.jevoncode.com,datanode,journalnode,
s6.jevoncode.com,datanode,journalnode,

它们之间需免密钥登录,这里就略,可自行百度。我这里提供关键命令:
1.在s1创建sshkey

ssh-keygen

2.复制公钥到s2

ssh-copy-id root@s2.jevoncode.com

搭建Zookeeper

只要涉及多台机器协调服务,这就不得不安装ZooKeeper了。
安装过程出门左转《2018年第16周-ZooKeeper基本概念(配搭建过程和Master-Workers例子)》
现假设我们在s1,s2和s3安装了ZooKeeper。

搭建Hadoop的HDFS

Hadoop的所有配置都在目录:
1.配置hadoop-env.sh,设置JDK目录和Hadoop目录

export JAVA_HOME=/opt/jdk1.8.0_172
export HADOOP_CONF_DIR=/usr/local/hadoop-2.7.3/etc/hadoops

2.配置core-site.xml,指定逻辑主机名、hadoop的数据目录和ZooKeeper的地址

<configuration>
    <!--用来指定hdfs的老大,ns为固定属性名,表示两个namenode-->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://ns</value>
    </property>
    <!--用来指定hadoop运行时产生文件的存放目录-->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/usr/local/hadoopDataDir/tmp</value>
    </property>
    <!--执行zookeeper地址-->
    <property>
        <name>ha.zookeeper.quorum</name>
        <value>s1.jevoncode.com:2181,s2.c7.local.jevoncode.com:2181,s3.jevoncode.com:2181</value>
    </property>
</configuration>

3.配置hdfs-site.xml,配置逻辑主机名,namenode节点地址,journalnode节点地址,journalnode的数据目录,指定故障转移控制器,配置fencing,指定namenode和datanode的数据目录,副本的个数,权限和节点之间的带宽设置,

<configuration>
    <!--执行hdfs的nameservice为ns,和core-site.xml保持一致-->
    <property>
        <name>dfs.nameservices</name>
        <value>ns</value>
    </property>
    <!--定义ns下有两个namenode,分别是nn1,nn2,然后再具体定义n1和n2-->
    <property>
        <name>dfs.ha.namenodes.ns</name>
        <value>nn1,nn2</value>
    </property>
    <!--nn1的RPC通信地址-->
    <property>
        <name>dfs.namenode.rpc-address.ns.nn1</name>
        <value>s1.jevoncode.com:9000</value>
    </property>
    <!--nn1的http通信地址-->
    <property>
        <name>dfs.namenode.http-address.ns.nn1</name>
        <value>s1.jevoncode.com:50070</value>
    </property>
    <!--nn2的RPC通信地址-->
    <property>
        <name>dfs.namenode.rpc-address.ns.nn2</name>
        <value>s2.jevoncode.com:9000</value>
    </property>
    <!--nn2的http通信地址-->
    <property>
        <name>dfs.namenode.http-address.ns.nn2</name>
        <value>s2.jevoncode.com:50070</value>
    </property>

    <!--指定namenode的元数据在JournalNode上的存放位置,这样,namenode2可以从jn集群里获取
         最新的namenode的信息,达到热备的效果
         格式:qjournal://host1:port1;host2:port2;host3:port3/journalId  
         其中,journalId 是该命名空间的唯一 ID
     -->
    <property>
        <name>dfs.namenode.shared.edits.dir</name>
        <value>qjournal://s4.jevoncode.com:8485;s5.jevoncode.com:8485;s6.jevoncode.com:8485/ns</value>
    </property>
    <!--指定JournalNode存放数据的位置-->
    <property>
        <name>dfs.journalnode.edits.dir</name>
        <value>/usr/local/hadoopDataDir/journal</value>
    </property>

    <!--开启namenode故障时自动切换-->
    <property>
        <name>dfs.ha.automatic-failover.enabled</name>
        <value>true</value>
    </property>
    <!--配置切换的实现方式-->
    <property>
        <name>dfs.client.failover.proxy.provider.ns</name>
        <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
    </property>
    <!--配置规避机制-->
    <property>
        <name>dfs.ha.fencing.methods</name>
        <value>sshfence</value>
    </property>
    <!--配置规避机制的ssh登录秘钥所在的位置,其他用户就指定用户的sshkey,如/homg/jevoncode/.ssh/id_rsa-->
    <property>
        <name>dfs.ha.fencing.ssh.private-key-files</name>
        <value>/root/.ssh/id_rsa</value>
    </property>
 
    <!--配置namenode数据存放的位置,可以不配置,如果不配置,默认用的是core-site.xml里配置的hadoop.tmp.dir的路径-->
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:///usr/local/hadoopDataDir/namenode</value>
    </property>
    <!--配置datanode数据存放的位置,可以不配置,如果不配置,默认用的是core-site.xml里配置的hadoop.tmp.dir的路径-->
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:///usr/local/hadoopDataDir/datanode</value>
    </property>
 
    <!--配置block副本数量-->
    <property>
        <name>dfs.replication</name>
        <value>3</value>
    </property>
    <!--设置hdfs的操作权限,false表示任何用户都可以在hdfs上操作文件-->
    <property>
        <name>dfs.permissions</name>
        <value>false</value>
    </property>

    <!-- 修改内网带宽限制为100MB,默认是10MB-->
    <property>
        <name>dfs.balance.bandwidthPerSec</name>
        <value>104857600</value>
    </property>
     
 
</configuration>

4.创建目录/usr/local/hadoopDataDir

mkdir -p /usr/local/hadoopDataDir

5.配置/etc/profile

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

6.在ZooKeeper创建高可用(HA)用的数据结点

hdfs zkfc -formatZK,这个指令的作用是在zookeeper集群上生成ha

7.启动journalnode服务,在s4,s5,s6节点执行命令:

hadoop-daemons.sh start journalnode

8.格式化namenode,在s1节点执行命令:

hadoop namenode -format

9.在s1启动namenode:

hadoop-daemon.sh start namenode

10.在s2设置namenode状态并启动namenode

hdfs namenode  -bootstrapStandby
hadoop-daemon.sh start namenode

11.在s4,s5,s6节点启动datanode服务:

hadoop-daemon.sh start datanode

12.在namenode节点s1,s2启动故障转移控制器

hadoop-daemon.sh start zkfc

13.现可以查看node结点的启动情况

http://s1.jevoncode.com:50070/

由于故障转移控制器也只是将standby状态的namenode替换为active状态,但之前的active namenode还是会被kiil掉,而不是重启,所以在两个namenode节点都需要shell脚本来监听进程,当进程关闭时自动拉起:
文件名: hdfs-monitor.sh

#!/bin/bash
while true; do
beginTime=`date +%Y%m%d%H%M%S`
echo $beginTime "monitor namenode"
procnum=`ps -ef|grep "namenode"|grep -v grep|wc -l`  
if [ $procnum -eq 0 ]; then
 echo "namenode is crash, restart up..."
 hadoop-daemon.sh start namenode
 echo "finish"
fi
sleep 30  
done

此脚本会每30秒检查namenode进程是否还存在,不存在则自动拉起。
为了保存这shell脚本的日志,我们创建目录

mkdir /usr/local/monitor/namenode/
cp hdfs-monitor.sh /usr/local/monitor/namenode/

后台执行hdfs-monitor.sh

nohup ./hdfs-monitor.sh &

HDFS

HDFS(Hadoop distributed filesystem),Hadoop分布式文件系统。
熟悉操作系统,特别是需要熟悉Linux系统,搭建大数据平台也是需要很多Linux知识的,可以看鸟哥私房菜,我的基本都是从他那学的。熟悉操作系统都知道,操作系统有一块是文件系统,文件系统封装了操作磁盘的细节,每个磁盘都有默认的数据块大小,这是磁盘进行读/写的最小单位。于是我们操作文件,就由文件系统映射到磁盘上对应的块,这对用户来说是无感知的。
同样的,在大数据里,一个机器是不可能装下所有数据,哪怕能装满你的业务等数据,但磁盘的读写是个硬伤,1T硬盘,假设读的数据传输速度为100MB/s,读完正磁盘的数据至少得花2.5个小时。所以假设我们有个100个机器,每部机器存储1%的数据,并行读写(都是顺序读),不到两分钟就可以读完所有数据。但仅使用1%的磁盘容量有点浪费,于是我们可以存储多个数据集。于是通过网络管理多台机器存储的文件的系统,称为分布式文件系统(DFS)。
所以如果你熟悉Linux的文件系统,学起这个分布式文件系统会没那么吃力。
此系统架构于网络之上,势必也引入网络编程的复杂性。如文件系统的能够容忍节点故障且不丢失任何数据。为了避免数据丢失,最常见的做法是复制(replication) :系统保存数据的副本(replica),一旦系统发生故障,就可以使用另外保存的副本。

HDFS的设计

HDFS以流式访问数据的形式来存储超大文件,运行于商业硬件集群上。

  • 超大文件,指具有几百M、几百G甚至几百TB大小的文件。
  • 流式访问数据,在我认为,应该就是顺序访问,与随机访问相对应。HDFS的构建思路:一次写入,多次读写。每次分析都将涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟 比 读取第一条记录的时间延迟更重要。
  • 商用硬件,Hadoop并不需要运行在昂贵且高可靠的硬件上。就是工业批量生产的廉价机子。HDFS的涉及能够在机子故障时,能够继续运行且不让用户察觉到明显的中断。所以在前期机器规划时,机子不用规划太大,一个机子几十T硬盘大小,几十个逻辑核,那就没必要了。我觉得随便十几核,几T数据即可,关键是要可以加机子,N个屌丝胜过一个高富帅。
  • 低时间延迟的数据访问,要求低时间延迟数据访问的应用,例如几十毫秒响应的,不适合在HDFS上运行。HDFS重在数据量,而不是响应时间上,我在上一周文章就提到。HDFS是为了高数据吞吐量应用设计的,这可能会以提高时间延迟作为代价。当然这前提也是资源一定的情况下,可以试下加机子,可惜暂时我还没有这个资源去尝试。
  • 小量的小文件,由于namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于namenode的内存容量。根据经验,每个文件、目录和数据块的存储信息大约站150字节。因此,HDFS默认数据块大小是128M,如果你有100万个文件(122T左右),每个文件占一个数据块,至少需要300M内存。
  • 追加和不能修改,HDFS中的文件写入只支持单个写入者,而且写操作总是以“追加”方式在文件末尾写数据。不支持在文件的任意位置进行修改。这第二点说的的流式访问数据是匹配起来的。

HDFS的架构

clipboard.png

namenode和datanode

HDFS集群上的机子都运行两个服务,分别是namenode和datanode,运行namnode服务的为管理节点,运行datanode的是工作节点。一般是有一个active状态的namenode、一个standby状态的namenode和多个datanode。
namenode管理文件系统的命名空间。它维护这文件系统树及整颗树内所有的文件和目录。这些信息以两个文件形式永久保存在磁盘上:命名空间镜像文件和编辑日志文件。namnode也记录着每个文件中各个块所在的数据节点信息,但它并不永久保存块的位置信息,因为这些信息会在系统启动时根据datanode给的信息重建。
datanode是文件系统的工作结点。它们提供存储并检索数据块的功能(受客户端或namenode调度),并且定期向namenode发送它们所存储的块的列表。
客户端(client)代表用户通过namenode和datanode交互来访问整个文件系统。客户端提供一个类型POSIX(可移植操作系统节目)的文件系统接口,因此用户在编程时无需知道namenode和datanode也可实现其功能。

OJM(quorum journal manager)

如果namenode失效了(就是没有Active状态的namenode),那么所有的客户端,包括MapReduce作业,均无法读写文件,因为namenode时唯一存储原数据与文件到数据块映射的地方。知道有新的namenode上线,Hadoop才能继续往外提供服务。
如果想要从一个失效的namenode恢复,系统管理员得启动一个拥有文件系统原数据副本的新的namenode,并配置datanode和客户端以便使用这个新的namenode,这个新的namenode只有满足以下情形才能响应服务:
1.将命名空间的映像导入内存中;
2.重演编辑日志;
3.接收到足够多的来自datanode1数据块报告并退出安全模式。
对于一个大型,并拥有大量文件和数据块的集群,namenode的冷启动需要30分钟,甚至更长时间。
Hadoop2针对上述问题,增加了对HDFS高可用用(HA)的支持。在这一个视线中,引入了一对活动-备用(active-standby)的namenode。架构上做了以下修改:

  • namenode之间需要通过高可用共享存储实现编辑日志的共享。当standby namenode接管工作后,它将通读共享编辑日志直至末尾,以实现与active namenode的状态同步,并继续读取由活动namenode写入的新条目。
  • datanode需要同时向两个namenode发送数据块处理报告,因为数据块的映射信息存储在namenode的内存中,而非磁盘。
  • 客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明的。这个可以从hdfs-site.xml配置的属性dfs.nameservices看出来,不会指定特定某个namenode,而是设置为一组,通过组名去访问,这一过程Hadoop就会选择Active状态的namenode来交互。
  • standby namenode为active namenode命令空间设置周期性检查点。

高可用共享存储有两种选择:NFS过滤器或群体日志管理器(QJM,quorum journal manager)。
QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计,被推荐用于大多数据HDFS部署中。
QJM以一组日志节点(journalnode)的形式运行,每一次编辑必须写入多数journalnode中。在本篇文章中的部署,就是使用三个journalnode,所以系统能够容忍其中任何一个的损失。这种安排与ZooKeeper的工作方式类似,但并没有用Zookeeper。
QJM在同一时间仅允许一个namenode向编辑日志中写入数据。

QJM理解

首先,Hadoop不是只有namenode和datanode,作为商用,就必须高可用,于是有了QJM。
其次,QJM虽然没有用Zookeeper实现,但QJM也只Hadoop高可用实现的一部分(高可用共享存储的一个选择),还要有选取新的namenode作为active namenode这个实现,也就是后续要将的zkfc,它是基于Zookeeper的实现。

zkfc(ZooKeeper Failover Controller)

除了上述描述的QJM,作为高可用,还需要能够选取新的namenode作为active namenode。这个选取过程称为故障转移。Hadoop有一个称为故障转移控制器(failover controller),管理着将namenode之间的active和standby状态,在两个都是standby状态的namenode时,failover controller会将其中一个namenode状态为active,所以如果你没有启动故障转移控制,会发现两个namenode都是standby,整个HDFS都无法使用。
转移控制器有很多实现方式,但默认一种是使用了ZooKeeper来确保有且仅有一个active namenode。每个namenode节点都运行着一个轻量级的故障转移控制器,其工作作用就是监视宿主namenode是否失效(通过一个简单的心跳机制实现)并在namenode失效时进行故障切换。

在本篇文章中的部署过程,hadoop-daemon.sh start zkfc 就是启动故障转移控制器。用jps命令就能看到java进程名为DFSZKFailoverController。

由于无法确切知道失效的namenode是否已经停止运行。Hadoop提供了方法“规避(fencing)”来确保先前active namenode不会执行危害系统并导致系统崩溃的操作。这个fencing是这样配置的:

    <property>
        <name>dfs.ha.fencing.methods</name>
        <value>sshfence(jevoncode:10034)</value>
    </property>

当active namenode崩溃时,standby namenode会先根据上面的配置,通过ssh确认active namenode已经关闭(强制关闭,kill),然后再将本身状态至为active。
客户端的故障转移通过客户端类库实现透明处理。HDFS URI使用一个逻辑主机名,该逻辑主机名映射到一对namenode地址,客户端类库会根据这个配置访问每一个naemnode,直到完成请求。这配置是dfs.nameservices,如下:


    <!--执行hdfs的nameservice为ns,和core-site.xml保持一致-->
    <property>
        <name>dfs.nameservices</name>
        <value>ns</value>
    </property>
    <!--定义ns下有两个namenode,分别是nn1,nn2,然后再具体定义n1和n2-->
    <property>
        <name>dfs.ha.namenodes.ns</name>
        <value>nn1,nn2</value>
    </property>
    <!--nn1的RPC通信地址-->
    <property>
        <name>dfs.namenode.rpc-address.ns.nn1</name>
        <value>s1.jevoncode.com:9000</value>
    </property>
    <!--nn1的http通信地址-->
    <property>
        <name>dfs.namenode.http-address.ns.nn1</name>
        <value>s1.jevoncode.com:50070</value>
    </property>
    <!--nn2的RPC通信地址-->
    <property>
        <name>dfs.namenode.rpc-address.ns.nn2</name>
        <value>s2.jevoncode.com:9000</value>
    </property>
    <!--nn2的http通信地址-->
    <property>
        <name>dfs.namenode.http-address.ns.nn2</name>
        <value>s2.jevoncode.com:50070</value>
    </property>

电脑杂技集团
208 声望32 粉丝

这家伙好像很懂计算机~