hadoop1.X學習筆記

黃思喆發表於2015-05-27

hadoop學習筆記

hadoop算是經典的分散式系統框架(distributed system framework)了, 整個系統跑在jvm上,所以原生支援java,對於我這個討厭java的人來說簡直是災難, 不過好在有python藉口,通過第三方庫(如:mrjob)可以實現python程式碼寫mapreduce的工作。

題外話,其實呢,個人覺得大部分情況下分散式系統完全用不到。。。 python+sqlite已經可以完成絕大多數個人專案了。一些牢騷以後有空會寫出來。

基礎

單機安裝

我的是mac,一般都可以通過brew安裝

brew install hadoop121

安裝的是1.21版本,為啥不用2.X版本呢,因為後續主要使用spark, 要的也就是hadoop的檔案系統HDFS而已,反正我討厭java不願意用hadoop,而且2.X還在更新中,老折騰累了。。。

安裝注意幾點

JAVA_HOME 的配置

which java確定你用的是啥版本的java,用whereis java確定你的jdk位置

配置你的hosts檔案

具體位置我不知道。。。我用的mac,在/etc資料夾下內容大致如下

127.0.0.1    localhost
255.255.255.255    broadcasthost
::1             localhost
fe80::1%lo0    localhost
127.0.0.1       XXX

配置你的hadoop

位置在hadoop根目錄中有個conf資料夾,裡面有幾個檔案要添些東西

hadoop-env.sh

修改第9行JAVAHOME到你的jdk即可

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home

core-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!-- Put site-specific property overrides in this file. -->

<configuration>
    <property>
    <name>fs.default.name</name>
    <value>localhost:9000</value>
    </property>
    <!--fs.default.name:用來配置namenode,指定HDFS檔案系統的URL,通過該URL我們可以訪問檔案系統的內容,也可以把localhost換成本機IP地址;如果是完全分佈模式,則必須把localhost改為實際namenode機器的IP地址;如果不寫埠,則使用預設埠8020。 -->
    <property>
    <name>hadoop.tmp.dir</name>
    <value>/usr/local/Cellar/hadoop121/tmp/hadoop_tmp</value>
    </property>
    <!-- hadoop.tmp.dir:Hadoop的預設臨時路徑,這個最好配置,如果在新增節點或者其
    他情況下莫名其妙的DataNode啟動不了,就刪除此檔案中的tmp目錄即可。不過如果刪除了NameNode機器的此目錄,那麼就需要重新執行NameNode格式化的命令。該目錄必須預先手工建立。-->

</configuration>

hdfs-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!-- Put site-specific property overrides in this file. -->

<configuration>
    <property>
    <name>dfs.data.dir</name>
    <value>/usr/local/Cellar/hadoop121/hdfs/data</value>
    </property>
    <!--配置HDFS儲存目錄,資料存放目錄,用於datanode存放資料-->
    <property>
    <name>dfs.name.dir</name>
    <value>/usr/local/Cellar/hadoop121/hdfs/name</value>
    </property>
    <!--用來儲存namenode的檔案系統後設資料,包括編輯日誌和檔案系統映像,如果更換地址的話,則需要重新使用hadoop namenode –format命令格式化namenode-->
    <property>
    <name>dfs.replication</name>
    <value>1</value>
    </property>
    <!--用來設定檔案系統冗餘備份數量,因為只有一個節點,所有設定為1,系統預設數量為3-->
</configuration>

mapred-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!-- Put site-specific property overrides in this file. -->

<configuration>
    <property>
    <name>mapred.job.tracker</name>
    <value>localhost:9001</value>
    </property>
    <!--該項配置用來配置jobtracker節點,localhost也可以換成本機的IP地址;真實分佈模式下注意更改成實際jobtracker機器的IP地址-->
</configuration>

其實單機預設也能跑。。。

ssh設定

先要去mac的偏好設定裡把共享中的遠端登入給勾選起來

$ ssh localhost

如果要口令就

$ ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

補充:環境變數設定

linux下我也不太懂,我就拿mac下的來說吧,應該是差不多的

$ cd ~
$ vim .bash_profile

內容增加如下:

#-----------for java-------------------------------------------------------------------------------
export java_home=/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home # Add java_home
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home # Add java_home
export JAVAPATH=/Users/huangsizhe/workspace/javafamily/java
#-----------for hadoop-------------------------------------------------------------------------------
export HADOOP_HOME=/usr/local/Cellar/hadoop121/1.2.1/libexec #你的hadoop根目錄
export HADOOP_CORE=$HADOOP_HOME/hadoop-core-1.2.1.jar #編譯方便
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/bin #把bin資料夾加入環境變數,我圖方便弄的,為了防止衝突建議不加,後面的操作可以用$HADOOP_HOME/bin/XXX代替
export HADOOP_HOME_WARN_SUPPRESS=1 #防止一個警告
export HADOOPPATH=/Users/huangsizhe/workspace/DistributedSystems/hadoop #方便快速定位自己的project,可有可無

單機測試

單機測試主要這幾個:

  1. hadoop系統執行測試
  2. mapreduce測試
  3. python介面測試

hadoop系統執行測試

先格式化namenode

$ hadoop namenode -format

ssh 到localhost $ ssh localhost

然後是啟動hadoop守護程式

$ start-all.sh

測試是否全部守護程式開啟了

$ jps

如果看到類似

1729 DataNode
1973 Jps
1943 TaskTracker
1807 SecondaryNameNode
1651 NameNode
1862 JobTracker

則說明成功了,注意這裡面有一個沒有就說明沒裝好

mapreduce測試

先自己試下官方的例子

cd到自己的專案資料夾,執行如下命令

$ hadoop fs -put $HADOOP_HOME/conf input
$ hadoop jar $HADOOP_HOME/hadoop-examples-1.2.1.jar grep input output 'dfs[a-z.]+'
$ hadoop fs -get output  output

看到多出一個叫output的目錄,那個就是結果了

wordcount

這個是最簡單的mapreduce了,簡單說就是把一篇文章中出現的單詞做個詞頻統計

先把之前的input和output刪了,省的亂

$ hadoop fs -rmr input
$ hadoop fs -rmr output

專案檔案結構如下:

project |
        |-src
        |-input
        |-lib
        |-bin

WordCount.java放在src裡,內容如下:

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.mapred.*;
import org.apache.hadoop.util.*;

public class WordCount {

    public static class Map extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

    public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
        String line = value.toString();
        StringTokenizer tokenizer = new StringTokenizer(line);
        while (tokenizer.hasMoreTokens()) {
            word.set(tokenizer.nextToken());
            output.collect(word, one);
            }
        }
    }

    public static class Reduce extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
        public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
            int sum = 0;
            while (values.hasNext()) {
                sum += values.next().get();
            }
            output.collect(key, new IntWritable(sum));
        }
    }

    public static void main(String[] args) throws Exception {
        JobConf conf = new JobConf(WordCount.class);
        conf.setJobName("wordcount");

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

        conf.setMapperClass(Map.class);
        conf.setCombinerClass(Reduce.class);
        conf.setReducerClass(Reduce.class);

        conf.setInputFormat(TextInputFormat.class);
        conf.setOutputFormat(TextOutputFormat.class);

        FileInputFormat.setInputPaths(conf, new Path(args[0]));
        FileOutputFormat.setOutputPath(conf, new Path(args[1]));

        JobClient.runJob(conf);
    }
}

然後編譯

$ javac -classpath $HADOOP_CORE -d lib scr/WordCount.java

生成jarke執行程式

$ jar -cvf $HADOOPPATH/wordcount/bin/wordcount.jar -C lib/ .

然後在hdfs上存入input的檔案,我把spark的readme.md丟在input中,然後

$ hadoop fs -put input input

檢視下內容~~

$ hadoop fs -ls
$ hadoop fs -cat input/README.md

然後執行程式啦~

$ hadoop jar bin/wordcount.jar input output

輸出到本地~~

$ hadoop fs -get output output

結果可以看到了~~

python 介面測試

java這種煩死人的語言我不喜歡用,好在有python~~。 只是遺憾的是隻能使用Hadoop Streaming來實現mapreduce。

既然都用python了當然最好是用封裝好的第三方庫來寫了,我選的mrjob ,pip 安裝即可

同樣的寫個WordCount.py

from mrjob.job import MRJob
import string

class MRWordFrequencyCount(MRJob):

    def mapper(self, _, line):
        line = line.strip()
        delset = string.punctuation
        line = line.translate(None,delset)
        words = line.split()
        for word in words:
            yield word, 1

    def reducer(self, key, values):
        yield key, sum(values)


if __name__ == '__main__':
    MRWordFrequencyCount.run()

不用HDFS執行

python script/WordCount.py input/README.md -r hadoop >output/out.md

用HDFS

python script/WordCount.py hdfs:///user/huangsizhe/input -r hadoop > pyout.txt

似乎不能輸出到HDFS上不過也蠻好了

完全分散式安裝

由於沒有那麼多硬體,所以就用虛擬機器模擬完全分散式安裝了

結構設計

叢集需要多臺計算機同時運算,其中一臺master2臺slave。 節點間用區域網互聯,可以相互ping通,所以用靜態ip地址,基本環境如下表

虛擬機器系統 | 機器名稱 | ip地址 --- | --- | -- debain8 | Master | 192.168.1.140 debain8 | slave1 | 192.168.1.141 debain8 | slave2 | 192.168.1.142

java配置

單機版本已經配置過一回,這次不詳細寫,只寫基本步驟,這次為了模擬伺服器配置將只在terminal中進行

下載jdk

從官網下載,然後將其放入資料夾中jvm ~$ wget http://download.oracle.com/otn-pub/java/jdk/7u75-b13/jdk-7u75-linux-x64.tar.gz?AuthParam=1425543713_d268d467b9cdd038acdf7a1748834ea6 ~$ su /home/hsz# tar zxvf jdk-7u75-linux-x64.tar.gz -C /usr/lib/jvm

配置環境變數

修改/etc/profile檔案,新增JAVA_HOME,HADOOP_INSTALL,以及將對應的bin資料夾假如PATH。不重啟的話別忘記source下

網路設定

域名解析設定

從這邊開始就是與單機版不同的地方了~~

首先修改/etc/hostname,將debian修改掉。master主機修改為master,slave1為slave1,slave2為slave2.

然後修改/etc/hosts,其中將127.0.0.1 debian改為127.0.0.1 master改為127.0.0.1 slave1`這種

再在其中新增

192.168.1.140 master
192.168.1.141 slave1
192.168.1.142 slave2

terminal中設定本機ip地址

用ifconfig檢視本機ip

一般與希望的不同,這邊我們臨時修改ip

sudo ifconfig eth1 192.168.1.141

每臺機器都將ip地址改好。相互之間ping一下看看能不能連通

也可以在路由器端直接修改ip地址,可以檢視我的這篇文章

設定專用賬戶

為了好記好用,我們為三臺機器都設一個名為hduser的賬戶,該賬戶屬於一個叫hadoop的group

sudo addgroup hadoop    //設定hadoop使用者組
sudo adduser --ingroup hadoop hduser
sudo adduser hduser sudo
su - hduser    //進入hduser賬戶

ssh連通

三臺機器通訊需要使用ssh無密碼通訊

生成本機ssh密匙

首先為各自機器生成一對公鑰,私鑰

~$ ssh-keygen -t dsa -P "" //用dsa因為它比rsa速度快
~$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
~$ ssh localhost

第一次ssh localhost會要你設定下,以後就可以無密碼登陸了。每臺機器都這麼設定下,要求都能 ssh localhost無密碼登陸。

master與slave建立聯絡

將三臺機器上.ssh/資料夾下下載主機的公鑰

~$ ssh-copy-id hduser@slave1
~$ ssh-copy-id hduser@slave2

這就算配置好了