1. 前言
Hadoop 是一个实现了 MapReduce 计算模型的开源分布式并行编程框架,借助于 Hadoop, 程序员可以轻松地编写分布式并行程序,将其运行于计算机集群上,完成海量数据的计算。在本文档中,详细介绍了如何部署Hadoop 分布式运行环境,如何让程序分布式运行于多台普通的计算机上等内容(在这里用的是VMware虚拟的linux系统)。而hadoop又分为单机(非分布式)模式,伪分布式运行模式和分布式运行模式,其中前两种运行模式体现不了Hadoop 分布式计算的优势,并没有什么实际意义,但对程序的测试及调试很有帮助,我们先从这两种模式入手,了解基于 Hadoop的分布式并行程序是如何编写和运行的。
然后在对分布式运行模式做一个详细的说明。
2. 部署说明
2.1 单机(非分布式)模式
这种模式在一台单机上运行,没有分布式文件系统,而是直接读写本地操作系统的文件系统。
一、实现进入hadoop目录(这是我们解压的hadoop的目录)
$ cd /cygdrive/c/hadoop-0.20.0
二、在hadoop-0.20.0目录下创建一个test-in的文件夹
$ mkdir test-in
三、在 test-in 目录下创建两个文本文件,文件名分别为file1.txt,file2.tex, WordCount 程序将统计其中各个单词出现次数,具体的操作如下:
$ cd test-in 切入到test-in目录下
$ echo "hello world bye world" >file1.txt
$ echo "hello hadoop goodbye hadoop" >file2.txt
$ cd ..
四、执行hadoop提供的wordcount例子
$ bin/hadoop jar hadoop-0.20.0-examples.jar wordcount test-intest-out
五、执行结果:
$ cd test-out
$ cat part-00000
bye 1
goodbye 1
hadoop 2
hello 2
world 2
注意事项:运行 bin/hadoop jar hadoop-0.20.0-examples.jar wordcount test-intest-out时,务必注意第一个参数是 jar,不是 -jar, 当你用 -jar时,不会告诉你是参数错了,报告出来的错误信息是:Exceptionin thread "main" java.lang.NoClassDefFoundError:org/apache/hadoop/util/ProgramDriver,当时以为是 classpath的设置问题,浪费了不少时间。通过分析 bin/hadoop脚本可知,-jar并不是 bin/hadoop 脚本定义的参数,此脚本会把 -jar 作为Java 的参数,Java的-jar参数表示执行一个 Jar文件(这个 Jar文件必须是一个可执行的 Jar,即在 MANIFEST中定义了主类),此时外部定义的 classpath是不起作用的,因而会抛出java.lang.NoClassDefFoundError异常。而 jar是bin/hadoop 脚本定义的参数,会调用 Hadoop自己的一个工具类 RunJar,这个工具类也能够执行一个 Jar文件,并且外部定义的 classpath有效。
2.2 伪分布式运行模式
这种模式也是在一台单机上运行,但用不同的Java进程模仿分布式运行中的各类结点 ( NameNode, DataNode, JobTracker, TaskTracker,Secondary NameNode ),请注意分布式运行中的这几个结点的区别:
从分布式存储的角度来说,集群中的结点由一个NameNode和若干个 DataNode组成, 另有一个 Secondary NameNode作为 NameNode的备份。从分布式应用的角度来说,集群中的结点由一个JobTracker和若干个TaskTracker组成,JobTracker负责任务的调度,TaskTracker负责并行执行任务。TaskTracker必须运行在DataNode 上,这样便于数据的本地计算。JobTracker和 NameNode则无须在同一台机器上。
a. 首先修改配置文件(conf下)
core-site.xml配置如下信息:
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific propertyoverrides in this file. -->
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://192.168.0.116:9000</value>
<description>The name of the defaultfile system. Either the literal string
"local" or a host:port forDFS.</description>
</property>
</configuration>
l 参数 fs.default.name指定 NameNode的 IP 地址和端口号。缺省值是 file:///,表示使用本地文件系统,用于单机非分布式模式。此处我们指定使用运行于本机localhost上的 NameNode。
hdfs-site.xml配置如下信息:
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overridesin this file. -->
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
<description>Default blockreplication. The actual number of replications
can be specified when the file is created.The default is used if replication
is not specified in createtime.</description>
</property>
</configuration>
l 参数 dfs.replication指定 HDFS中每个 Block 被复制的次数,起数据冗余备份的作用。在典型的生产系统中,这个数常常设置为3。
mapred-site.xml配置信息如下:
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific propertyoverrides in this file. -->
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>192.168.0.116:9001</value>
<description>The host and port thatthe MapReduce job tracker runs at. If
"local", then jobs are run in-processas a single map and reduce task.</description>
</property>
</configuration>
l 参数 mapred.job.tracker指定 JobTracker的 IP 地址和端口号。缺省值是 local,表示在本地同一 Java进程内执行JobTracker和 TaskTracker, 用于单机非分布式模式。此处我们指定使用运行于本机localhost上的 JobTracker (用一个单独的 Java进程做 JobTracker )。
b. 配置 SSH
为了执行ssh localhost的时候,确认你的机器可以用 SSH连接,并且连接时不需要手工输入密码,需要执行如下代码:
$ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
$cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
c. 格式化一个新的文件系统
$cd /cygdrive/c/hadoop-0.16.0
$bin/hadoop namenode–format
d. 运行wordcount应用
将本地文件系统上的./test-in目录拷到HDFS的根目录上,目录名改为input,操作如下:
$bin/hadoop dfs -put ./test-in input
执行bin/hadoop dfs -help可以学习各种HDFS命令的使用
执行wordcount例子,代码如下:
$bin/hadoop jar hadoop-0.16.0-examples.jar wordcount input output
查看执行结果:
$bin/hadoop dfs -get output output
$cat output/*
#也可以直接查看
$bin/hadoop dfs -cat output/*
停止hadoop进程
$bin/stop-all.sh
故障诊断
(1) 执行 $ bin/start-all.sh启动 Hadoop进程后,会启动5个 java进程,同时会在 /tmp目录下创建五个 pid文件记录这些进程 ID 号。通过这五个文件,可以得知 namenode, datanode,secondary namenode, jobtracker, tasktracker分别对应于哪一个 Java进程。当你觉得 Hadoop 工作不正常时,可以首先查看这5个 java进程是否在正常运行。
(2) 使用 web接口。访问 http://localhost:50030可以查看 JobTracker的运行状态。访问 http://localhost:50060可以查看 TaskTracker的运行状态。访问 http://localhost:50070可以查看 NameNode以及整个分布式文件系统的状态,浏览分布式文件系统中的文件以及 log等。
(3) 查看 ${HADOOP_HOME}/logs目录下的 log文件,namenode, datanode,secondary namenode, jobtracker, tasktracker各有一个对应的 log文件,每一次运行的计算任务也有对应用 log文件。分析这些 log文件有助于找到故障原因。
2.3 分布式运行模式
下面的内容我们将重点介绍hadoop分布式模式如何部署。
2.3.1 准备工作
1.虚拟机环境搭建
a) 安装VMware
具体操作参考:VMware_Workstation_6.0绿色汉化版的安装和使用
b) 在VMware上新建三个Linux的虚拟机
使这三台虚拟机器的机器名分布为home06,home07,home08均安装 Redhat Enterprise Linux 5.0 (其它 Linux 发行版亦可), 确保各台机器之间网络畅通,机器名与 IP 地址之间解析正确,从任一台机器都可以 ping 通其它机器的机器名。如有机器名的解析问题,可通过设置/etc/hosts 文件解决,当然更好的解决方法是在你的网络中配置 DNS 服务器。此外,需要在三台机器上创建相同的用户帐号,如 songhq, 或直接使用 root 帐号亦可。在这里我们直接使用root账户。
在这里我们通过设置/etc/hosts 文件来解决机器名的解析问题。home06,home07,home08的/etc/hosts 文件的设置如下图:
关于RedhatEnterprise Linux 5.0如何在VMware上安装以及VMware如何新建虚拟机请参考文档:VMware_Workstation_6.0绿色汉化版的安装和使用.doc 如何在VMware上安装Linux系统.doc
c) 以home06为主节点
我们将使用 home06 作为分布式文件系统 HDFS 的 Name Node及 MapReduce 运行过程中的 Job Tracker 结点,我们将 home06称之为主结点。其它两台机器 (home07, home08) 作为 HDFS 的Data Node 以及 MapReduce 运行过程中的Task Tracker 结点,这些结点可统称为从结点。如你需要部署更多的机器,也是很容易的,将新加入的机器作为 Data Node以及 Task Tracker 结点即可,其配置过程与本文介绍的三台机器的环境类似,此不赘述。(这里的机器为虚拟的机器)
2. 配置SSH
l 目的
在 Hadoop 分布式环境中,Name Node (主节点) 需要通过 SSH 来启动和停止 Data Node (从结点)上的各类进程。我们需要保证环境中的各台机器均可以通过 SSH 登录访问,并且 NameNode 用 SSH 登录 Data Node 时,不需要输入密码,这样 Name Node 才能在后台自如地控制其它结点。可以将各台机器上的 SSH 配置为使用无密码公钥认证方式来实现。
l 操作步骤
现在流行的各类 Linux 发行版一般都安装了 SSH 协议的开源实现OpenSSH, 并且已经启动了 SSH 服务, 即这些机器缺省应该就是支持 SSH 登录的。如果你的机器缺省不支持 SSH, 请下载安装 OpenSSH。
以下是配置 SSH 的无密码公钥认证的过程。首先,在 homer06 机器上执行命令,如下代码所示:
homer06: $ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/caoyuz/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/caoyuz/.ssh/id_rsa.
Your public key has been saved in /home/caoyuz/.ssh/id_rsa.pub.
The key fingerprint is:
2e:57:e2:bf:fd:d4:45:5c:a7:51:3d:f1:51:3c:69:68 root@krusty04
这个命令将为 home06 上的当前用户 root 生成其密钥对,密钥对的保存路径使用缺省的 /home/root/.ssh/id_rsa, 要求输入 passphrase 的时候,直接回车。这样生成的证书以及公钥将存储在 /home/root/.ssh 目录,形成两个文件 id_rsa,id_rsa.pub。然后将 id_rsa.pub 文件的内容复制到每一台机器(包括本机 home06)的 /home/root/.ssh/authorized_keys文件的尾部,如果机器上不存在/home/root/.ssh/authorized_keys文件,可以自行创建一个。请注意 id_rsa.pub 文件的内容是长长的一行,复制时需注意,不要遗漏字符或混入了多余换行符。
接下来可以做一下 SSH 连接测试,从 home06 分别向 home06, home07, home08 发起 SSH 连接请求,确保不需要输入密码就能 SSH 连接成功。注意第一次 SSH 连接时会出现如下提示信息:
The authenticity of host [home06] can't be established.The key fingerprint is: 74:32:91:f2:9c:dc:2e:80:48:73:d4:53:ab:e4:d3:1a Are yousure you want to continue connecting (yes/no)?
请输入 yes, 这样 OpenSSH 会把连接过来的这台主机的信息自动加到 /home/root/.ssh/know_hosts文件中去,第二次再连接时,就不会有这样的提示信息了。
[root@home06 ~]# ssh home07
Last login: Tue Sep 22 00:31:062009 from home06
[root@home07 ~]#
用ssh主机名能直接进入相应的主机就说明配置成功了,如上代码。
补充:
1. 当我们在home6主机上执行ssh-keygen -t rsa之后会在home6的/home/root/.ssh/id_rsa文件夹下生成id_rsa,id_rsa.pub,然后将 id_rsa.pub 文件的内容复制到每一台机器(包括本机 home06)的 /home/root/.ssh/authorized_keys文件的尾部。如果没有authorized_keys这个文件我们需要手动创建一个,创建的命令为:touch authorized_keys,然后在把这个文件copy到home7和home08相应的文件夹中去。
2.在这里我充分应用了SecureCRT这个工具,具体的使用请查看我提供的资料。
2.3.2 部署、配置和启动Hadoop环境
a) 安装 Hadoop 及 jdk1.6
我们首先在主控结点 home06 上安装和配置好 Hadoop。我们把 Hadoop 安装在 /usr/hadoop-0.20.0目录中,并且 Jdk 1.6 安装在 /usr/java 目录下。务必安装jdk1.6,否则会有问题,具体请查看问题文档:搭建hadoop环境遇到的问题.
具体的操作请查看:Linux下jdk1.6的安装和配置.doc
b) 修改 conf/hadoop-env.sh 文件
在其中设置 JAVA_HOME 环境变量:export JAVA_HOME=”/usr/java/jdk1.6.0_16”
一定要记得把前面的#号去掉
c) 修改 conf/hadoop-site.xml 文件
在hadoop-0.20.0版本以前的版本conf文件夹下是存在hadoop-site.xml这个文件的,但是到hadoop-0.20.0这个版本它的配置是需要分开配置的。分成了core-site.xml,hdfs-site.xml以及mapred-site.xml三个文件进行配置。
具体的配置情况如下:
core-site.xml配置如下信息:(公共配置文件)
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific propertyoverrides in this file. -->
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://192.168.0.116:9000</value>
<description>The name of the defaultfile system. Either the literal string
"local" or a host:port forDFS.</description>
</property>
<property>
<name>dfs.name.dir</name>
<value>/tmp/hadoop-root/dfs/name</value>
<description>Determines where on thelocal filesystem the DFS name node
should store the name table. If this is acomma-delimited list of directories
then the name table is replicated in all ofthe directories,
for redundancy. </description>
</property>
<property>
<name>dfs.data.dir</name>
<value>/tmp/hadoop-root/dfs/data</value>
<description>Determines where on thelocal filesystem an DFS data node
should store its blocks. If this is acomma-delimited list of directories,
then data will be stored in all nameddirectories, typically on different devices.
Directories that do not exist areignored.</description>
</property>
</configuration>
l 参数 fs.default.name 指定 Name Node 的 IP 地址和端口号,此处我们将其设定为 home06 及 9000 端口,
hdfs-site.xml配置如下信息:(HDFS配置文件)
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific propertyoverrides in this file. -->
<configuration>
<property>
<name>dfs.replication</name>
<value>2</value>
<description>Default blockreplication. The actual number of replications
can be specified when the file is created.The default is used if replication
is not specified in create time.</description>
</property>
</configuration>
l 参数 dfs.name.dir 指定 Name Node 相关数据在本地文件系统上的存放位置, 此处我们将其设定为/tmp/hadoop-root/dfs/name ,
l 参数 dfs.data.dir 指定 Data Node 相关数据在本地文件系统上的存放位置,此处我们将其设定为 /tmp/hadoop-root/dfs/data 。注意, Hadoop 会自动创建这两个目录,无需事先创建。
mapred-site.xml配置信息如下:(MR配置文件)
<?xml version="1.0"?>
<?xml-stylesheettype="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific propertyoverrides in this file. -->
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>192.168.0.116:9001</value>
<description>The host and port thatthe MapReduce job tracker runs at. If
"local", then jobs are runin-process as a single map and reduce task.</description>
</property>
</configuration>
l 参数 mapred.job.tracker 指定 JobTracker 的 IP 地址和端口号,此处我们将其设定为 home06 及 9001 端口。
l 更多的参数配置,可以参考 src/core/core-default.xml 文件,并在上三个文件中进行相应的设置。
d) 设定主从节点
修改conf/masters 文件,将其中的 localhost 改为 homer06 ,修改 conf/slaves文件,删掉其中的 localhost,将我们的另两台机器 homer07, homer08加入,注意每个机器一行。
Home06的conf/masters文件如下配置:
conf/slaves文件如下配置:
e) 将 Hadoop 部署到其它机器上去
至此, 我们已经在 home06上安装和配置好了 hadoop和 jdk, 现在需要将其部署到其它机器上去,通过 scp命令即可完成,如代码所示:
home06: $ scp -r /usr/hadoop-0.20.0 home07:/usr/hadoop-0.20.0
home06: $ scp -r /usr/java/ jdk1.6.0_16 home07:/usr/java/ jdk1.6.0_16
home06: $ scp -r /usr/hadoop-0.20.0 home08: /usr/hadoop-0.20.0
home06: $ scp -r /usr/java/ jdk1.6.0_16 home08: /usr/java/ jdk1.6.0_16
其中用 scp 拷贝 jdk目录到其它机器上去不是必须的。你只需保证你的所有机器上均安装了 jdk1.5以上版本,并且都是安装在同一目录。
f) 在 home06 上格式化一个新的分布式文件系统
home06: $ cd /usr /hadoop-0.20.0
home06: $ bin/hadoop namenode -format
[root@home06 hadoop-0.20.0]# bin/hadoop namenode -format
09/09/21 16:13:50 INFO namenode.NameNode: STARTUP_MSG:
/************************************************************
STARTUP_MSG: Starting NameNode
STARTUP_MSG: host = home06/192.168.0.116
STARTUP_MSG: args = [-format]
STARTUP_MSG: version = 0.20.0
STARTUP_MSG: build = https://svn.apache.org/repos/asf/hadoop/core/branches/branch-0.20 -r 763504; compiled by 'ndaley' on Thu Apr 9 05:18:40 UTC 2009
************************************************************/
Re-format filesystem in /tmp/hadoop-root/dfs/name ? (Y or N) y
Format aborted in /tmp/hadoop-root/dfs/name
09/09/21 16:13:52 INFO namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at home06/192.168.0.116
************************************************************/
g) 在 home06 上启动 hadoop 进程
home06: $ cd /usr/hadoop-0.20.0
home06: $ bin/start-all.sh
[root@home06 hadoop-0.20.0]# bin/start-all.sh
starting namenode, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-namenode-home06.out
home07: starting datanode, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-datanode-home07.out
home08: starting datanode, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-datanode-home08.out
home06: starting secondarynamenode, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-secondarynamenode-home06.out
starting jobtracker, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-jobtracker-home06.out
home07: starting tasktracker, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-tasktracker-home07.out
home08: starting tasktracker, logging to /usr/hadoop-0.20.0/bin/../logs/hadoop-root-tasktracker-home08.out
启动完成之后,运行 ps-ef 命令应该可以看到home06上启动了 3个新的 java进程 (namenode, secondarynamenode, jobtracker)。
同时,我们可以到home07, home08两台机器上用 ps –ef查看,这两台机器上应该已经自动启动了 2个新的java 进程 (datanode, tasktracker)
通过jps查出:
[root@home06hadoop-0.20.0]# jps
3475 SecondaryNameNode
3616 Jps
3544 JobTracker
3320 NameNode
2.3.3 运行 Hadoop 样例程序
至此,整个 Hadoop 分布式环境已经部署完毕,并已启动相关后台进程。现在我们可以尝试运行一下hadoop提供给我们的 wordcount程序,如下代码所示:
homer06: $ mkdir -p /home/test-in
# 请先将待测的文件放到本地文件系统的/home/test-in目录
homer06: $ cd /home/caoyuz/hadoop-0.16.0
homer06: $ bin/hadoop dfs –put /home/test-in input
# 将本地文件系统上的 /home/test-in 目录拷到 HDFS 的根目录上,目录名改为 input
$ bin/hadoop jar hadoop-0.16.0-examples.jar wordcount input output
#查看执行结果:
# 将文件从 HDFS 拷到本地文件系统中再查看:
$ bin/hadoop dfs -get output output
$ cat output/*
# 也可以直接查看
$ bin/hadoop dfs -cat output/*
现在拥有了一个真正的分布式执行环境,我们的数据分布存储于数据节点 home07及 home08上,可以在这两台机器的/tmp/hadoop-root/dfs/data目录 (这是我们在 conf/core-site.xml中指定的 dfs.data.dir参数)下看到一些数据文件,并且整个 wordcount的计算过程神奇地由home06, home07, home08三台机器并行协同完成,我们还可以很方便的增加更多的机器来参与运算。这就是分布式并行程序的优势:可以很容易地通过加入新的机器来获得更多的存储空间和计算能力,部署的机器越多,就越能有效地完成海量数据的计算。
2.3.4 FAQ
Home06,home07,home08的防火窗要关闭,否则将不能相互访问。Linux关闭防火窗的命令为:/etc/rc.d/init.d/iptablesstop