2012年8月31日 星期五

Eclipse開發MapReduce程式(1)



前言

MapReduce是用java寫的(不會寫java的可以用拉丁豬,but我不會拉丁豬),所以使用eclipse搭配hadoop-eclipse-plugin來開發Hadoop的MapReduce程式很方便,寫完後可以在eclipse中debug還可以直接看到程式產出的output,以下就是簡單的幾個範例。
雖然Hadoop框架是用java實作,MapReduce應用程式不一定要用java來寫,可以利用Hadoop streaming跟Hadoop pipes這兩個工具把其他語言套到MapReduce中。(依舊是那個but~~這兩個我都不會)
官網上入門的範例是wordcount,以下是Hadoop in action中的範例,書中的程式碼是0.20之前的寫法,以下我就把程式碼改寫成1.0.3的版本。以下是MapReduce的小小簡介
,再深入的底層原理我也不懂

凡治眾如治寡,分數是也。鬥眾如鬥寡,形名是也。《孫子》
MapReduce的原理對應到演算法就是"Divide and Conquer"(分治法)。

MapReduce採用"分而治之"的思想,把對大規模數據的操作,分發給一個主節點管理下的各分節點來共同完成,然後通過整合各分節點的中間結果,得到最終結果。簡單的說,MapReduce就是"任務的分解與結果的匯整"

上述的處理過程被MapReduce分為兩個函數,map跟reduece。map,負責把工作(job)分解成很多任務(task);reduce負責把分解後多任務(task)處理的結果匯總起來

將一個大問題,分割成許多小問題;將這些小問題解決之後,原本的大問題也就解決了。如果小問題還是很難,那就再切成更小的問題就行了。分割問題、各個擊破,這就是"Divide and Conquer"的精神。


網路上面很多高手寫的網誌很清楚的介紹MapReduece執行的過程, 以下這連結是我常常去看的, 推薦給各位
MapReduce介紹
這位網誌的主人還有寫了其他相關工具的介紹, 也是很不錯的, 有空可以去逛逛


環境

主戰機是windows7專業版64位元,vmplayer裝ubuntu12.04 桌面板 64位元。
在下大膽的假設看到這邊的人已經照著之前的網誌裝好環境了。
Eclipse是3.6 SR2。



工具包

載點 

以上連結中有一個cite75_99.zip下載後把裡面的txt檔放到你喜歡的目錄底下。這一個美國專利引用次數的紀錄檔,有16,500,000多筆資料,第一欄是專利代號,第二欄是第一欄的專利被哪一個專利引用過。
PS:先把第一行"CITING","CITED"刪掉吧,不然等一下第三個MapReduce程式會出錯,找出bug後會再補上去的。

實作
假設各看官都已經成功啟動Hadoop也照著之前的網誌裝好eclipse了。

[步驟一]

先建立一個HDFS的目錄,hadoop dfs -mkdir /hduser/cite/input,再把cite75_99.txt上傳到HDFS目錄中,
hadoop dfs -put hadoopbook/cite/data/cite75_99.txt /hduser/cite/input。(這邊有點久,要等一下,因為會切成4個block,每一個block會copy3份)。完成後確認一下檔案,hadoop fsck /hduser/cite/input/cite75_99.txt -files -blocks -locations



上傳好後到eclipse看看,檔案是不是放在你要的路徑中


[步驟二]

功力厲害的是可以用nano或是三小碗糕編輯器等等來寫程式,像我這種沒IDE就不會寫程式的就要借用eclipse了。
依照個人口味啦,我是習慣寫好MapReduce程式後沒有錯誤的紅叉叉,把java檔複製到我要的路徑下,再把.java編譯成jar檔。先建立一個HdPrj請見下圖。





按下右邊的"Configure Hadoop install directory"
進來後,填入你hadoop的安裝資料夾路徑,/home/pablo/hadoop-1.0.3
OK-->finish,這樣就好了。



接下來在HdPrj按右鍵,Build path-->Coufigure build path..-->頁籤切換到Libraries path



有三個東西要改,hadoop-ant-1.0.3.jar,hadoop-core-1.0.3.jar,hadoop-tools-1.0.3.jar的source attachment跟Javadoc location。從ant開始,剩下兩個依樣畫葫蘆,滑鼠點下Source attachment再點Edit。



按照以下的畫面修改



core跟tools的分別修改成(千萬不要照我的路徑寫,要按照自己的路徑><)
/home/pablo/hadoop-1.0.3/src/core
/home/pablo/hadoop-1.0.3/src/tools
以上三個的Javadoc location都是file:/home/pablo/hadoop-1.0.3/docs/api/


[步驟三]

稍微說明一下MapReduce架構。
MapReduce任务過程分成兩個處理階段:map 階段 和reduce階段。每個階段都以key/value對作為輸入及輸出,並由開發人員選擇他們的類型。開發人員還需要定義兩個class(書裡面寫函數):map和reduce。今天是2012/09/03話說Hadoop 2.0 Alpha已經發佈了。舊的api不知道還有沒有留著,我想應該是有啦,我是喜歡用新的寫法,以下是新舊(舊的是指0.2之前的)兩種不同的寫法比較。

舊API寫法:
public class YouClass extends Configured implements Tool {
   public static class MapClass extends MapReduceBase implements Mapper<Text, Text,Text, Text> {
public void map(Text key, Text, value,OutputCollector<Text, Text> output,Reporter reporter) throws IOException {
.............
  }
}

public static class Reduce extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
  public void reduce(Text key, Iterable value,OutputCollector<Text, Text> output,Reporter reporter)throws IOException {
.............
  }
}

public int run(String[] args) throws Exception {
 ...............
}

public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new YouClass(), args);
System.exit(res);
  }
}

新API寫法:
public class YouClass extends Configured implements Tool {
  public static class MapClass extends Mapper<Text, Text, Text, Text> {
  public void map( Text key, Text value, Context context) throws IOException, InterruptedException {
..........
  }
}

public static class Reduce extends Reducer<Text, Text, Text, Text> { 
  public void reduce( Text key, Iterable value, Context context) throws IOException, InterruptedException {
..........
  }
}

public int run(String[] args) throws Exception {
...............
}

public static void main(String[] args) throws Exception {
  int res = ToolRunner.run(new Configuration(), new YouClass(), args);
   System.exit(res);
  }
}

新舊API主要的差異有以下幾點:
1.新版的Map跟Reduce傾向於使用抽象類別,而不是舊版的interface。
2.新版的API是放在org.apache.hadoop.mapreduce中舊版的是放
在org.apache.hadoop.mapred中,撰寫時版本一定要統一。
3.新版的API用Context代替了舊版的OutputCollector跟Report。
輸出key/value時不再是outputCollector.collect()而是context.write()。
4.新版執行作業是用job舊版是用JobClient。
5.新版用Configuration來改變配置,舊版是用JobConf。
6.多了可以拋出InterruptedException而非單一的IOException。
7.reduce()方法中用Iterable代替Iterator,更容易使用foreach語法。
8.map產出的檔案名稱改成part-m-00000, reduce產出的檔案名稱改成part-r-00000(從零開始編號)
以上八點是我目前可以想到的,以後發現有別的差異再補上。
OK,現在就來開始新增一個class,MyJob。

import java.io.IOException;



import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.conf.Configured;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.LongWritable;

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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class MyJob extends Configured implements Tool{
public static class MapClass extends Mapper<LongWritable, Text, Text, Text> {
public void map (LongWritable key, Text value, Context context) throws IOException , InterruptedException {
String[] citation = value.toString().split(",");
context.write(new Text(citation[0]), new Text(citation[1]));
}
}
public static class ReduceClass extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
String csv = "";
for (Text val : values) {
if (csv.length() > 0) {
csv += ",";
}
csv += val.toString();
}
context.write(key, new Text(csv));
}
}
public int run(String [] args) throws IOException , InterruptedException, ClassNotFoundException {
Configuration conf = getConf();
Job job = new Job(conf , "MyJob");
job.setJarByClass(MyJob.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.setMapperClass(MapClass.class);
job.setReducerClass(ReduceClass.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);

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

System.exit(job.waitForCompletion(true) ? 0 : 1);
return 0;
}


public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new MyJob(), args);
System.exit(res);
}

}

[步驟四]

寫一個MapReduce有兩種方式來執行,第一:在文字檔中寫好編譯成jar再用hadoop jar來執行,第二:在eclipse中寫好後,args[0]跟args[1]寫死路徑然後run as-->run on hadoop。先從第二種講起比較簡單。
我們把以上的code複製貼到eclipse的MyJob.java中,args[0]跟args[1]分別改成"/hduser/cite/input"以及"/hduser/cite/output"。(這樣的做法是把參數hardcode了)
PS:args[0]是要分析的檔案資料輸入路徑,剛剛已經放了一個cite75_99.txt了。
args[1]是檔案分析完產生最後結果的檔案輸出路徑。切記不能先產生這路徑,不然會發生Exception。
args[0]跟args[1]是在terminal中指令傳入的兩個參數,在eclipse中可以這樣搞。



改完後執行run as -- > run on hadoop,選擇你的hadoop location,你就會看到console就開始跑了。會看到map從0%一直跑到100%,再接著reduce從0%一直跑到100%。完成後就會產生一個/hduser/cite/output資料夾裡面會有兩個檔案。見下圖。







你要在eclipse裡面打開part-r-00000來看結果也可以,到terminal中看也可以,我是用後者。hadoop dfs -cat /hduser/cite/output/part-r-00000輸出的檔案很長我就不執行截圖了,跑完天都黑了。


*****************************************************************
16,500,000筆資料在大象的世界裡面應該算"奈米級"的數量吧,不過令我很驚豔的是在VM中跑大象兩分鐘就統計完了,挺不賴的。遙想當年有位前輩舉了最生活化的例子就是統計投票數,每個投開票所統計完票數後(把data放在local端計算)再傳回給大選中心,這樣的計數效率是最好的。
*****************************************************************
以下我用terminal編譯來執行一次。我是用我自己的習慣來決定存放.java .class .jar的路徑,不一定要跟我一樣。
我把.java放在/home/pablo/hadoopbook/cite/java/MyJob/MyJob.java
編譯過的.class放在/home/pablo/hadoopbook/cite/class/MyJob/,包起來的.jar我放在hadoopJar中。以下每一行是一行指令,請記得把輸入輸出的路徑改回args[0]以及args[1]再把/hduser/cite/output這資料夾先刪除,不然看到以下的錯誤訊息。這是hadoop安全機制的一種。
Exception in thread "main" org.apache.Hadoop.mapred.FileAlreadyExistsException: Output directory output already exists
這目錄在執行工作前應該不存在的, 否則Hadoop將會回報錯誤以及拒絕執行工作, 這措施是為了預防資料漏失(當一個需要執行長時間的任務被另一個任務覆蓋是很討厭的事情)
hadoop dfs -rmr /hduser/cite/output
javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/cite/class/MyJob/ hadoopbooks/cite/java/MyJob/MyJob.java
jar -cvf hadoopJar/MyJob.jar -C hadoopbooks/cite/class/MyJob/ .
hadoop jar hadoopJar/MyJob.jar MyJob /hduser/cite/input /hduser/cite/output

結果會跟用ecplise跑出來一樣,terminal在跑的時候,也可以到web管理介面去看Map跟Reduce執行的結果跟進度。
http://namenode的ip:50030。(只有用terminal編譯執行jar檔web管理介面才有資訊可以看,用eclipse執行沒有)
(我的是http://10.0.3.1:50030)













這程式是統計每個專利有被那些專利引用,下面第二個程式是指顯示那些專利被引用過幾次,而不是列出一堆來。
新增一個class MyJob2.java

import java.io.IOException;



import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.conf.Configured;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.LongWritable;

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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class MyJob2 extends Configured implements Tool{
public static class MapClass extends Mapper<LongWritable, Text, Text, Text> {
public void map (LongWritable key, Text value, Context context) throws IOException , InterruptedException {
String[] citation = value.toString().split(",");
context.write(new Text(citation[0]), new Text(citation[1]));
}
}
public static class ReduceClass extends Reducer<Text, Text, Text, LongWritable> {
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
int count = 0 ;
for (Text val:values) {
count++;
}
context.write(key, new LongWritable(count));
}
}
public int run(String [] args) throws Exception {
Configuration conf = getConf();
Job job = new Job(conf , "MyJob2");
job.setJarByClass(MyJob2.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.setMapperClass(MapClass.class);
job.setReducerClass(ReduceClass.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);

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

System.exit(job.waitForCompletion(true) ? 0 : 1);
return 0;
}

public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new MyJob2(), args);
System.exit(res);
}

}

我就不用eclipse裡面執行了,我習慣在terminal中編譯執行。以下應該可以看出我的存放路徑XD。
javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/cite/class/MyJob2/ hadoopbooks/cite/java/MyJob2/MyJob2.java
jar -cvf hadoopJar/MyJob2.jar -C hadoopbooks/cite/class/MyJob2/ .
hadoop jar hadoopJar/MyJob2.jar MyJob2 /hduser/cite/input /hduser/cite/output1
過程跟結果就不多說了,自己執行就知道了。



有時不一定要對上傳的檔案做分析,也可以使用產出的檔案當作MapReduce的輸入檔案,以下的CitationHistogram.java就是一個例子。這程式是要把被引過的專利次數畫成一個XY軸的統計圖表,不過程式只有產出數字沒有產出圖表,因為我也不會XD,會的好心人來教教我吧。

新增一個CitationHistogram.java
import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
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.input.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class CitationHistogram extends Configured implements Tool {

public static class MapClass extends Mapper<Text, Text, IntWritable, IntWritable> {

private final static IntWritable uno = new IntWritable(1);
private IntWritable citationCount = new IntWritable();

@Override
public void map(Text key, Text value, Context context) throws IOException, InterruptedException {
if (!"".equals(value)) {
citationCount.set(Integer.parseInt(value.toString()));
context.write(citationCount, uno);
}
}
}

public static class ReduceClass extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable> {

@Override
public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException , InterruptedException {
int count = 0;
for (IntWritable val : values) {
count += val.get();
}

context.write(key, new IntWritable(count));
}
}

public int run(String [] args) throws Exception {
Configuration conf = getConf();
Job job = new Job(conf , "CitationHistogram");
job.setJarByClass(CitationHistogram.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[0]));
job.setMapperClass(MapClass.class);
job.setReducerClass(ReduceClass.class);
job.setInputFormatClass(KeyValueTextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);

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

System.exit(job.waitForCompletion(true) ? 0 : 1);
return 0;
}

public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new CitationHistogram(), args);
System.exit(res);
}

}

javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/cite/class/ CitationHistogram/ hadoopbooks/cite/java/ CitationHistogram / CitationHistogram .java
jar -cvf hadoopJar/ CitationHistogram .jar -C hadoopbooks/cite/class/ CitationHistogram / .
hadoop jar hadoopJar/ CitationHistogram .jar CitationHistogram /hduser/cite/output1 /hduser/cite/output2
程式指令都有了,自己執行吧。



三個都執行後你的HDFS目錄應該會長得像這樣子。
hadoop dfs -ls /hduser/cite



到http://10.0.3.1:50030去看,應該會有三個Completed Jobs(MyJob2我自己多做了一次)



以上,OVER。

2012年8月29日 星期三

HDFS簡單操作以及安裝Eclipse Hadoop Plugin

前言

這篇的HDFS簡單操作,簡單到只有"檔案上傳"跟"檢視上傳的檔案",其他的我也不太會。
想要把安裝Eclipse Hadoop Plugin跟這篇放一起是因為上傳檔案完後可以從eclipse裡面看到你的路徑還有檔案,也可以從eclipse裡面刪除,很方便。這篇的指令就不多解釋了,拜神可以查到一堆。這網站也不賴的


工具


https://docs.google.com/folder/d/0B1hKA25bLYJETVdveVItUklFLUE/edit
這裡面有hadoop-eclipse-plugin-1.0.3.jar,apat63_99.zip
hadoop-eclipse-plugin-1.0.3.jar直接丟到eclipse的plugins資料夾裡面。
apat63_99.zip解開後把txt檔找個地方放,我是放在/home/pablo/hadoopbook/apat/input/裡面,隨你放找得到就好。
至於hadoop-eclipse-plugin-1.0.3.jar怎麼complier出來的,我不會,我是google找到的。
apat63_99.txt是hadoop in action書中用的範例檔案,是專利明細檔。以hadoop來說這份拿來分析的檔案不算大,只是用來玩玩罷了。


環境


主戰機是windows7專業版64位元,vmplayer裝ubuntu12.04 桌面板 64位元。
已經照著之前的網誌裝好環境了。
Eclipse是3.6 SR2。


實作

[步驟一]

這裡我假設看到這段文字的人都已經成功啟動Hadoop了,並且把plugin丟進去了,開啟eclipse。

開啟eclipse之後,有些地方要調一下,跟著我做下去吧。

視窗切換到MapReduce。


切換後就畫面會向下圖那像,右下方會出現一隻藍色大象(New hadoop location...),請假裝沒看到最下方的那隻藍色大象,因為我之前已經產生過了。





點進來後出現的視窗,就照著下圖的去修改。(host跟user name要換成自己的)


修改完後,關掉eclipse再重開,我相信各位看官的eclipse左方(全部展開)以及下方會有跟下圖一樣的東西。


恭喜,你們已經踏進八奇的領域了!!



[步驟二]


回到主戰機(UD1204)的terminal,輸入hadoop dfs -ls /

會出現hadoop的檔案目錄,現在只有一個/home,不過我不愛用這個,我喜歡自己建另一個,請再輸入hadoop dfs -mkdir /hduser/apat/input,會看到的東西請見下圖。


see,我又多建了一個系統檔案的目錄了,下個步驟就把apat63_99.txt放到/hduser/apat/input裡面去,請輸入hadoop dfs -put hadoopbook/apat/data/apat63_99.txt /hduser/apat/input。

這樣就放上去囉,至於這檔案放到哪裡去?請下hadoop fsck /hduser/apat/input/apat63_99.txt -files -blocks -locations
預設是64MB切成一個block,且備份3份,從上圖可以看的出來每一個block放在哪一些datanode中。

再回到eclipse看看,把左邊的DFS Locations展開,你會看到你剛剛建立的路徑以及放上去的東西。




以上,OVER。

2012年8月28日 星期二

Linux Containers建置Hadoop完全分散環境



前言


話說有某人筆電是裝硬碟,一顆win7一顆ubuntu12.04,上班環境是使用win7來開發的,想要摸魚玩玩hadoop還要切換到另一顆,這樣真的很不方便。再說原本是使用linux kvm來安裝hadoop環境的,在vm player裡不能使用kvm(天要亡某人啊!!),但是天無絕人之路,某年某天就在那個時候的這個時間某人熊熊學到了LXC技術,彷若絕地重生XD,讓某人上班時有個惡搞的玩具了。以下就來簡短說明一下那位某人的新玩具。


2014/03/16更新:
如果想要在vmware station中使用kvm的話,可以在settings中的hardware-->Processors選項中把VT-x功能打開,如下圖:

如此一來就可以使用kvm或libvirt等等的功能了,不過目前筆者的最愛還是Linux Container....XD!

**********************************************************************************************
LXC全名是Linux Containers,是由IBM開發的,類似 linux vserver, openvz 的機制。簡單的說就是在同一個kernel底下可以執行多個OS(也可說是
不同的 filesystem,但共用同一個 kernel)。
LXC不但有不同的 filesystem,還能夠設定不同的網路,就好像是另一台虛擬機器,但是不會像 kvm 或 xen 那樣耗費那麼多的資源。LXC 可限制用多少 memory、cpu 等等,才不會讓某一個 container 影響到另一個 container 或甚至是 host,這都是 chroot 做不到的。
**********************************************************************************************

****中的文字來自於陳松林老師的講義



環境

主戰機是windows7專業版64位元,vmplayer裝ubuntu12.04 桌面板 64位元。


工具

按我下載shell這裡面有個iLXC.rar,就解壓縮後直接放到家目錄下面,千萬不要放到其他地方,不然startLxc.sh會找不到路徑的。


實作

[步驟一]

首先開啓自己的terminal跟著以下步驟做:
安裝lxc套件sudo apt-get install lxc執行後就等等他吧,看到yes/no就回答yes.....無腦的一鍵安裝


裝完lxc後,請ifconfig -a這時你會發現出現了一個lxcbr0(這是lxc預設的bridge,你不爽用也可以視而不見)。對,這就是等下namenode要用的ip位置,很瞎吧。本來我自己做了GW,啟動hadoop後datanode都無法正常啟動,當我把namenode的ip改成10.0.3.1後居然啥都正常了。以我目前的斤兩,我無法解釋為何這樣可以通,管他的讓我們繼續搞下去吧。



因為會用bash script來讀取xml,所以要安裝解析XML的套件xpath指令是
sudo apt-get install uml-utilities libxml2-utils libxml-xpath-perl大膽地給他裝下去吧,看到yes/no就回答yes.....這也是無腦的一鍵安裝。

不過各位看官不會直接去下指令解析xml,因為我都寫在bash script裡面了,最後每一個os分身都建立好後,會用到自動化啟動分身的bash script。



[步驟二]

來建立第一個LXC,這一個要當作之後的clone的對象,所以一堆東西都要在這一個裡面都設定好,之後再clone出去的分身就不用再大費周章的去改設定了。sudo lxc-create -t ubuntu -n myUD1204(-t 代表 type, -n 代表 name)
裝完應該會有這樣的畫面出來,default的帳密都是ubuntu/ubuntu。



















myUD1204產生完後可以到/var/lib/lxc這目錄去看看,所有產生的"分身"都會以資料夾的形式存在這邊。每個資料夾裡面會有三個檔案(config,fstab,rootfs.hold)一個資料夾(rootfs)。這篇網誌的實作主要會動到的是rootfs這資料夾以及config這檔案。

[步驟三]

因為myUD1204這一個是要拿來當每個分身的"媽媽",基於方便操作,我們要先把會用到的資料或檔案放到myUD1204裡面,再事先編輯好每個設定檔還有使用者帳密。就是底下幾件事要做:

*新增使用者(預設是ubuntu,要新增一個你自己的帳密)
*安裝jdk(hadoop是要吃JDK的)
*把hadoop安裝檔放進去
*網路設定(編輯/etc/var/lib/${yourLXC}/config)

1.新增使用者


先使用背景啟動myUD1204,用ubuntu/ubuntu登入來新增使用者(用個你喜歡的帳號吧)
sudo lxc-start -n myUD1204 -d(-d是背景作業)
sudo lxc-console -n myUD1204





因為一個沒有裝啥套件的乾淨OS,你需要什麼套件要自己裝,等一下要編輯使用者權限所以需要nano,sudo apt-get install nano。裝好後來新增帳號,指令是sudo useradd -m -s /bin/bash XXXX跟sudo passwd XXXX連續兩次輸入你要給這新帳號啥密碼。下圖是我示範新增一個名叫"hduser"的帳號。



再來讓新的帳號有sudo的權限,sudo nano /etc/sudoers
按照下圖的方式來編輯。


登出(exit),再用你建立的新帳密登入。之後clone的分身全部可以用這一個帳號了。

2.安裝jdk以及編寫JAVA_HOME


假設你的家目錄(我的是UD1204這一台)已經有jdk-6u34-linux-x64.bin跟hadoop-1.0.3.tar.gz,我們就直接把這兩個cp過去給myUD1204,指令如下:
(pablo是我的帳號,你要改成你自己的帳號)
sudo cp jdk-6u34-linux-x64.bin /var/lib/lxc/myUD1204/rootfs/home/pablo
sudo cp hadoop-1.0.3.tar.gz /var/lib/lxc/myUD1204/rootfs/home/pablo
 複製完後到myUD1024(用你的帳號登入)執行以下兩個指令./jdk-6u34-linux-x64.bin跟tar vxfz hadoop-1.0.3.tar.gz
完成後你的myUD1204家目錄的目錄結構應該像下圖一樣,



再來編輯.bashrc,加入JAVA_HOME以及PATH
nano .bashrc,輸入以下四行,
export JAVA_HOME=/home/pablo/jdk1.6.0_34
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH="$PATH:$JAVA_HOME/bin:/home/pablo/hadoop-1.0.3/bin:$JRE_HOME/bin"



完成後登出(exit)再登入(你的新帳號),確認一下java以及hadoop是否正確。

3.編輯Hadoop設定檔

請見之前的網誌有寫到,請參照。
UD1204是namenode,jobtracker,secondarynamenode
LXC251,LXC252,LXC253是datanodes,tasktrackers

4.網路設定


再來設定/etc/hosts跟/etc/network/interfaces
從下載下來的檔案(iLXC)中的conf/lxc.xml可以看到我會啟動四台分身,以及產生的bridge ip是給192.168.200.254(順便當GW)所以/etc/hosts跟/etc/network/interfaces要先編輯成下圖的內容,





[步驟四]

這步驟要產生另外四個分身,並且分別對其細節再修改一下。
回到主戰機中(是在vmplayer中的那一台呦,不要亂了,名字千萬要跟我的一樣,因為我在xml設定檔中就是用這幾個名字,看不順眼的也是可以自己改啦),
sudo lxc-clone -o myUD1204 -n LXC251
sudo lxc-clone -o myUD1204 -n LXC252
sudo lxc-clone -o myUD1204 -n LXC253
(-o 代表 oringinal)

都產生完後/var/lib/lxc裡面會產生這四個分身的資料夾,我們接下來要分別來編輯這四個的網路設定(因為是用myUD1204)
在主戰機的終端機中,
cd /var/lib/lxc/LXC251
sudo nano config
要修改的地方如下:
lxc.network.link=lxc200(這是我設定檔中產生的bridge)
lxc.network.hwaddr= 00:16:92:68:20:51(mac address)

ps:lxc.network.type=veth,veth代表是虛擬網卡改成phys表示獨佔實體網路卡,這時的lxc.network.hwaddr要mark起來


按照這樣的方法依序把LXC252,LXC253也修改好。
每一台的lxc.network.link都是lxc200
mac address只有最後一個不一樣,要改52,53或是自己亂改都可以不要重複就好。我自己的是00:16:92:68:20:52 00:16:92:68:20:53。以上完成後,就需要用到iLXC的資料夾了,請確定這資料夾在家目錄,並按照下圖指令操作。使用bash script讓四台分身一起同時啟動。
這時你也可以用brctl show來看看lxc200這一個虛擬bridge上面有插著幾片網卡。




上圖可以看到有個lxcbr0,這是安裝lxc後原生的bridge,lxc200是我自己產生的,並在每一個分身的資料夾裡的config中設定網卡要插在哪一個bridge中,這就是為啥要修改lxc.network.link=lxc200的原因了。
最後要到每一台的/etc/network/interfaces修改ip,例如我要進到LXC251這台的話就是在開一個終端機,sudo lxc-console -n LXC251就可以了,因為我在startLxc.sh都是啟動後丟背景作業,記得要用新增的帳號登入。
假設進到了LXC251這台後,來修改ip吧,sudo nano /etc/network/interfaces


依LXC251,LXC252,LXC253的順序把address修改成192.168.200.51 192.168.200.52 192.168.200.53。
每一台的/etc/hosts也要修改,請見下圖。
127.0.0.1 localhost
10.0.3.1 UD1204~~~~不要忘了這很瞎的地方
192.168.200.51 LXC251
192.168.200.52 LXC252
192.168.200.53 LXC253



改完後,把每一台都關機再開一次機(這是我的習慣啦,有些人是登出再登入就好)。
在主戰機中分別下sudo lxc-stop -n LXC251,sudo lxc-stop -n LXC252,sudo lxc-stop -n LXC253這三台就關機了。再用我的bash script把每一台都啟動。
最後測通一下這虛擬網路,就互ping這四台分身吧,應該很簡單,這邊我就不做了。例如在LXC251這一台分身中做,ping UD1204 ping LXC252 ping LXC253

[步驟五]

ssh自動登入。
UD1204是namenode所以要在UD1204產生憑證再傳到LXC251,LXC252,LXC253中。
請記得每台的家目錄底下要自己mkidr .ssh,不然會提示說找不到資料夾。
在UD1204的終端機中做以下的指令,
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys



用ssh UD1204測試看看,是否可以不用密碼登入。
成功的話就把key傳到另外三台datanode中,請見下圖。
scp ~/.ssh/id_dsa.pub LXC251:.ssh/authorized_keys
scp ~/.ssh/id_dsa.pub LXC252:.ssh/authorized_keys
scp ~/.ssh/id_dsa.pub LXC253:.ssh/authorized_keys



做完後一定要確認是否從UD1204登入到datanode中都是不用輸入密碼的,因為這關係到hadoop的啟動是否成功。
ssh LXC251
ssh LXC252
ssh LXC253
(跳出來就用exit)

[步驟六]

網路測通後,就是要來啟動hadoop了。在之前的網誌有提過了,這邊不說太多了。
1.hadoop namenode -format



2.start-all.sh(前一篇有說過啟動方式有三種)



3.jps
namenode:


datanode:



4.hadoop dfsadmin -report



看到了吧,三台datanode都乖乖地出現了XD

結語

這下子我在win7的環境中可以開啟vmplayer,不用kvm被限制住,也可以有分散式環境來玩Hadoop。這篇用LXC來建構Hadoop環境並沒有去限制datanode的ram跟cpu,話說沒有限制的話,會搶資源喲!!有興趣的人可以拜神一下。我個人覺得這不是一個很標準的虛擬網路環境,覺得有問題的人請不要鞭我XD,這只是用來玩玩Hadoop的。
還是老話一句,這兩篇建構環境的網誌並沒有提到HDFS的操作跟Ecplise開發MapReduce,我有空再來補上去吧。

這篇是某某人花很多時間,死很多細胞,掉很多頭髮弄出來的,要引用的人請佛心來著的告知我一下啊><