上篇中,我们共同探讨了HDFS的设计思想、体系结构等问题,现在,让我们开始真正的Hadoop配置管理之旅。
从理论上来说,Hadoop的配置管理是非常简单的,因为它既没有Oracle数据库那样复杂的体系结构,也没有众多的命令,配置文件也非常简单。但实际上,大家会感觉这样的任务很麻烦,特别是初学者。
Hadoop是一个开放式平台,包含很多框架,每个框架都不断变化,版本升级很快,每个框架都有很多BUG,框架之间的兼容性也存在很大的问题。在配置管理Hadoop的过程中,你会遇到很多莫名其妙的问题,这些问题足以让你精神崩溃。以下软件/框架是本人在实验环境中选用的,它们没有出现什么问题,至少没有出现严重的问题。
- 操作系统:Red Hat Enterprise Linux 7.4
- JDK:jdk1.8.0_152
- Hadoop 3.1.1
- Zookeeper 3.4.12
- Hbase 1.4.7
一、文件解压缩
在Apache官网下载Hadoop 3.1.1,以hadoop用户登录每个Linux操作系统,对文件解压缩。按照规划,所有用到的框架都解压缩到共同的目录/Hadoop下,这样方便统一管理。hadoop用户需要对这个目录具有读写权限。文件解压缩的命令为:
tar zxvf hadoop-3.1.1.tar.gz
接下来,需要在hadoop用户的HOME目录的.bash_profile文件中修改环境变量,例如:
在解压缩所产生的目录中,sbin子目录下是用来启动/关闭node所需要的脚本命令,在bin子目录下包含Hadoop的几个实用工具,在etc/hadoop目录下包含Hadoop的配置文件。
在实际的配置过程中,需要修改很多的文本文件,这样的任务需要在所有操作系统中进行。一个好的做法是,在其中一个操作系统中修改文件,然后通过scp命令把修改之后的文件拷贝到其他操作系统中。
二、单NameNode配置
这是一种典型的分布式配置,包括一个NameNode和多个DataNode,这种配置并没有高可用性,如果唯一的NameNode出现故障,整个HDFS将不可用。
1. 在hadoop-env.sh文件中配置环境变量
在这个文件中,唯一必须指定的环境变量是JAVA_HOME,变量值与.bash_profile文件中的同名变量相同。其他变量如HADOOP_CONF_DIR也可以在这个文件中指定。
2. 在workers文件中定义所有的DataNode
DataNode的定义非常简单,只需要在这个文件中指定DataNode的主机名或者IP地址即可,每行指定一个。例如:
Server1
Server2
Server3
3. 在core-site.xml文件中定义HDFS的全局信息
这个文件的初始内容只包含一对空的标记和,以及少量其他信息。在这对标记之间,需要加入HDFS参数的定义,一般至少需要加入以下定义:
fs.defaultFS
hdfs://Server1:8020
hadoop.tmp.dir
/Hadoop/hadoop-3.1.1/tmp
其中参数fs.defaultFS用于定义NameNode的地址,也就是HDFS的入口地址,客户端对HDFS的方式是通过这个地址进行的。根据上面的定义,Server1为NameNode,它所监听的端口是8020。参数hadoop.tmp.dir用于定义一个目录,HDFS在运行过程中所产生的一些文件被写入这个目录,默认的参数值为/tmp。在默认情况,NameNode的元数据文件,以及DataNode的数据都被写入这个目录的子目录下。这样的默认定义显然不合理,因为操作系统每次重新启动时,都会将/tmp目录清空。
在Hadoop中,所有的XML文件都具有相同的格式,在每个文件中都需要定义若干参数。为节省篇幅,在本文以后的内容中,将不再罗列每个参数的完整格式,而仅仅列出参数的名称和参数值,至于参数的完整格式,以及文件的完整内容,请读者自行脑补。
4. 在hdfs-site.xml文件中定义HDFS的详细信息
在这个文件中的空标记和之间,加入以下参数的定义。实际上,很多参数可以采用默认值,读者可以参考官方文档,了解默认值的定义。
- dfs.replication:指定HDFS中数据的拷贝数,默认为3
- dfs.blocksize:指定HDFS中块的大小,如64M,128M,256M等
- dfs.namenode.name.dir:指定操作系统的本地目录,NameNode将元数据文件写入这个目录,默认值为/tmp下的子目录
- dfs.datanode.data.dir:指定操作系统的本地目录,DataNode将数据写入这个目录,默认值为/tmp下的子目录。在这个目录中,一个块对应一个文件
- dfs.namenode.rpc-address:指定NameNode的RPC地址,如Server1:8020,参数值与core-site.xml定义的入口地址相同
- dfs.namenode.http-address:指定NameNode的HTTP地址,如Server1:9870。管理与可以在浏览器中输入这个地址,通过WEB访问方式查看HDFS中各个node的状态,以及HDFS中的文件
5. 利用scp命令,将上述1-4步所修改的配置文件拷贝到其他操作系统对应的目录下,即Hadoop的etc/hadoop子目录。例如:
6. 对NameNode进行格式化
以hadoop用户身份登录NameNode,执行命令:
hdfs namenode -format
此时NameNode的目录将自动产生,也就是参数dfs.namenode.name.dir指定的目录,在这个目录下将产生最初的Fsimage文件。
三、HDFS的启动和关闭
现在,见证奇迹的时刻到了。如果NameNode和DataNode能够顺利启动,那么Hadoop就具有最基本的雏形了。这些node可以分别启动,也可以集中启动。例如,以hadoop用户登录NameNode,执行下面的命令启动NameNode进程:
hdfs --daemon start namenode
以hadoop用户登录每个DataNode,分别执行下面的命令,启动每个DataNode进程:
hdfs --daemon start datanode
对应的关闭命令分别是:
hdfs --daemon stop namenode
hdfs --daemon stop datanode
如果在此之前配置了SSH对等关系,那么HDFS的启动或关闭就非常容易了。以hadoop用户身份登录任何一个操作系统,分别执行下面的命令来启动或关闭整个HDFS:
start-dfs.sh
stop-dfs.sh
当我们启动HDFS之后,还需要查看每个node的状态是否正常。在操作系统中,每个node都表现为一个Java进程,所以借助于JDK提供的命令jps就能够列出这样的进程。例如,在Server1中执行jps命令,不需要任何命令行参数,结果类似如下:
25392 NameNode
25483 DataNode
这表明,在Server1中运行一个NameNode和一个DataNode。每行的第一个列代表进程的pid,第二个列代表进程名称。
由于各种原因,node可能无法正常启动,这时需要判断出错的原因。最好的方法是查看每种node所产生的日志文件。在Hadoop解压缩所产生的目录下,有一个logs子目录,HDFS的日志文件就在这个目录下,每种node对应一个文件,根据文件名称就能够判断它们的对应关系。各种错误,总有一款或多款等着你。
四、hdfs shell的用法
到此为止,我们得到了一个功能完善的Hadoop运行环境,在这里现在可以运行MapReduce任务了。HDFS为Hadoop提供了一个文件系统空间,它采用了类似于传统文件系统那样的层次结构,最顶层是根目录,每个目录下可以包含子目录和文件。HDFS中的文件在操作系统中是看不见的,为此,Hadoop提供了一个hdfs shell,用来对HDFS中的文件和目录进行管理。hdfs shell通过以下两条命令之一来调用:
hdfs dfs -子命令
hadoop fs -子命令
这两条命令实际是等价的,它们提供了一系列相同的子命令,这些子命令的名称和用法类似于Linux的操作系统命令。例如,下面的命令用于在HDFS的根目录下创建子目录input:
hdfs dfs -mkdir /input
下面的命令用于把本地文件系统中的一个.zip文件上传到HDFS的/input目录中:
下面的命令用于列出HDFS的/input目录中的文件:
hdfs dfs -ls /input
五、多NameNode配置
到目前为止,我们配置的Hadoop已经能够正常运行了,但这样的配置有一个缺陷,那就是:NameNode没有高可用性。为了保证NameNode的安全,可以定义多个NameNode,它们的状态可以在Active和Standby之间切换,这种配置就是HDFS集群。在Hadoop 2.x中,可以定义两个NameNode,在3.x中可以定义两个以上NameNode。
HDFS集群的定义与上述第二步介绍的过程相同,启动方法也与第三步介绍的内容相同,只是core-site.xml和hdfs-site.xml文件的内容有所不同。在core-site.xml文件中通过下面的方式定义HDFS的入口地址:
fs.defaultFS
hdfs://mycluster
其中字符串mycluster代表集群的名称,也是一个名称空间的名称,这个集群的结构在文件hdfs-site.xml中定义。在hdfs-site.xml中,以下参数的含义与之前相同:
- dfs.replication
- dfs.blocksize
- dfs.namenode.name.dir
- dfs.datanode.data.dir
为了定义HDFS集群,需要为以下参数指定适当的参数值:
- dfs.nameservices:定义集群的名称,也就是定义一个名称空间,在这个集群中包含若干Name Node
- dfs.ha.namenodes.mycluster:定义集群mycluster中各个NameNode的ID,在本例中,参数值为nn1,nn2
- dfs.namenode.rpc-address.mycluster.nn1:为ID为nn1的NameNode指定RPC地址,在本例中,参数值为Server1:8020
- dfs.namenode.http-address.mycluster.nn1:为ID为nn1的NameNode指定HTTP地址,在本例中,参数值为Server1:9870
- dfs.namenode.rpc-address.mycluster.nn2:为ID为nn2的NameNode指定RPC地址,在本例中,参数值为Server2:8020
- dfs.namenode.http-address.mycluster.nn2:为ID为nn2的NameNode指定HTTP地址,在本例中,参数值为Server2:9870
在这里虽然定义了多个NameNode,但它们之间并不能共享Fsimage和Editlog文件,也就是说,这两个文件在多个NameNode是不一致的,所以,目前的定义并不完整。
六、NameNode高可用性的配置
在正常情况下,多个NameNode中只有一个状态为Active,这就是主NameNode,其余为从NameNode,状态为Standby。主NameNode负责维护最新的Fsimage和Editlog文件。如果HDFS的结构有变化,主NameNode将更新Fsimage和Editlog文件。HDFS可以采用NFS和QJM两种方法,使Fsimage和Editlog文件在主、从NameNode之间保持一致。这里只介绍QJM方法。
通过QJM方法实现NameNode高可用性的配置过程如下。
1. 在第五步配置的基础上,修改hdfs-site.xml文件,加入以下参数定义:
- dfs.journalnode.edits.dir:为JournalNode指定一个本地目录,每个JournalNode将在这个目录下创建子目录,在本例中,参数值为/Hadoop/hadoop-3.1.1/journal,这个目录需要手工创建
- dfs.namenode.shared.edits.dir :定义JournalNode的IP地址及端口,以及上述目录的子目录名称,一般来说,这个子目录的名称就是集群的名称。在本例中,这个参数的值为:
- dfs.client.failover.proxy.provider.mycluster :指定一个Java类,NameNode类的状态转换通过这个类来实现。这个类是由Hadoop提供的,名称为:
org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider - dfs.ha.fencing.methods:值为sshfence
- dfs.ha.fencing.ssh.private-key-files:值为/home/hadoop/.ssh/id_rsa,即hadoop用户的私钥文件
2. 利用scp命令,将修改之后的配置文件拷贝到其他操作系统对应的目录下
3. 以hadoop用户登录各个JournalNode,执行下面的命令,启动JournalNode。
hdfs--daemon start journalnode
4. 在选定的主NameNode上,以Hadoop用户登录,执行下面的命令,对NameNode进行格式化。
hdfs name node-format
5. 将NameNode的目录,即dfs.name node.name.dir指定的目录,拷贝到其他NameNode对应的目录下
6. 依次启动各个NameNode和各个DataNode
7. 通过执行jps命令,查看各个node的启动情况。例如,在Server1上,这条命令的执行结果如下所示:
29252 NameNode
29607 JournalNode
29388 DataNode
以后,对HDFS的启动应该按照下面的顺序进行:
首先执行下面的命令,启动各个JournalNode:
hdfs --daemon start journalnode
然后执行下面的命令,启动各个NameNode:
hdfs --daemon start namenode
最后执行下面的命令,启动各个DataNode:
hdfs --daemon start datanode
或者在任何一个node上,执行命令start-dfs.sh,这条命令将启动各个JournalNode,NameNode以及DataNode。
七、NameNode状态的转换
在这里不得不抱歉地告诉大家,HDFS集群虽然已经配置完成,而且能够启动起来,但是这样的配置还是有缺陷的。多个NameNode的状态并不能自动转换,需要通过命令进行手工转换,它们的初始状态都是Standby。如果希望实现NameNode状态的自动转换,请继续关注我们的公众号文章。Hadoop提供了hdfs命令,利用这条命令及其子命令和参数,可以对NameNode进行状态控制。
下面这条命令用于查看各个NameNode的状态:
hdfs haadmin -getAllServiceState
这条命令最初的执行结果为:
Server1:8020 standby
Server2:8020 standby
下面这条命令用于把ID为nn1的NameNode转换为Active状态:
hdfs haadmin -transitionToActive nn1
下面这条命令用于把ID为nn1的NameNode转换为Standby状态:
hdfs haadmin -transitionToStandby nn1
下面这条命令用于对两个NameNode进行failover,也就是说,把两个NameNode进行状态互换。这条命令执行的前提是其中一个NameNode的状态为Active,另一个为Standby。
hdfs haadmin -failover nn1 nn2
如果希望NameNode能够实现状态的自动切换,就需要进一步配置ZooKeeper。实际上,ZooKeeper可以为Hadoop中的各种集群提供底层支持,所以,它也是大家必须掌握的一个框架。
编辑:小恒