本节书摘来自华章计算机《Mahout算法解析与案例实战》一书中的第2章,第2.节,作者:樊 哲,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.1 Mahout安装前的准备
就像前面提到的,我们是在Hadoop云平台编写算法时遇到困难才想到使用Mahout的,所以首先要有一个Hadoop云平台才行。这里要注意的是,虽然Mahout最初应用的平台是Hadoop集群平台,但是现在经过扩展,Mahout已经不仅仅适用于Hadoop集群云平台了,还可以单机运行算法,即与使用Java编写的算法是一样的,而且这个算法还是被优化过的。配置基于Hadoop云平台的Mahout 环境所使用的软件包括Linux操作系统、JDK、Hadoop、Mahout、SSH,它们对应的版本见表2-1。
2.1.1 安装JDK
安装完操作系统后,首先要查看是否安装了SSH(SSH包括客户端ssh-client和服务器端ssh-server)。系统开启后,打开终端,输入ssh,如果出现下面的提示:
mahout@ubuntu:~$ ssh
usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
[-D [bind_address:]port] [-e escape_char] [-F configfile]
[-I pkcs11] [-i identity_file]
[-L [bind_address:]port:host:hostport]
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
[-R [bind_address:]port:host:hostport] [-S ctl_path]
[-W host:port] [-w local_tun[:remote_tun]]
[user@]hostname [command]
则ssh-client已经包含在操作系统中了,但是我们还需要使用ssh-server。如何验证是否已经安装了ssh-server呢?首先查看本机IP,使用命令ifconfig即可查看本机IP。然后使用命令ssh IP地址(远程登录IP宿主机的命令,需要用户名和密码)即可验证本机是否已经安装了ssh-server(当然,如果端口22被禁止,那么上面的命令也会出现同样的信息提示)。若出现下面的窗口,则说明已经安装了ssh-server。
mahout@ubuntu:~$ ssh 192.168.128.129
The authenticity of host '192.168.128.129 (192.168.128.129)' can't be established.
ECDSA key fingerprint is 53:c7:7a:dc:3b:bc:34:00:4a:6d:18:1c:5e:87:e7:e8.
Are you sure you want to continue connecting (yes/no)?
若没有安装,则使用下面的命令(apt-get install为Ubuntu特有的安装程序的命令,需要root权限)进行安装。
sudo apt-get install ssh
这里不建议使用自行下载SSH安装包的方式进行下载,因为不同的操作系统对应的下载包是不相同的,而且SSH的安装配置也比较麻烦。在Ubuntu系统中,使用apt-get方式下载并安装软件的方式是比较好的,可以省去很多不必要的麻烦,这里建议系统中没有自带SSH的读者使用上述命令进行安装。安装完毕后,直接按照上面的方式验证是否安装成功。
注
意 这里使用sudo命令,如果用户没有sudo权限,则使用root账号登录修改/etc/sudoers
文件,在 root ALL ALL=(ALL) ALL=(ALL)后面添加一行 yourusername ALL ALL= (ALL) ALL=(ALL)即可。
接着下载并安装JDK。这里一般不建议使用apt-get方式进行JDK的下载及安装,因为使用apt-get下载及安装的JDK是open-jdk,这个JDK对我们后面的测试可能会有影响,一般建议使用Sun公司(2009年被甲骨文公司收购)的JDK,下载最新版本的JDK,地址为:http://www.oracle.com/technetwork/java/javase/downloads/index.html。因为使用的是Ubuntu操作系统,所以要下载的版本扩展名是.tar.gz。另外,也可以直接下载.bin文件,这个文件下载后可以直接运行,比如下载了jdk-6u33-linux-x64.bin,可以直接在终端运行:
./ jdk-6u33-linux-x64.bin
即可安装JDK到系统中,重启完成安装。
下载.tar.gz版本的安装方式为:解压JDK到用户目录/home/mahout(默认用户为mahout),然后使用如下命令(tar命令用户解压缩)。
tar -zxvf jdk-7u25-linux-x64.tar.gz
JDK安装主要就是指配置JDK的路径,访问路径配置好之后就可以正常使用了。在Ubuntu系统中,一般可以通过下面三种方法配置JDK访问路径。
(1)修改/etc/profile文件
使用命令 sudo vim /etc/profile对/etc/profile文件进行编辑(没有安装vim的用户可以使用apt-get命令进行下载及安装),在最后面添加下面的语句:
JAVA_HOME=/home/mahout/jdk1.7.0_25
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME
export PATH
export CLASSPATH
然后使用命令source /etc/profile更新一下/etc/profile(最好重新登录),再使用命令java –version即可查看Java的版本信息,一般如下:
mahout@ubuntu:~/jdk1.7.0_25$ java -version
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
这种安装方式对系统所有用户都有影响,也就是说,配置好相关参数后,系统所有用户都可以使用这个JDK。由于我们暂时只使用一个账号,因此,为了方便,仅采用这种方式安装。
(2)修改~/.bashrc
使用命令sudo vim ~/.bashrc,在最后一行后面添加如下内容。
JAVA_HOME=/home/mahout/jdk1.7.0_25
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME
export PATH
export CLASSPATH
参考上述内容查看Java版本信息即可验证是否安装成功。
(3)在终端直接设置访问路径
在终端中输入命令:
export JAVA_HOME=/home/mahout/jdk1.7.0_25
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
参考上述内容查看Java版本信息即可验证是否安装成功(这种安装方式只能在此终端的范围内有效,因此不建议使用此种方案)。
2.1.2 安装Hadoop
在本书使用的测试环境中,Hadoop是1.0.4版本,可以在下面的网页中进行下载:http://archive.apache.org/dist/hadoop/core/hadoop-1.0.4/ 。因为操作系统的关系,所以这里同样下载扩展名为.tar.gz的Hadoop版本。
Hadoop的安装过程一般包括配置JDK(前面已经配置)、配置SSH无密码登录、设置Hadoop相关配置文件、格式化Hadoop文件系统、启动Hadoop并进行相关验证。下面详细介绍相关的配置。
1.配置SSH
SSH无密码登录主要是为了主节点和各个子节点(slave)的通信需要,如果没有进行无密码登录配置,那么每次启动Hadoop集群时都会要求输入相应的密码,这样当集群数量过多时将会异常麻烦,因此,就需要配置SSH无密码登录了。如何配置SSH无密码登录呢?首先,打开一个终端(打开终端默认当前目录为该用户的home目录,比如用户为mahout,则默认目录为/home/mahout),输入如下命令(ssh-keygen 生成SSH的公匙):
ssh-keygen –t rsa
然后单击〈Enter〉键三次即可,当在终端中出现下面的信息时,说明密钥的公匙已经创建。
mahout@ubuntu:~/.ssh$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/mahout/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/mahout/.ssh/id_rsa.
Your public key has been saved in /home/mahout/.ssh/id_rsa.pub.
The key fingerprint is:
77:2b:e3:b1:2f:b4:d6:07:d8:ee:0d:89:b3:a7:46:76 mahout@ubuntu
The key's randomart image is:
+--[ RSA 2048]----+
| |
| |
| |
| |
| S .o. |
| .=oEo |
| +*=+. |
| .=B+o. |
| o=*o.. |
+-----------------+
进入.ssh目录可以看到有一个名为id_rsa.pub的文件。新创建一个文件名字为authorized_keys的空白文件,同时把id_rsa.pub复制到该文件中。可以直接使用命令(cp为复制命令,当要复制的文件不存在时新建该文件):
cp id_rsa.pub authorized_keys
即可创建并赋值该文件。然后直接输入:
ssh localhost
若直接显示为登录成功,则说明SSH无密码登录配置成功。
2.设置Hadoop相关配置文件
Hadoop的相关配置文件全部在$HADOOP_HOME/conf里面,主要配置的文件有core-site.xml、hadoop-env.xml、hdfs-site.xml、mapred-site.xml。由于我们这里是配置伪分布式的集群,即只有一个节点,因此大多数配置都按默认的,只有个别需要配置。各个文件的详细配置如下:
(1)core-site.xml
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/mahout/hadoop/tmp</value>
<description>A base for other temporary directories.</description>
</property>
<property>
<name>fs.default.name</name>
<description>fs name url</description>
<value>hdfs://ubuntu:9000</value>
</property>
</configuration>
这里配置两个属性,分别是hadoop.tmp.dir和fs.default.name,前者设置后不用每次启动集群都格式化文件系统,后者用于设置文件系统的访问名称(这里设置为ubuntu(测试机器名),不过要在/etc/hosts中配置相应的IP和host机器名的映射关系)。
(2)hadoop-env.xml
export JAVA_HOME=/home/mahout/jdk1.7.0_25
在这个文件中可以设置多个与Hadoop相关的属性,但是JDK属性是必需的,因此,这里设置前面配置好的JDK的路径即可。
(3)hdfs-site.xml
<configuration>
<property>
<name>dfs.replication</name>
<!--设置一个数据块存储的份数-->
<value>1</value>
</property>
<property>
<name>dfs.data.dir</name>
<value>/home/mahout/hadoop/hadoopfs/data </value>
</property>
<property>
<name>dfs.name.dir</name>
<value>/home/mahout/hadoop/hadoopfs/name</value>
</property>
</configuration>
这里配置的文件的备份数量为1,因为这里只有一个节点,默认的是3个。其他两个属性是文件系统在本地的映射。
(4)mapred-site.xml
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>ubuntu:9001</value>
</property>
</configuration>
这个文件主要设置JobTracker的名称(名称和前面core-site.xml文件一样设置为ubuntu)。
如果是集群,一般都是直接配置机器名的,然后在/etc/hosts中配置相应的机器名的映射。这里没有介绍关于第二名称节点(即主节点的备份节点)的配置,因为这里的重点是介绍Mahout算法原理,对相关配置感兴趣的读者可以在笔者的博客http://blog.csdn.net/fansy1990中阅读相关文章。
3.格式化Hadoop文件系统
格式化Hadoop文件系统其实就是创建Hadoop文件系统在本地的映射,这里只使用一条命令进行主名称节点的格式化即可达到对名称节点和数据节点同时创建映射文件的效果。首先打开终端,进入$HADOOP_HOME/bin文件夹中,输入命令(Hadoop的脚本,用于格式化主节点):
./hadoop namenode -format
当出现下面类似的信息时,说明格式化成功。
mahout@ubuntu:~/hadoop-1.0.4/bin$ ./hadoop namenode -format
13/08/10 15:38:31 INFO namenode.NameNode: STARTUP_MSG:
/************************************************************
STARTUP_MSG: Starting NameNode
STARTUP_MSG: host = ubuntu/192.168.128.130
STARTUP_MSG: args = [-format]
STARTUP_MSG: version = 1.0.4
STARTUP_MSG: build = https://svn.apache.org/repos/asf/hadoop/common/branches/branch-1.0 -r 1393290; compiled by 'hortonfo' on Wed Oct 3 05:13:58 UTC 2012
************************************************************/
13/08/10 15:38:31 INFO util.GSet: VM type = 64-bit
13/08/10 15:38:31 INFO util.GSet: 2% max memory = 19.33375 MB
13/08/10 15:38:31 INFO util.GSet: capacity = 2^21 = 2097152 entries
13/08/10 15:38:31 INFO util.GSet: recommended=2097152, actual=2097152
13/08/10 15:38:32 INFO namenode.FSNamesystem: fsOwner=mahout
13/08/10 15:38:32 INFO namenode.FSNamesystem: supergroup=supergroup
13/08/10 15:38:32 INFO namenode.FSNamesystem: isPermissionEnabled=true
13/08/10 15:38:32 INFO namenode.FSNamesystem: dfs.block.invalidate.limit=100
13/08/10 15:38:32 INFO namenode.FSNamesystem: isAccessTokenEnabled=false
accessKeyUpdateInterval=0 min(s), accessTokenLifetime=0 min(s)
13/08/10 15:38:32 INFO namenode.NameNode: Caching file names occuring more than 10 times
13/08/10 15:38:32 INFO common.Storage: Image file of size 112 saved in 0 seconds.
13/08/10 15:38:32 INFO common.Storage: Storage directory /home/mahout/hadoop/hadoopfs/name has been successfully formatted..
13/08/10 15:38:32 INFO namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at ubuntu/192.168.128.130
************************************************************/
同时在本地文件目录/home/mahout/hadoop/下也可以看到产生的相应文件,文件名称和我们在$HADOOP_HOME/conf/中的配置文件设置一致。
4.启动Hadoop并验证
经过上面的几个步骤后,就可以启动Hadoop伪分布式集群了。打开终端,进入$HADOOP_HOME/bin目录,输入命令(启动所有Hadoop进程):
./start-all.sh
或者分别输入:
./start-dfs.sh
./start-mapred.sh
即可启动Hadoop伪分布式集群。第一种方式是启动Hadoop相关的全部进程,第二种方式是先启动文件系统相关的进程,然后再启动任务相关的进程。输入命令后,在终端会有相关的信息提示,如下:
mahout@ubuntu:~/hadoop-1.0.4/bin$ ./start-all.sh
starting namenode, logging to
/home/mahout/hadoop-1.0.4/libexec/../logs/hadoop-mahout-namenode-ubuntu.out
localhost: starting datanode, logging to
/home/mahout/hadoop-1.0.4/libexec/../logs/hadoop-mahout-datanode-ubuntu.out
localhost: starting secondarynamenode, logging to
/home/mahout/hadoop-1.0.4/libexec/../logs/hadoop-mahout-secondarynamenode-ubuntu.out
starting jobtracker, logging to
/home/mahout/hadoop-1.0.4/libexec/../logs/hadoop-mahout-jobtracker-ubuntu.out
localhost: starting tasktracker, logging to
/home/mahout/hadoop-1.0.4/libexec/../logs/hadoop-mahout-tasktracker-ubuntu.out
继续在终端中输入jps命令,可以看到启动的全部进程,一般如下:
mahout@ubuntu:~/hadoop-1.0.4/bin$ jps
18216 NameNode
18448 DataNode
18688 SecondaryNameNode
18773 JobTracker
19005 TaskTracker
19049 Jps
前5行显示的进程即为Hadoop集群所需的全部进程,当看到这5个进程时基本可以确定集群启动了。
注
意 这里只是说基本确定启动,而非确定是真正的启动了。可能由于各个机器的环境不同导致相同的软件配置出现不一样的结果,也有可能这些进程只是暂时启动而已,因此,要使用另外的方式进行确定。同时,当出现进程启动不完全时,要进行相关问题的排查,这个时候在$HADOOP_HOME/logs文件夹里面对应的日志信息会有很大帮助。
为了确保集群确实是启动了,需要在Web UI界面进行相关信息的查看。在浏览器中输入http://ubuntu:50030,可以看到如图2-1所示的任务监控界面。
图2?1 任务监控界面
在任务监控界面的上部可以看到整个集群的状态是RUNNING,说明集群已经成功启动;在集群概述(Cluster Summary)部分可以看到整个集群的情况,包括节点数、可运行的Map和Reduce的个数等;在下半部分可以看到集群是否正在执行任务,以及已经完成的任务和各个任务的详细日志信息;在右上角可以看到快速链接窗口,方便用户快速进入相关页面。在浏览器中输入http://ubuntu:50070 ,即可看到文件系统的监控界面,如图2-2所示。
图2?2 文件系统的监控界面
在图2-2所示的界面中,可以查看相关文件系统的目录、相关数据存储节点的信息、云平台文件系统使用百分比和相关负载情况。
提
示 由于Hadoop中使用的是以机器名进行访问的机制,加上我们在Web UI界面访问也是按照机器名来进行访问的,因此要在/etc/hosts中进行相关的配置,具体配置如下:192.169.128.130 ubuntu。
经过以上步骤,Hadoop伪分布式集群就搭建起来了,但是有时由于每台机器的环境不同,就算环境搭建好,并且在Web UI界面可以看到相关的集群信息,还是不能运行Hadoop程序,因此,这里再介绍一个Hadoop基础程序:单词计数程序。通过对此程序的介绍,可以让读者确定所搭建的Hadoop云平台是否配置好,同时还可以让读者初步了解Hadoop程序的一般编程模式。
单词计数,按照字面意思就是对一个文本中的单词个数进行统计。代码清单2-1是该程序的代码。
代码清单2?1 单词计数
package org.apache.hadoop.examples;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class WordCount {
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length != 2) {
System.err.println("Usage: wordcount <in> <out>");
System.exit(2);
}
Job job = new Job(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
通过观察,可以发现上面代码分为三部分:Mapper、Reducer和Main主程序。Hadoop的编程一般也是要编写相应的Mapper、Reducer和Main主程序的,因此也可将这样的做法当做Hadoop的编程模式。关于Hadoop的原理等其他信息可以参考Hadoop书籍,这里不进行介绍。下面介绍如何运行此程序。
1)打开终端,进入Hadoop安装目录,运行下面的命令(Hadoop脚本,用于上传文件到HDFS文件系统上):
bin/hadoop fs –copyFromLocal LICENSE.txt input/test
上传完毕后,在浏览器中打开文件系统的监控界面可以看到上传的文件,如图2-3所示。
图2?3 上传test文件
2)在终端中输入命令(Hadoop脚本,用于运行jar程序):
bin/hadoop jar hadoop-examples-1.0.4.jar wordcount input/test output/test-output
然后在终端中就会运行该Hadoop程序了,在终端和浏览器任务监控界面都可以看到程序的运行情况,在任务运行完毕后,在浏览器任务监控界面可以看到已经运行完的Hadoop程序,如图2-4所示。
图2?4 WordCount程序在浏览器任务监控界面中的运行情况
通过上面的测试,下面就可以进行Mahout的安装配置了。