Microsoft Azure中实践Hadoop

Microsoft Azure提供的虚拟主机大大方便了学习,几秒钟就可以从模板生成一个Linux实例,打开terminal就可以开工。客户端可以是超极本甚至平板,而服务端可以根据需求弹性选择计算能力,方便至极。天下大势,分久必合,合久必分,从最早的兼容分时系统,到后来的个人电脑,现在又开始拥抱云计算,貌似来来回回有些折腾,实际上只是应时而动顺势而为,人们从未停止追求终极计算能力的步伐。

这里要分享的是在Microsoft Azure上安装Hadoop实例的步骤,主要参考了How to install Hadoop一文,根据Microsoft Azure做了适当修改。我选择的是Ubuntu Server 14.04 LTS模板,创建好之后用Putty或者Git Bash等登陆,以前介绍过,不再赘述。

配置环境

首先安装Java:

sudo add-apt-repository ppa:webupd8team/java 
sudo apt-get update
sudo apt-get upgrade 
sudo apt-get install oracle-java7-installer

可以通过java -version来测试是否一切安好。

接下来创建hduser账号专门用于Hadoop操作:

sudo addgroup hadoop 
sudo adduser --ingroup hadoop hduser

然后配置SSH访问,这是Hadoop在运行时所需要的:

su hduser
ssh-keygen -t rsa -P ""
cat .ssh/id_rsa.pub >> .ssh/authorized_keys

可以用ssh localhost来测试是否一切安好。

为了把hduser加到超级用户组里面以方便操作,运行sudo visudo并在最后加入:

hduser ALL=(ALL:ALL) ALL

根据Hadoop在线文档,IPv6必须禁止,所以打开/etc/sysctl.conf并把以下添加到文件最后:

#disable ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

重启Linux并登录后可以用cat /proc/sys/net/ipv6/conf/all/disable_ipv6是否返回1来测试是否一切安好。

安装Hadoop

方便起见我们使用PPA来安装Hadoop:

sudo add-apt-repository ppa:hadoop-ubuntu/stable
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install hadoop

为了方便操作,修改/home/hduser/.bashrc环境文件并把以下内容添加至文件末:

# Set JAVA_HOME (we will configure JAVA_HOME directly later on)
export JAVA_HOME=/usr/lib/jvm/java-7-oracle
# Some convenient aliases and functions for running Hadoop
unalias fs &> /dev/null
alias fs="hadoop fs"
unalias hls &> /dev/null
alias hls="fs -ls"
# Add Hadoop bin/ directory to PATH
export PATH=$PATH:$HADOOP_HOME/bin

下面修改/etc/hadoop/conf.empty/hadoop-env.sh并且把

# export JAVA_HOME=/usr/lib/j2sdk1.5-sun

修改成Oracle官方的JVM:

export JAVA_HOME=/usr/lib/jvm/java-7-oracle

现在我们需要创建一个临时目录供测试Hadoop:

sudo mkdir /home/hduser/tmp
sudo chown hduser:hadoop /home/hduser/tmp
sudo chmod 755 /home/hduser/tmp

接着修改/etc/hadoop/conf/core-site.xml把如下内容添加至xml元素中间:

<property>
  <name>hadoop.tmp.dir</name>
  <value>/home/hduser/tmp</value>
</property>

<property>
  <name>fs.default.name</name>
  <value>hdfs://localhost:54310</value>
</property>

继续修改mapred-site.xml:

<property>
  <name>mapred.job.tracker</name>
  <value>localhost:54310</value>
</property>

继续修改hdfs-site.xml:

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

至此,配置完毕。跟新硬盘一样,在正式使用之前,首先需要格式化NameNode:

hadoop namenode -format

然后就可以启动这个Hadoop集群(虽然仅仅有一个node)啦:

/usr/lib/hadoop/bin/start-all.sh

可以使用jps来测试是否一切安好。

使用Hadoop

仍然使用WordCount来做基准测试。

首先下载样本:

然后把文件复制到HDFS里面:

hadoop dfs -copyFromLocal . /user/hduser/gutenberg

可以通过hadoop dfs -ls /user/hduser/gutenberg来测试是否一切安好。

之后编辑Java源文件WordCount.java如下。注意,这个环境配置下必须在job实例化以后加入job.setJarByClass(WordCount.class);以避免异常发生。源代码如下:

package org.myorg;

import java.io.IOException;
import java.util.*;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

public class WordCount {

    public static class Map extends Mapper&lt;LongWritable, Text, Text, IntWritable&gt; {

        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String line = value.toString();
            StringTokenizer tokenizer = new StringTokenizer(line);
            while (tokenizer.hasMoreTokens()) {
                word.set(tokenizer.nextToken());
                context.write(word, one);
            }
        }
    }

    public static class Reduce extends Reducer&lt;Text, IntWritable, Text, IntWritable&gt; {

        public void reduce(Text key, Iterable&lt;IntWritable&gt; values, Context context)
                throws IOException, InterruptedException {
            int sum = 0;
            for (IntWritable val : values) {
                sum += val.get();
            }
            context.write(key, new IntWritable(sum));
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();

        Job job = new Job(conf, "wordcount");
        job.setJarByClass(WordCount.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);

        job.setInputFormatClass(TextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);

        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        job.waitForCompletion(true);
    }
}

接着编译并创建jar包:

mkdir build
javac -classpath /usr/lib/hadoop/hadoop-core-1.0.3.jar -d build WordCount.java
jar -cvf WordCount.jar -C build/ .

完事具备,直接运行:

hadoop jar WordCount.jar org.myorg.WordCount /user/hduser/gutenberg /user/hduser/gutenberg-output

有图有真相!

运行完毕以后需要把结果复制出来:

mkdir gutenberg-output
hadoop dfs -getmerge /user/hduser/gutenberg-output /home/hduser/gutenberg-output/

终于可以查看统计结果了:

head gutenberg-output/gutenberg-output

至此,我们顺利在Azure虚拟主机中完成第一个Hadoop实例!

习惯了Windows的图形界面,摸索Linux真是有种一夜回到解放前的感觉啊!记得第一次javac还是在1998年的时候,后来还在《电脑爱好者》上连载了《Java咖啡馆》系列,如今Java仍然如日中天,Hadoop整个生态系统更是建立在Java之上,果真好眼光,嘿嘿。

 



张 琪