Hadoop Map/Reduce チュートリアル

Hadoop Map/Reduce チュートリアル
翻訳: 藤田昭人
IIJ Innovation Institute
2008 年 12 月 10 日
This translation in Japanese is the derivative work of “Hadoop Map/Reduce Tutorial” documentation,
whose copyright owner is The Apache Software Foundation.
Copyright (c) 2008 IIJ Innovation Institute Inc.
Licensed under the Apache License, Version 2.0 (the ”License”); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an ”AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions and limitations
under the License.
Copyright (c) 2008 (株) IIJ イノベーションインスティテュート
この翻訳は Apache License Version 2.0(「本ライセンス」)に基づいてライセンスされます。あなたがこの
ファイルを使用するためには、本ライセンスに従わなければなりません。本ライセンスのコピーは下記の場所
から入手できます。
http://www.apache.org/licenses/LICENSE-2.0
適用される法律または書面での同意によって命じられない限り、本ライセンスに基づいて頒布されるソフト
ウェアは、明示黙示を問わず、いかなる保証も条件もなしに「現状のまま」頒布されます。本ライセンスでの
権利と制限を規定した文言については、本ライセンスを参照してください。
1
1 目的
このドキュメントは Map/Reduce フレームワークとサービスのユーザー側面でのすべてについてチュートリ
アルとして総合的に解説します
2 準備
Hadoop のインストールと設定、そして動作していることを確認してください。詳細については次を参照して
ください:
• Hadoop Quickstart [?] for first-time users.
• Hadoop Cluster Setup [?] for large, distributed clusters.
3 概要
Hadoop Map/Reduce はコモディティ・ハードウェアから成る大規模なクラスタ(数千ノード)において、信
頼性や耐障害性を確保しつつ膨大な量のデータ(数テラ・バイトのデータ・セット)を並列的に処理するアプ
リケーションを容易に作成するためのソフトウェア・フレームワークです。
通常 Map/Reduce のジョブは入力データ・セットを依存性のないチャンクに分割し、完全に並列的に実行さ
れる map タスクによって処理されます。map の出力はフレームワークにソートされた後 reduce タスクの入
力となります。一般にジョブの入力と出力はいずれもファイルシステムに格納されます。フレームワークはタ
スクのスケジューリングとモニタリング、失敗したタスクの再実行を対処します。
一般的には計算ノードとストレージ・ノードは同一、すなわち、Map/Reduce フレームワークと分散ファイル
システムは同一のノード・セットで稼動しています。このコンフィグレーションはフレームワークが既にデー
タが存在するノードにタスクを効率よくスケジュールすることを可能にし、クラスタ全体で非常に高い帯域幅
を達成する結果となります。
Map/Reduce のフレームワークは単独マスターの JobTracker とクラスターのノードごとに1つずつ存在す
るスレーブの TaskTracker から構成されます。マスターはスレーブで実行されるジョブのコンポーネント・
タスクのスケジューリングとモニタリング、失敗したタスクの再実行の責任を負います。スレーブはマスター
から指示されたタスクを実行します。
アプリケーションは最低限でも、入力と出力の配置を指定し、適切なインターフェースやアブストラクショ
ン・クラスにより実装した map、reduce 関数を提供します。これらに加え、その他のジョブ・パラメーター
はジョブ・コンフィグレーションを含みます。そして Hadoop のジョブ・クライアントは、ジョブ(jar や実
行形式ファイルなど)と JobTracker へのコンフィグレーションのサブミットを行います。このコンフィグ
レーションはその後、スレーブへのソフトウェアやコンフィグレーションの配布、タスクのスケジューリング
とモニタリング、ジョブ・クライアントへのステータスや診断の情報の提供を引き受けます。
Hadoop のフレームワークは JavaTM で実装されていますが、Map/Reduce アプリケーションは Java で書
かれている必要はありません。
• Hadoop Streaming はユーザーが mapper と reducer として任意の実行形式(例えばシェル・スクリ
プト)での作成と実行を可能にするユーティリティです。
• Hadoop Pipes は Map/Reduce アプリケーションを実装するための(JNITM に基づかない)SWIG 互
換の C++ API です。
2
4 入力と出力
Map/Reduce フレームワークは<key, value>ペアを排他的に操作します。すなわち、フレームワークはジョ
ブの入力を<key, value>ペアのセットと見なし、ジョブの出力として異なるタイプが考えられる<key, value>
セットを生成します。
key と value のクラスはフレームワークによってシリアライズすることができなければならず、したがって
Writable インターフェースを実装する必要があります。さらに key クラスはフレームワークによるソートを
容易にするため、WritableComparable インターフェースを実装しなければなりません。
Map/Reduce ジョブの入出力タイプ次の通りです:
( i n p u t ) <k1 , v1>−> map −> <k2 , v2>−> combine −> <k2 , v2>−> r e d u c e −> <k3 , v3 >( ou tp ut )
5 事例: WordCount v1.0
詳細に移る前にその動き方の感触を掴むため Map/Reduce アプリケーションの事例をソース解析してみま
しょう。
WordCount は指定された一連の入力で出現する個々の単語の総数をカウントする単純なアプリケーション
です。
これは Hadoop インストレーションの local-standalone、pseudo-distributed、fully-distributed
[?] において動作します。
5.1 ソースコード
WordCount.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package o r g . myorg ;
import j a v a . i o . IOException ;
import j a v a . u t i l . ∗ ;
import
import
import
import
import
o r g . apache . hadoop . f s . Path ;
o r g . apache . hadoop . c o n f . ∗ ;
o r g . apache . hadoop . i o . ∗ ;
o r g . apache . hadoop . mapred . ∗ ;
o r g . apache . hadoop . u t i l . ∗ ;
public c l a s s WordCount {
public s t a t i c c l a s s Map extends MapReduceBase
implements Mapper<LongWritable , Text , Text , I n t W r i t a b l e > {
private f i n a l s t a t i c I n t W r i t a b l e one = new I n t W r i t a b l e ( 1 ) ;
private Text word = new Text ( ) ;
public void map( LongWritable key , Text v a l u e ,
O u t p u t C o l l e c t o r <Text , I n t W r i t a b l e > output ,
R e p o r t e r r e p o r t e r ) throws IOException {
String l i n e = value . toString ( ) ;
S t r i n g T o k e n i z e r t o k e n i z e r = new S t r i n g T o k e n i z e r ( l i n e ) ;
while ( t o k e n i z e r . hasMoreTokens ( ) ) {
word . s e t ( t o k e n i z e r . nextToken ( ) ) ;
output . c o l l e c t ( word , one ) ;
3
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
}
}
}
public s t a t i c c l a s s Reduce extends MapReduceBase
implements Reducer<Text , I n t W r i t a b l e , Text , I n t W r i t a b l e > {
public void r e d u c e ( Text key , I t e r a t o r <I n t W r i t a b l e > v a l u e s ,
O u t p u t C o l l e c t o r <Text , I n t W r i t a b l e > output ,
R e p o r t e r r e p o r t e r ) throws IOException {
in t sum = 0 ;
while ( v a l u e s . hasNext ( ) ) {
sum += v a l u e s . ne xt ( ) . g e t ( ) ;
}
output . c o l l e c t ( key , new I n t W r i t a b l e ( sum ) ) ;
}
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n {
JobConf c o n f = new JobConf ( WordCount . c l a s s ) ;
c o n f . setJobName ( " wordcount " ) ;
c o n f . setOutputKeyClass ( Text . c l a s s ) ;
conf . setOutputValueClass ( IntWritable . class ) ;
c o n f . s e t M a p p e r C l a s s (Map . c l a s s ) ;
c o n f . s e t C o m b i n e r C l a s s ( Reduce . c l a s s ) ;
c o n f . s e t R e d u c e r C l a s s ( Reduce . c l a s s ) ;
c o n f . s e t I n p u t F o r m a t ( TextInputFormat . c l a s s ) ;
c o n f . setOutputFormat ( TextOutputFormat . c l a s s ) ;
F i l e I n p u t F o r m a t . s e t I n p u t P a t h s ( c o n f , new Path ( a r g s [ 0 ] ) ) ;
FileOutputFormat . setOutputPath ( c o n f , new Path ( a r g s [ 1 ] ) ) ;
J o b C l i e n t . runJob ( c o n f ) ;
}
}
5.2 利用法
WordCount.java をコンパイルし jar を作成します。但し HADOOP HOME をインストレーションのルート、
HADOOP VERSION をインストールされた Hadoop のバージョンと仮定します。
$ mkdir w o r d c o u n t c l a s s e s
$ j a v a c −c l a s s p a t h $ {HADOOP HOME}/ hadoop−$ {HADOOP VERSION}− c o r e . j a r
−d w o r d c o u n t c l a s s e s WordCount . j a v a
$ j a r −c v f / u s r / j o e / wordcount . j a r −C w o r d c o u n t c l a s s e s / .
$
入出力については次のように仮定します:
• /usr/joe/wordcount/input –HDFS の上の入力ディレクトリ
• /usr/joe/wordcount/output –HDFS の上の出力ディレクトリ
入力のサンプル・テキストファイルは:
$ b i n / hadoop d f s − l s / u s r / j o e / wordcount / i n p u t /
/ u s r / j o e / wordcount / i n p u t / f i l e 0 1
4
/ u s r / j o e / wordcount / i n p u t / f i l e 0 2
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / i n p u t / f i l e 0 1
H e l l o World Bye World
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / i n p u t / f i l e 0 2
H e l l o Hadoop Goodbye Hadoop
アプリケーションを実行します:
$ b i n / hadoop j a r / u s r / j o e / wordcount . j a r \
o r g . myorg . WordCount / u s r / j o e / wordcount / i n p u t / u s r / j o e / wordcount / output
出力は:
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / output / part −00000
Bye 1
Goodbye 1
Hadoop 2
Hello 2
World 2
アプリケーションはオプション-files を使って、タスクのカレント・ワーキング・ディレクトリを表現する
コンマで区切られたパス・リストを指定することができます。オプション-libjars は、アプリケーションが
map と reduce のクラスパスに jar を追加することを可能にします。-archives はアーカイブを引数として
引き渡すことを可能にします。このアーカイブは、タスクのカレント・ワーク・ディレクトリで作成されるも
ので、unzipp/unjar され jar/zip の名前にリンクされます。コマンド・ラインに関するより詳細は Commands
manual [1] で説明しています。
-libjars と-files を使った wordcount の事例は次のように実行されます。
$ hadoop j a r hadoop−examples . j a r wordcount
− f i l e s c a c h e f i l e . t x t − l i b j a r s mylib . j a r i n p u t output
5.3 ソース解析
アプリケーションは WordCount は非常にストレートです。
Mapper 実装 (14-29 行) はメソッド map (19-29 行) により指定された TextInputFormat (55 行) により提供
された入力を1回に1行ずつ処理をします。行は StringTokenizer により空白で区切られたトークンに分割
し、<<word>, 1>という key-value ペアを発行します。
与えられたサンプル入力に対して1番目の map は次のとおり発行します:
<Hello, 1>
<World, 1>
<Bye, 1>
<World, 1>
2番目の map は次のとおり発行します:
<Hello, 1>
<Hadoop, 1>
<Goodbye, 1>
<Hadoop, 1>
ジョブにより起動された map の総数やそれらのきめ細かい制御の方法に関する詳細はチュートリアルで後ほ
ど説明します。
WordCount では combiner(52 行) も指定されています。したがって、ここの map の出力は、keys でソー
トされた後にローカルで集約するため(Reducer と動揺にジョブ・コンフィグレーション毎に)ローカルの
combiner を経由します。
5
1番目の map の出力は次のようになります:
<Bye, 1>
<Hello, 1>
<World, 2>
2番目の map の出力は次のようになります:
<Goodbye, 1>
<Hadoop, 2>
<Hello, 1>
メソッド reduce(33-41) による Reducer の実装 (31-42 行) は個々の key (この例では単語)ごとの出現回数
である値を合計しているだけです。
これによりジョブの出力は次のようになります:
<Bye, 1>
<Goodbye, 1>
<Hadoop, 2>
<Hello, 2>
<World, 2>
メソッド run は、
(コマンド・ラインより引き渡される)入出力のパス、JobConf では key/value のタイプや
入出力のフォーマットなど、ジョブのさまざまな事柄について指定します。その後、ジョブを起動しその過程
をモニターするため、JobClient.runJob(61 行) をコールします。
JobConf、JobClient、Tool、さらにその他のインターフェースやクラスに関する詳細はチュートリアルで後
ほど説明します。
6 Map/Reduce – ユーザー・インターフェース
このセクションではユーザーの側面からみた Map/Reduce フレームワークの詳細についてまとめて情報を提
供します。このドキュメントはユーザーの実装やコンフィグレーション、ジョブのチューニングについて丁寧
なやり方で支援するべきでしょう。しかしながら、利用可能な最も包括的なドキュメンテーションとして個々
のクラスやインターフェースの javadoc が存在することに注意してください; これはチュートリアルでしかな
いと意味づけられています。
まず最初に Mapper と Reducer のインターフェースを取り上げましょう。通常、アプリケーションは map メ
ソッドと reduce メソッドを提供するために、それらを実装します。
次に JobConf、JobClient、Partitioner、OutputCollector、Reporter、InputFormat、OutputFormat
などのその他のコアなインターフェースについて議論します。
最後に DistributedCache や IsolationRunner といった幾つかのフレームワークの便利な機能に関する議
論を要約します。
6.1 Payload
通常、アプリケーションは map メソッドと reduce メソッドを提供するために Mapper と Reducer のイン
ターフェースを実装します。これらはジョブの中核を形成します。
6.1.1 Mapper
Mapper は入力の key/value ペアを一連の中間的な key/value ペアにマップします。
6
Map は入力レコードを中間レコードに変換する独立したタスクです。変換された中間レコードは入力レコー
ドと同じタイプである必要がありません。与えられた入力ペアは0、あるいは多くの出力ペアにマップされる
でしょう。
Hadoop の Map/Reduce フレームワークはジョブの InputFormat により生成される InputSplit 毎に1つ
の map タスクを生成します。
全体として Mapper の実装はメソッド JobConfigurable.configure(JobConf) を介してジョブの JobConf
を処理して、初期化されたそれら自身をオーバーライドします。次にフレームワークは、そのタスクの
InputSplit において key/value ペア毎に map(WritableComparable, Writable, OutputCollector,
Reporter) をコールします。そしてアプリケーションは、求められる全てのクリーンアップを実行するため、
Closeable.close() をオーバーライドすることができます。
出力ペアは入力ペアと同じタイプである必要がありません。与えられた入力ペアは出力が全くないことも多く
のペアを出力することも起こり得ます。OutputCollector.collect(WritableComparable,Writable) を
コールすることにより、出力ペアは受け取られます。
アプリケーションは Reporter を使って、進捗の報告、アプリケーション・レベルのステータス・メッセージ
の設定、カウンターの更新を行うことができますが、単に稼動していることを示すだけでも構いません。
与えられた出力キーに割り当てられた中間値はすべて、引き続きフレームワークによってグループ化され、最終
的な出力を決定するために Reducer(s) に渡されます。JobConf.setOutputKeyComparatorClass(Class)
を介して Comparator を指定することにより、ユーザはグルーピングをコントロールすることができます。
Mapper の出力はソートされ Reducer 毎に分割されます。分割される総数はジョブの reduce タスクの総数に
一致します。ユーザはカスタムな Partitioner を実装することにより、どのキー(すなわちレコード)がど
の Reducer に渡るのかをコントロールすることができます。
オプションとしてユーザは JobConf.setCombinerClass(Class) を介して combiner を指定し、中間出力
のローカルの集合を実行することができます。これは Mapper から Reducer に転送される大量のデータを削
減するために役立ちます。
ソートされた中間出力は常に SequenceFile フォーマットのファイルに格納されます。JobConf を介して
CompressionCodec を使用すると、アプリケーションは中間出力の圧縮をコントロールすることができます。
• Map の総数は?
map の総数は通常入力の合計サイズ、すなわち、入力ファイルのブロック総数によって決定されます。
非常にCPU負荷の小さい map タスクでは 300 map が設定されますが、map の並列化の適正なレベルは1
ノードあたり 10-100 map あたりに見えます。タスクの設定にはしばらくの時間がかかりますので、map の
実行には少なくとも1秒は掛かるケースがベストです。
したがってブロックサイズが12bMBの10TBの入力データを要求した場合、setNumMapTasks(int) を
使ってそれより高く設定しなければ、最終的に 82,000 map になります。
6.1.2 Reducer
Reducer はキーを共有する一連の中間値をより小さな値のセットに縮小します。
ジョブの reduce 総数は JobConf.setNumReduceTasks(int) を介してユーザーにより設定されます。
Reducer の実装は全体としてメソッド JobConfigurable.configure(JobConf) を介してジョブの JobConf
を処理して、初期化されたそれら自身をオーバーライドします。次にフレームワークは、グループ化さ
れた入力の<key, (list of values) > ペア毎にメソッド reduce(WritableComparable, Iterator,
OutputCollector, Reporter) をコールします。そしてアプリケーションは、求められる全てのクリーン
アップを実行するため、Closeable.close() をオーバーライドすることができます。
Reducer は shuffle、sort、reduce の3つのプライマリ・フェーズを持ちます。
• Shuffle
Reducer への入力はソートされた Mapper の出力です。フレームワークはこのフェーズでは、HTTP を介し
7
て全ての Mapper の出力の適切な分割をフェッチします。
• Sort
このステージでフレームワークは Reducer の入力をキーに基づいてグループ化します。(異なる Mapper が
同じキーをの出力を持っている可能性があります)
shuffle と sort のフェーズは同時に発生します; map の出力はフェッチされながらマージされます。
• Secondary Sort
reduce 処理の前に中間キーをグループ化するための同等のルールが(オリジナルの)キーをグループ化す
るものとは異なることを求められる場合、JobConf.setOutputValueGroupingComparator(Class) 介し
て Comparator を指定する可能性があります。中間キーがグループ化される方法をコントロールするために
JobConf.setOutputKeyComparatorClass(Class) を使用することがあるので、値の2回目のソートをシ
ミュレートするための結合においてこれらは使用される可能性があります。
• Reduce
こ の フ ェ ー ズ で は 、グ ル ー プ 化 さ れ た 入 力 の<key, (list of values)> ペ ア 毎 に メ ソ ッ ド
reduce(WritableComparable, Iterator, OutputCollector, Reporter) がコールされます。
通常 reduce タスクの出力は、OutputCollector.collect(WritableComparable, Writable) を介して
FileSystem にライトされます。
アプリケーションは Reporter を使って、進捗の報告、アプリケーション・レベルのステータス・メッセージ
の設定、Counters の更新を行うことができますが、単に稼動していることを示すだけでも構いません。
Reducer の出力はソートされません。
• Reduce の総数は?
reduce の正確な総数は概ね (ノード総数 * mapred.tasktracker.reduce.tasks.maximum) の 0.95 倍ある
いは 1.75 倍になります。
0.95 倍の場合、全ての reduce は map が終了すると直ちに起動し、map の出力の転送を開始することができ
ます。1.75 倍の場合、ジョブの負荷分散をもっと良い状態にするためより高速なノードは reduce の最初のラ
ウンドを終了し、reduce の第2波を起動するでしょう。
reduce の総数の増加はフレームワークのオーバーヘッドを増大させますが、負荷分散を増加させ、障害コス
トを低下させます。
フレームワークが不完全なタスクや障害停止したタスク用に少数の reduce スロットをリザーブするため、上
記のスケーリング・ファクタは総数よりも若干少なくなります。
• Reducer NONE
reduce 処理が必要でない場合、reduce タスクの数を0に設定することは有効です。
この場合 map タスクの出力は直接 FileSystem の setOutputPath(Path) で設定した出力パスに格納され
ます。フレームワークは map 出力を FileSystem にライトする前にソートすることはありません。
6.1.3 Partitioner
Partitioner はキー・スペースを分割します。
Partitioner は中間的な map 出力のキーの分割をコントロールします。キー(あるいはその一部)は分割を
取り出すため、通常はハッシュ関数によって利用されます。分割の総数はジョブの reduce タスクの総数と同
じです。したがって、中間キー(すなわちレコード)の m の reduce タスクが reduce 処理のためにいずれに
送られるかをこれがコントロールします。
HashPartitioner が Partitioner のデフォルトです。
8
6.1.4 Reporter
Reporter は Map/Reduce アプリケーションの機能で、進捗の報告、アプリケーション・レベルのステータ
ス・メッセージの設定、Counters の更新を行います。
Mapper と Reducer の実装は進捗の報告、あるいは単に稼動していることを示すだけのために Reporter を
利用することができます。個々の key/value ペアを処理するためにアプリケーションはどこに多くの時間を
費やしているか?というシナリオでは、フレームワークはタスクがタイムアウトしたり、タスクをキルされる
ことを仮定できるので、これが鍵を握ります。これを回避するもう1つの方法はコンフィグレーション・パラ
メータの mapred.task.timeout に十分に大きな値を設定することです。(あるいはタイムアウトを無効にす
るため0を設定しても構いません)
さらにアプリケーションは Reporter を使用して、Counters を更新することができます。
6.1.5 OutputCollector
OutputCollector は Map/Reduce フレームワークによって提供される Mapper や Reducer のデータ出力
(中間出力とジョブ出力の両方)を集めるための一般化された機能です。
Hadoop の Map/Reduce には役に立つ一般的な mappers、reducers、partitioners のライブラリがバンドル
されています。
6.2 ジョブ・コンフィグレーション
JobConf は Map/Reduce のジョブ・コンフィグレーションを表現します。
JobConf は Hadoop フレームワークに実行させる Map/Reduce ジョブをユーザが記述するための主要なイ
ンターフェースです。フレームワークは JobConf に記述されているどおりにジョブを誠実に実行しようとし
ます。しかしながら:
• コンフィグレーション・パラメータの幾つかは管理者によって決定値としてマークされ、それ故に変更
できません。
• いくつかのジョブ・パラメーター(例えば setNumReduceTasks(int))は直接設定されるのに対し、他
のパラメーター(例えば setNumMapTasks(int))は残りのフレームワークやジョブ・コンフィグレー
ションと微妙に関連し、設定はより複雑です。
通常 JobConf は Mapper、combiner(存在する場合)
、Partitioner、Reducer、InputFormat、OutputFormat
の実装を指定するために使用されます。また JobConf は入力ファイル・セットを (setInputPaths(JobConf,
Path...) /addInputPath(JobConf, Path))、や (setInputPaths(JobConf, String) /addInputPaths(JobConf,
String)) で、出力ファイルがライトされる場所を (setOutputPath(Path)) で示します。
JobConf はオプションで、利用する Comparator、DistributedCache にプットされるファイル、中間出力
とジョブ出力が圧縮されるか?否か?(そして圧縮方法)、ユーザに提供されるスクリプトを介したデバッグ
(setMapDebugScript(String) /setReduceDebugScript(String))、ジョブのタスクを恣意的に実行する
かどうか? (setMapSpeculativeExecution(boolean) /setReduceSpeculativeExecution(boolean))、
タスク毎の最大試行数 (setMaxMapAttempts(int) /setMaxReduceAttempts(int))、ジョブが許容するタス
ク障害のパーセンテージ (setMaxMapTaskFailuresPercent(int) /setMaxReduceTaskFailuresPercent(int))
といったジョブのその他の高度な側面を指定するために使用されます。
も ち ろ ん ユ ー ザ は 、ア プ リ ケ ー シ ョ ン に 必 要 な 任 意 の パ ラ メ ー タ の 設 定・参 照 に set(String,
String)/get(String, String) を 使 う こ と が で き ま す 。し か し な が ら 、( 読 み 出 し 専 用 の )大 量 の
データには DistributedCache を使用してください。
9
6.3 タスクの実行と環境
TaskTracker は個別の jvm の子プロセスとして Mapper タスクと Reducer タスクを実行します。
子タスクは親である TaskTracker の環境を継承します。ユーザが child-jvm に対し、JobConf のコンフィグ
レーション・パラメーター mapred.child.java.opts を介して、-Djava.library.path= <> などの標準
でないパスといった追加オプションを指定することで、ランタイム・リンカーがシェアード・ライブラリを探
索します。mapred.child.java.opts がシンボル @taskid@ を含んでいる場合、それは map/reduce のタ
スクIDに書き換えられます。
複数の引数や置き換えを行っている、jvm の GC のロギングを行い、パスワードなしで JVM JMX エージェ
ントを始動し(したがって jconsole にコネクトでできる)、child jvm のメモリやスレッドをウォッチし、ス
レッド・ダンプを得る事例を次に示します。この例は child jvm の最大ヒープ・サイズを 512MB に設定し、
child-jvm の java.library.path にパスの追加も行っています。
<p r o p e r t y>
<name>mapred . c h i l d . j a v a . o p t s</name>
<v a l u e>
−Xmx512M −Djava . l i b r a r y . path=/home/mycompany/ l i b
−v e r b o s e : g c −X l o g g c : /tmp/ @taskid@ . gc
−Dcom . sun . management . jmxremote . a u t h e n t i c a t e=f a l s e
−Dcom . sun . management . jmxremote . s s l=f a l s e
</ v a l u e>
</ p r o p e r t y>
Users と admins はいずれも mapred.child.ulimit を使って最大仮想メモリを指定することもできます。
mapred.child.ulimit の値はキロ・バイト (KB) で指定します。また JavaVM に引き渡す-Xmx への値は
0以上でなければなりません。でなければ VM は起動しないでしょう。
注: mapred.child.java.opts は task tracker の child task の起動時のコンフィグレーションにしか使われ
ません。デーモンのメモリ・オプションのコンフィグレーションは、cluster setup.html で説明されてい
ます。
tasck tracker はローカライズされたキャッシュとローカライズされたジョブを生成するためのローカル・ディ
レクトリー$mapred.local.dir/taskTracker/ を持っています。(複数のディスクにまたがる)複数のロー
カル・ディレクトリを定義することができ、個々のファイル名は半ばランダムにローカル・ディレクトリへ割
り当てられます。ジョブが開始すると、task tracker はコンフィグレーションで指定されたローカル・ディレ
クトリの下にローカライズされたジョブ・ディレクトリを生成します。したがって task tracker のディレク
トリー構造は次のようになります:
• ${mapred.local.dir}/taskTracker/archive/: 分散キャッシュ。このディレクトリーはローカラ
イズされた分散キャッシュを保持します。したがってローカライズされた分散キャッシュは、すべての
タスクやジョブで共有されます。
• ${mapred.local.dir}/taskTracker/jobcache/$jobid/: ローカライズされたジョブ・ディレク
トリー
– ${mapred.local.dir}/taskTracker/jobcache/$jobid/work/: ジョブに特化した共有ディレ
クトリー。タスクはこのスペースをスクラッチ・スペースとして使用し、相互でファイルを共有す
ることができますこのディレクトリーはコンフィグレーション・プロパティ job.local.dir を介
してユーザーに公開されます。ディレクトリはAPI JobConf.getJobLocalDir() を介してア
クセスすることができ、システム・プロパティとしても利用可能です。したがって(ストリーミン
グなどで)ユーザーは System.getProperty("job.local.dir") をコールして、ディレクトリ
にアクセスすることができます。
– ${mapred.local.dir}/taskTracker/jobcache/$jobid/jars/: ジョブの jar ファイルを保持
し展開する jars ディレクトリーです。job.jar は各マシンに自動的に配布されるアプリケーショ
ンの jar ファイルです。ジョブを開始するためのタスクより前に jars ディレクトリーで展開され
ます。job.jar の配置はAPI JobConf.getJar() を介してアプリケーションからアクセスでき
10
ます。JobConf.getJar().getParent() をコールして、jar を展開するディレクトリにアクセス
することができます。
– $mapred.local.dir/taskTracker/jobcache/$jobid/job.xml: ファイル job.xml - ジョブ
にローカライズされた一般的なジョブ・コンフィグレーションです。
– ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid: 実行が試みられるタスク
毎のタスク・ディレクトリです。それぞれのタスク・ディレクトリーはさらに次の構造を持ってい
ます:
∗ $mapred.local.dir/taskTracker/jobcache/$jobid/$taskid/job.xml: タスクのロー
カライズされたコンフィグレーションである job.xml ファイルです。タスクのローカライゼ
イションとはプロパティはジョブの中にあるこれら特定のタスクを固有の設定を意味します。
各タスクのためにローカライズされたプロパティは以下で説明します。
∗ ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/output: 中 間 出 力 フ
ァイルのためのディレクトリです。ここには map の出力ファイルといったフレームワー
クにより生成される一時的な map/reduce データが含まれます。
∗ ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/work: タスクのカレン
ト・ワーク・ディレクトリです。
∗ ${mapred.local.dir}/taskTracker/jobcache/$jobid/$taskid/work/tmp: タ ス ク 用
のテンポラリ・ディレクトリです。(ユーザはプロパティ mapred.child.tmp を指定して
map タスクおよび reduce タスクのためのテンポラリ・ディレクトリの値を設定することが
できます) 値が絶対パスでない場合、これは自動的に./tmp になり、タスクの作業ディレクト
リーに追加されます。そうでなければ直接割り当てられます。ディレクトリは存在しなけれ
ば作成されます。その後、子どもの java タスクはオプション-Djava.io.tmpdir=’ テンポラ
リ・ディレクトリの絶対パス’ で実行されますパイプとストリーミングは環境変数 TMPDIR=’
テンポラリ・ディレクトリの絶対パス’) によって設定されます。mapred.child.tmp が値
./tmp の場合、このディレクトリーは作成されます。
次のプロパティは各タスクの実行時にジョブ・コンフィグレーションでローカライズされます:
名前
タイプ
説明
mapred.job.id
mapred.jar
job.local.dir
mapred.tip.id
mapred.task.id
mapred.task.is.map
mapred.task.partition
map.input.file
map.input.start
map.input.length
mapred.work.output.dir
String
String
String
String
String
boolean
int
String
long
long
String
ジョブID
ジョブ・ディレクトリの job.jar の配置
ジョブに特化した共有スクラッチ・スペース
タスクID
タスク試行ID
map タスクのフラグ
ジョブ内のタスクのID
map がリードするファイルの名前
map input split を開始するオフセット
map input split を行うバイト数
タスクのテンポラリ出力ディレクトリ
タ ス ク の 標 準 出 力 (stdout) お よ び エ ラ ー 出 力 (stderr) は TaskTracker に よ っ て リ ー ド さ れ 、
$HADOOP LOG DIR/userlogs にログが出力されます。
map タスクや reduce タスクで利用される jars とネーティブ・ライブラリの配布にも DistributedCache は使
用されます。child-jvm は常に、その現在の作業ディレクトリーを java.library.path と LD LIBRARY PATH
に追加します。したがって、キャッシュされたライブラリーが System.loadLibrary あるいは System.load
介してロードされます。分散キャッシュからの共有ライブラリをロードする方法に関するより多くの詳細は
native libraries.html にドキュメント化されています。
11
6.4 ジョブの起動とモニタリング
JobClient はユーザーのジョブが JobTracker とやり取りを行うプライマリ・インターフェースです。
JobClient はジョブの起動、その進捗状態のトラッキング、コンポーネント・タスクのレポートやログへのア
クセス、Map/Reduce クラスタの状態情報の取得などの機能を提供します。
ジョブ起動プロセスには次の項目が含まれます:
1.
2.
3.
4.
ジョブの入出力仕様のチェック
ジョブに対する InputSplit 値の計算
ジョブの DistributedCache のために必要な会計情報を設定(必要なら)
FileSystem の Map/Reduce システム・ディレクトリへの
ジョブの jar およびコンフィグレーションのコピー
5. JobTracker へのジョブの起動および(オプションの)ジョブの状態のモニタリング
ジョブ・ヒストリ・ファイルもユーザーが指定したディレクトリ hadoop.job.history.user.location に記
録されます。
(デフォルトはジョブ出力ディレクトリ)ファイルは指定されたディレクトリの “ logs/history/”
に格納されます。したがってデフォルトでは mapred.output.dir/ logs/history に存在します。ユーザ
は hadoop.job.history.user.location に none を設定することにより、ロギングを停止することができ
ます。
ユーザは次のコマンドを使用して、指定された出れ区鳥にあるヒストリ・ログのサマリを見ることができます。
$ b i n / hadoop j o b −history output−d i r
このコマンドはジョブの詳細や障害停止、強制停止された詳細を出力します。成功したタスクや各々のタスク
ごとの試みといったジョブのより詳細な情報は次のコマンドを使って見ることができます。
$ b i n / hadoop j o b −history a l l output−d i r
ユーザは OutputLogFilter を使用して、出力ディレクトリー・リストからログ・ファイルにフィルターをか
けることができます。
通常、ユーザはアプリケーションを作成し、JobConf によって仕事の様々な側面について記述した上で、
JobClient を使用してジョブを起動し、その進行をモニターします。
6.4.1 ジョブの制御
ユーザは単独の Map/Reduce ジョブでは処理しきれない複雑なタスクの処理を達成するために Map/Reduce
ジョブをチェインする必要があるかもしれません。ジョブ出力は通常、分散ファイルシステムに行く、すなわ
ち、出力は次のジョブの入力として使えるので、これはかなり簡単です。
しかしながらこれは、ジョブが(正常/異常)終了していることを保証する責任はクライアントに直結してい
ることも意味します。そのような場合、次のような様々なジョブ制御オプションが使えます:
runJob(JobConf)
ジョブを起動してジョブが完了すると返ります。
submitJob(JobConf)
ジョブを起動するだけです。
その後、返された RunningJob へのハンドルを
ポーリングして、状態を問合せ、
スケジュール上の決定を行います。
JobConf.setJobEndNotificationURI(String)
ジョブ完了時に通知を行うよう設定をします。
したがってポーリングは回避されます。
12
6.5 ジョブ入力
InputFormat は Map/Reduce ジョブの入力使用を記述します。
Map/Reduce フレームはジョブの InputFormat の次の項目を信頼しています。
1. ジョブの入力仕様の有効性
2. InputSplit の論理的なインスタンスへの入力ファイルの分割
その後、個々の Mapper に割り当てられます。
3. RecordReader 実装の提供
Mapper によって処理する論理的な InputSplit から入力レコードを収集するために使用されます。
ファイルに基づく InputFormat 実装(FileInputFormat のサブクラスであることが一般的)のデフォルト
の振る舞いは、入力ファイルのバイト数によるトータル・サイズに基づいて入力を論理的な InputSplit イン
スタンスに分割することです。しかしながら、入力ファイルの FileSystem のブロック・サイズには入力スプ
リットの上限があります。スプリット・サイズの下限は mapred.min.split.size で設定できます。
レコード境界を尊重しなければならないので、多くのアプリケーションとって入力サイズに基づいた論理的
なスプリットは不十分なのは明らかです。そのような場合、アプリケーションはレコード境界の尊重に責任
を負い、個々のタスクに論理的な InputSplit についてのレコード・オリエンテッドなビューを提供する
RecordReader を実装するべきです。
TextInputFormat が InputFormat のデフォルトです。
TextInputFormat が指定されたジョブの InputFormat である場合、フレームワークは .gz と .lzo のエクス
テンションが伴う入力ファイルを検知し、適切な CompressionCodec を使用して、自動的にそれらの伸張を
行います。しかしながら、上記のエクステンションを備えた圧縮ファイルが分割できず、個々の圧縮ファイル
は単一の mapper によってその全てを処理されなければならないことに注意しなければなりません。
6.5.1 InputSplit
InputSplit は個々の Mapper によって処理されるデータを表現します。
通常 InputSplit は入力についてバイト・オリエンテッドなビューを示します。レコード・オリエンテッドな
ビューの処理や表現は RecordReader が責任を負います。
FileSplit がデフォルト InputSplit です。map.input.file に論理的なスプリットの入力ファイルのパス
を設定します。
6.5.2 RecordReader
RecordReader は InputSplit から<key, value >ペアをリードします。
通常 RecordReader は、InputSplit によって提供される入力のバイト・オリエンテッドなビューを変換し
て、処理するための Mapper 実装へレコード・オリエンテッドなビューを示します。それ故、RecordReader
はレコード境界を処理する責任を負い、タスクにキーと値を提示します。
6.6 ジョブ出力
OutputFormat は Map/Reduce ジョブの出力仕様について記述します。
Map/Reduce フレームはジョブの OutputFormat の次の項目を信頼しています。
1. ジョブの出力仕様の有効性;
例えば、出力ディレクトリーが存在するか否かのチェックなど。
13
2. RecordWriter 実装の提供
Mapper によって処理する論理的な InputSplit からジョブの出力ファイルをライトするために使用さ
れます。出力ファイルは FileSystem に格納されます。
TextOutputFormat がデフォルト OutputFormat です。
6.6.1 タスクに影響を与えるファイル
いくつかのアプリケーションにおいて、コンポーネント・タスクは実際のジョブ出力ファイルとは異なる2次
的なファイルの作成やライトを必要とする場合があります。
そのような場合、FileSystem で同じファイル (パス) にオープンあるいはライトを試みる同時に動作する同
じ Mapper あるいは Reducer の2つのインスタンス(例えば熟考するタスク)が問題になる可能性がありま
す。したがって、アプリケーションの開発者はタスク毎ではなく、(task 200709221812 0001 m 000000 0
みたいにタスクIDを使って)タスクの試み毎にユニークな名前を取らなければなりません。
これらの問題を回避するために、Map/Reduce フレームワークは FileSystem において個々のタスクの試みが
${mapred.work.output.dir} を介してアクセス可能で、タスクの試みの出力が格納される特殊なサブ・ディ
レクトリ${mapred.output.dir}/ temporary/ ${taskid} を維持しています。タスクの試みの無事完了し
た場合、${mapred.output.dir}/ temporary/ ${taskid} のファイル(だけ)が${mapred.output.dir}
に展開されます。もちろん、失敗したタスク試みのサブディレクトリーをフレームワークは廃棄します。この
プロセスはアプリケーションには完全に透過的です。
アプリケーションの開発者はこの機能を利用して、FileOutputFormat.getWorkOutputPath() を介してタ
スクの実行中に${mapred.work.output.dir} で要求される任意のサイド・ファイルを作成することができ
ます。またフレームワークは同様に無事完了したタスク試みのファイルを展開します。これによりタスク試み
毎にユニークなパスを取る必要はなくなります。
注意: 特定のタスクの試みが実行中の${mapred.work.output.dir} の値は、実際には${mapred.output.dir}/ temporary/ {
で、この値は map/reduce のフレームワークによって設定されます。したがってこの機能の利点を得るため
には、map/reduce タスクから FileOutputFormat.getWorkOutputPath() によって返されたパスを使って
任意のサイド・ファイルを作成するだけです。
reducer=NONE(つまり reduce が0)の時、(この場合には)map の出力は直接 HDFS に行くので、ジョ
ブの map に対しては全ての議論は正しくなります。
6.6.2 RecordWriter
RecordWriter は出力ファイルに出力<key, value> ペアをライトします。
RecordWriter の実装は FileSystem にジョブ出力を書きます。
6.7 その他の便利な機能
6.7.1 Counters
Counters はグローバルなカウンターを表現し、Map/Reduce のフレームワークあるいはアプリケーションの
いずれかにより定義されます。Counters は各々任意の Enum タイプになり得えます。特定の Enum Counters
は、Counters.Group タイプのグループに束ねられます。
アプリケーションは任意の(Enum タイプの)Counters を定義し、map あるいは reduce メソッドにおいて
Reporter.incrCounter(Enum, long) を介してその更新を行うことができます。その後、これらのカウン
ターはフレームワークにより広くから集約されます。
6.7.2 DistributedCache
DistributedCache はアプリケーションに特化した大きなリード専用のファイルを効率的に配布します。
14
DistributedCache はアプリケーションに必要な(テキスト、アーカイブ、jar などの)ファイルをキャッ
シュするため、Map/Reduce によって提供される機能です。
アプリケーションは JobConf で定義するURL (hdfs:// or http://) によってキャッシュするファイルを指
定します。DistributedCache は、hdfs://urls によって指定されたファイルが FileSystem に既に存在す
ると仮定します。
フレームワークはスレーブ・ノードで実行されるジョブの任意のタスクが起動される前に必要なファイルをそ
のノードへコピーします。その効率性は、ファイルがジョブ毎に1度しかコピーされないという事実とスレー
ブにはアーカイブされていなかったアーカイブをキャッシュする能力に起因します。
DistributedCache はキャッシュされているファイルの修正タイムスタンプを追跡します。ジョブが実行さ
れている間、キャッシュ・ファイルはアプリケーションや外部的に修正されるべきでないことは明らかです。
DistributedCache は単純なリード専用のデータ/テキストファイルだけでなくアーカイブと jar のような
より複雑なタイプを配布するために使用するすることができます。アーカイブ(zip, tar, tgz, tar.gz ファイ
ル)はスレーブ・ノードではアーカイブされません。ファイルは実行時パーミションのセットを持っています。
フ ァ イ ル や ア ー カ イ ブ は プ ロ パ テ ィ mapred.cache.{file|archve} を 設 定 す る こ と に よ り 配 布
す る こ と が で き ま す 。複 数 の フ ァ イ ル や ア ー カ イ ブ を 配 布 し な け れ ば な ら な い 場 合 、コ ン マ で
分 割 し た パ ス を 追 加 す る こ と が で き ま す 。A P I DistributedCache.addCacheFile(URI,conf)/
DistributedCache.addCacheArchive(URI,conf) あるいは DistributedCache.setCacheFiles(URIs,conf)/
DistributedCache.setCacheArchives(URIs,conf) によりプロパティを設定することもできます。UR
Iは hdfs://host:port/absolute-path#link-name の形式です。ストリーミングでは、コマンドライン・
オプション-cacheFile / -cacheArchive 介して、ファイルは配布されます。
オ プ シ ョ ン で 、ユ ー ザ は DistributedCache.createSymlink (コ ン フ ィ グ レ ー シ ョ ン )A P I を 介 し
て、あるいはコンフィグレーション・プロパティ mapred.create.symlink に yes を設定することで、
DistributedCache にタスクのカレント・ワーク・ディレクトリーへキャッシュされたファイルを symlink
するように指示することもできます。DistributedCache はURIの一部を symlink の名前として使用しま
す。例えば、URI hdfs://namenode:port/lib.so.1#lib.so のは、タスクの分散キャッシュにあるファ
イル lib.so.1 ですが、カレント・ワーク・ディレクトリーでは lib.so という symlink 名を持つでしょう。
DistributedCache は map あるいは reduce のタスクで使用される基本的なソフトウェアの配布メカ
ニズムとしても利用することができます。jar とネーティブ・ライブラリの両者を配布するために使用
することができます。DistributedCache.addArchiveToClassPath(Path, Configuration) あるいは
DistributedCache.addFileToClassPath(Path, Configuration) APIは、ファイルや jar のキャッ
シュに利用でき、さらに child-jvm の classpath に追加することができます。コンフィグレーション・プロパ
ティ mapred.job.classpath.{files|archives} への設定を行うことにより、同じことができます。同様
に、タスクのワーク・ディレクトリへ symlink されるキャッシュ・ファイルは、ネーティブ・ライブラリーを
配布・ロードするために使用することができます。
6.7.3 Tool
Tool インターフェースはジェネリックな Hadoop コマンドライン・オプションのハンドリングをサポートし
ます。
Tool は Map/Reduce のツールやアプリケーション全てでの標準です。アプリケーションは標準的な
コマンドライン・オプションのハンドリングについては ToolRunner.run(Tool, String[]) を介して
GenericOptionsParser を委託し、独自の引数のハンドリングのみを行うべきです。
ジェネリックな Hadoop コマンドライン・オプションは次のとおりです:
−c o n f <c o n f i g u r a t i o n f i l e >
−D <p r o p e r t y=v a l u e >
− f s < l o c a l | namenode : port >
− j t < l o c a l | j o b t r a c k e r : port >
15
6.7.4 IsolationRunner
IsolationRunner は Map/Reduce プログラムのデバッグを助けるユーティリィティです。
IsolationRunner を利用するためには、まず keep.failed.tasks.files に true を設定してください。
(keep.tasks.files.pattern も参照のこと)
次に、失敗した タ スクが実行 されたノードの TaskTracker のローカル・ディレクトリーに移動して、
IsolationRunner を実行します:
$ cd < l o c a l path>/t a s k T r a c k e r / $ { t a s k i d }/ work
$ b i n / hadoop o r g . apache . hadoop . mapred . I s o l a t i o n R u n n e r . . / j o b . xml
IsolationRunner は、(デバッガーの役割を果たす)単一の jvm において、正確に同じ入力により失敗した
タスクを実行します。
6.7.5 プロファイリング
プロファイリングは map や reduce のサンプルのための java のビルドイン・プロフィーラーの(2、3の)
代表的な事例を得るユーティリィティです。
ユーザはコンフィグレーション・プロパティ mapred.task.profile を設定することにより、システム
がジョブの幾つかのタスクのプロフィーラー情報の収集の可否を指定することができます。値はAPI
JobConf.setProfileEnabled(boolean) を使用して設定することができます。値が true の場合、タスク
のプロファイリングが可能になります。プロフィーラー情報はユーザーのログ・ディレクトリに格納されま
す、デフォルトではジョブのプロファイリングは有効になっていません。
ユーザーがプロファイリングが必要だとコンフィグレーションを行うと、コンフィグレーション・プロパティ
mapred.task.profile.{maps|reduces} を使用して、プロファイルを行う map/reduce タスクの範囲を設
定することができます。デフォルトで指定されている範囲は0∼2です。
ユ ー ザ は コ ン フ ィ グ レ ー シ ョ ン・プ ロ パ テ ィ mapred.task.profile.params を 設 定 す る こ と に
よ り 、プ ロ フ ァ イ ラ の コ ン フ ィ グ レ ー シ ョ ン の 引 数 も 指 定 す る こ と が で き ま す 。値 は A P I
JobConf.setProfileParams(String) を 使 用 し て 指 定 す る こ と が で き ま す 。ス ト リ ン グ に %s が 含
まれている場合、タスクの実行時に、プロファイリング出力ファイルの名前に置き換えられます。これらのパ
ラメーターはコマンド・ラインでタスクの Child JVM に渡されます。プロファイリング・パラメーターの
デフォルト値は-agentlib:hprof=cpu=samples,heap=sites,force=n,thread=y,verbose=n,file=%s
です。
6.7.6 デバッギング
Map/Reduce フレームワークはデバッグ用のユーザーが提示したスクリプトを実行する機能を提供します。
map/reduce タスクが障害停止した場合、ユーザは、(タスクの stdout、stderr、syslog、jobconf といった)
タスク・ログのポスト処理を行うためにスクリプトを実行することができます。ユーザーが提示したデバッ
グ・スクリプトの stdout と stderr は診断メッセージにプリントされます。これらの出力はオン・デマンドで
ジョブUIにも表示されます。
以降のセクションでは、ジョブに合わせてデバッグ・スクリプトを起動する方法について議論します。デバッ
グ・スクリプトを起動するためには、まずそれを配布しなければなりません。その後、スクリプトはコンフィ
グレーションに与えられます。
• スクリプト・ファイルを配布する方法:
デバッグ・スクリプト・ファイルを配布して symlink するためには、ユーザは DistributedCache メカニズ
ムを利用しなければなりません。
• スクリプトをサブミットする方法:
デ バ ッ グ・ス ク リ プ ト を サ ブ ミ ッ ト す る 手 っ 取 り 早 い 方 法 は 、map タ ス ク と reduce タ ス ク の 各 々
16
に ‘‘mapred.map.task.debug.script’’ と ‘‘mapred.reduce.task.debug.script’’ に 値 を 設
定 す る こ と で す 。こ れ ら の プ ロ パ テ ィ は A P I JobConf.setMapDebugScript(String) お よ び
JobConf.setReduceDebugScript(String) を 使 用 し て 設 定 す る こ と も で き ま す 。ス ト リ ー ミ ン グ に
ついては、map タスクと reduce タスクの各々にコマンド・ライン・オプション-mapdebug、-reducedebug
を付けてデバッグ・スクリプトをサブミットすることができます。
スクリプトの引数はタスクの stdout、stderr、syslog、jobconf ファイルです。map/reduce が失敗したノー
ドにおいて実行するデバッグ・コマンドは次のとおりです:
$ s c r i p t $stdout $stderr $syslog $jobconf
プログラム Pipes は、コマンドの5番目の引数として C++ プログラムの名前を持っています。したがって、
プログラム pipes のためのコマンドは次の通りです:
$ s c r i p t $ s t d o u t $ s t d e r r $ s y s l o g $ j o b c o n f $program
• デフォルト時の振る舞い:
pipes のために、デフォルト・スクリプトは gdb においてコア・ダンプが実行され、スタック・トレースをプ
リントし、スレッドの実行に関する情報を与えます。
6.7.7 JobControl
JobControl は Map/Reduce のジョブとそれが依存するものをカプセル化するユーティリィティです。
6.7.8 データ圧縮
Hadoop Map/Reduce は中間 map 出力とジョブ出力(例えば reduce の出力)の圧縮を指定する機能をアプリ
ケーションの開発者に提供します。また zlib と lzo 圧縮アルゴリズムをサポートする CompressionCodec
実装をバンドルしています。gzip ファイル・フォーマットもサポートされています。
さらに Hadoop はパフォーマンス (zlib) と Java ライブラリで未サポート (lzo) との理由から、上記の圧縮
コーディックのネーティブ・インプリメンテーションを提供しますそれらのサポート範囲と使用法に関するさ
らなる詳細はネーティブ・ライブラリのドキュメントで得られます。
• 中間出力
アプリケーションは中間 map 出力に対してAPI JobConf.setCompressMapOutput(boolean) を介して
圧縮を、API JobConf.setMapOutputCompressorClass(Class) 介して CompressionCodec の利用を制
御することができます。
• ジョブ出力
ア プ リ ケ ー シ ョ ン は ジ ョ ブ 出 力 に 対 し て A P I OutputFormatBase.setCompressOutput(JobConf,
boolean) を介して圧縮を、API OutputFormatBase.setOutputCompressorClass(JobConf, Class)
介して CompressionCodec の利用を、制御することができます。
ジョブ出力が SequenceFileOutputFormat で格納されている場合、要求する SequenceFile.CompressionType
(すなわち RECORD/BLOCK - RECORD to デフォルト)はAPI
SequenceFileOutputFormat.setOutputCompressionType (JobConf, SequenceFile.CompressionType)
を介して指定することが可能です。
17
7 事例: WordCount v2.0
ここで、私たちがここまで議論した Map/Reduce フレームワークにより提供される多くの機能を使用するよ
り完全な WordCount を示します。
これは起動と実行に HDFS、特に DistributedCache 関連の機能を必要とします。したがって Hadoop イ
ンストレーションの pseudo-distributed、fully-distributed [?] でのみ動作します。
7.1 ソースコード
WordCount.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package o r g . myorg ;
import j a v a . i o . ∗ ;
import j a v a . u t i l . ∗ ;
import
import
import
import
import
import
o r g . apache . hadoop . f s . Path ;
o r g . apache . hadoop . f i l e c a c h e . D i s t r i b u t e d C a c h e ;
o r g . apache . hadoop . c o n f . ∗ ;
o r g . apache . hadoop . i o . ∗ ;
o r g . apache . hadoop . mapred . ∗ ;
o r g . apache . hadoop . u t i l . ∗ ;
public c l a s s WordCount extends C o n f i g u r e d implements Tool {
public s t a t i c c l a s s Map extends MapReduceBase
implements Mapper<LongWritable , Text , Text , I n t W r i t a b l e > {
s t a t i c enum Counters { INPUT WORDS }
private f i n a l s t a t i c I n t W r i t a b l e one = new I n t W r i t a b l e ( 1 ) ;
private Text word = new Text ( ) ;
private boolean c a s e S e n s i t i v e = true ;
private Set<S t r i n g > p a t t e r n s T o S k i p = new HashSet<S t r i n g > ( ) ;
private long numRecords = 0 ;
private S t r i n g i n p u t F i l e ;
public void c o n f i g u r e ( JobConf j o b ) {
c a s e S e n s i t i v e = j o b . g e t B o o l e a n ( " wordcount . case . sensitive " , true ) ;
i n p u t F i l e = j o b . g e t ( " map . input . file " ) ;
i f ( j o b . g e t B o o l e a n ( " wordcount . skip . patterns " , f a l s e ) ) {
Path [ ] p a t t e r n s F i l e s = new Path [ 0 ] ;
try {
p a t t e r n s F i l e s = DistributedCache . getLocalCacheFiles ( job ) ;
} catch ( IOException i o e ) {
System . e r r . p r i n t l n (
" Caught à exception à while à getting à cached à files :Ã" +
StringUtils . stringifyException ( ioe ));
}
f o r ( Path p a t t e r n s F i l e : p a t t e r n s F i l e s ) {
parseSkipFile ( patternsFile );
}
18
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
}
}
private void p a r s e S k i p F i l e ( Path p a t t e r n s F i l e ) {
try {
BufferedReader f i s =
new B u f f e r e d R e a d e r (new F i l e R e a d e r ( p a t t e r n s F i l e . t o S t r i n g ( ) ) ) ;
S t r i n g p a t t e r n = null ;
while ( ( p a t t e r n = f i s . r e a d L i n e ( ) ) != null ) {
p a t t e r n s T o S k i p . add ( p a t t e r n ) ;
}
} catch ( IOException i o e ) {
System . e r r . p r i n t l n (
" Caught à exception à while à parsing à the à cached à file à ’" +
p a t t e r n s F i l e + " ’Ã:Ã" +
StringUtils . stringifyException ( ioe ));
}
}
public void map( LongWritable key , Text v a l u e ,
O u t p u t C o l l e c t o r <Text , I n t W r i t a b l e > output , R e p o r t e r r e p o r t e r )
throws IOException {
String l i n e = ( c a s e S e n s i t i v e ) ? value . toString ()
: v a l u e . t o S t r i n g ( ) . toLowerCase ( ) ;
for ( S t r i n g pattern : patternsToSkip ) {
l i n e = l i n e . r e p l a c e A l l ( p a t t e r n , "" ) ;
}
S t r i n g T o k e n i z e r t o k e n i z e r = new S t r i n g T o k e n i z e r ( l i n e ) ;
while ( t o k e n i z e r . hasMoreTokens ( ) ) {
word . s e t ( t o k e n i z e r . nextToken ( ) ) ;
output . c o l l e c t ( word , one ) ;
r e p o r t e r . i n c r C o u n t e r ( Counters . INPUT ORDS, W1) ;
}
i f ((++numRecords % 1 0 0 ) == 0 ) {
r e p o r t e r . s e t S t a t u s ( " Finished à processing Ã" +
numRecords + "à records Ã" + " from à the à input à file :Ã" + i n p u t F i l e ) ;
}
}
}
public s t a t i c c l a s s Reduce extends MapReduceBase
implements Reducer<Text , I n t W r i t a b l e , Text , I n t W r i t a b l e > {
public void r e d u c e ( Text key , I t e r a t o r <I n t W r i t a b l e > v a l u e s ,
O u t p u t C o l l e c t o r <Text , I n t W r i t a b l e > output ,
R e p o r t e r r e p o r t e r ) throws IOException {
in t sum = 0 ;
while ( v a l u e s . hasNext ( ) ) {
sum += v a l u e s . ne xt ( ) . g e t ( ) ;
}
output . c o l l e c t ( key , new I n t W r i t a b l e ( sum ) ) ;
}
}
public i n t run ( S t r i n g [ ] a r g s ) throws E x c e p t i o n {
JobConf c o n f = new JobConf ( g e t C on f ( ) , WordCount . c l a s s ) ;
c o n f . setJobName ( " wordcount " ) ;
19
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
c o n f . setOutputKeyClass ( Text . c l a s s ) ;
conf . setOutputValueClass ( IntWritable . class ) ;
c o n f . s e t M a p p e r C l a s s (Map . c l a s s ) ;
c o n f . s e t C o m b i n e r C l a s s ( Reduce . c l a s s ) ;
c o n f . s e t R e d u c e r C l a s s ( Reduce . c l a s s ) ;
c o n f . s e t I n p u t F o r m a t ( TextInputFormat . c l a s s ) ;
c o n f . setOutputFormat ( TextOutputFormat . c l a s s ) ;
L i s t <S t r i n g > o t h e r a r g s = new A r r a y L i s t <S t r i n g > ( ) ;
f o r ( in t i =0; i < a r g s . l e n g t h ; ++i ) {
i f ( " - skip " . e q u a l s ( a r g s [ i ] ) ) {
D i s t r i b u t e d C a c h e . a d d C a c h e F il e (new Path ( a r g s [++ i ] ) . t o U r i ( ) , c o n f ) ;
c o n f . s e t B o o l e a n ( " wordcount . skip . patterns " , true ) ;
} else {
o t h e r a r g s . add ( a r g s [ i ] ) ;
}
}
F i l e I n p u t F o r m a t . s e t I n p u t P a t h s ( c o n f , new Path ( o t h e r a r g s . g e t ( 0 ) ) ) ;
FileOutputFormat . setOutputPath ( c o n f , new Path ( o t h e r a r g s . g e t ( 1 ) ) ) ;
J o b C l i e n t . runJob ( c o n f ) ;
return 0 ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n {
i n t r e s = ToolRunner . run (new C o n f i g u r a t i o n ( ) , new WordCount ( ) , a r g s ) ;
System . e x i t ( r e s ) ;
}
}
7.2 サンプル実行
入力のサンプル・テキスト・ファイルを次の通りです:
$ b i n / hadoop d f s − l s / u s r / j o e / wordcount / i n p u t /
/ u s r / j o e / wordcount / i n p u t / f i l e 0 1
/ u s r / j o e / wordcount / i n p u t / f i l e 0 2
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / i n p u t / f i l e 0 1
H e l l o World , Bye World !
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / i n p u t / f i l e 0 2
H e l l o Hadoop , Goodbye t o hadoop .
アプリケーションは次のとおり実行します:
$ b i n / hadoop j a r / u s r / j o e / wordcount . j a r
o r g . myorg . WordCount / u s r / j o e / wordcount / i n p u t / u s r / j o e / wordcount / output
出力は次の通りです:
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / output / part −00000
Bye 1
Goodbye 1
Hadoop , 1
Hello 2
World ! 1
20
World , 1
hadoop . 1
to 1
入力が私たちが見た最初のバージョンと異なることそしてそれが出力にどのように影響するかに注目してくだ
さい。
さて、DistributedCache を使って無効にするワード・パターンをリストするパターン・ファイルをプラグ・
インしてみましょう。
$ hadoop d f s −cat / u s e r / j o e / wordcount / p a t t e r n s . t x t
\\.
\\ ,
\\!
to
もう一度実行します。今度はオプションが増えます。
$ b i n / hadoop j a r / u s r / j o e / wordcount . j a r
o r g . myorg . WordCount −Dwordcount . case . s e n s i t i v e=t r u e
/ u s r / j o e / wordcount / i n p u t / u s r / j o e / wordcount / output
−s k i p / u s e r / j o e / wordcount / p a t t e r n s . t x t
予想通り、次のように出力されます:
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / output / part −00000
Bye 1
Goodbye 1
Hadoop 1
Hello 2
World 2
hadoop 1
更にもう一度実行します。今度はケース・センシティビティをオフにします。
$ b i n / hadoop j a r / u s r / j o e / wordcount . j a r
o r g . myorg . WordCount −Dwordcount . case . s e n s i t i v e=f a l s e
/ u s r / j o e / wordcount / i n p u t / u s r / j o e / wordcount / output
−s k i p / u s e r / j o e / wordcount / p a t t e r n s . t x t
確かに、次のように出力されます:
$ b i n / hadoop d f s −cat / u s r / j o e / wordcount / output / part −00000
bye 1
goodbye 1
hadoop 2
hello 2
world 2
21
7.3 特徴
The second version of WordCount improves upon the previous one by using some features offered by the
Map/Reduce framework:
• Demonstrates how applications can access configuration parameters in the configure method of
the Mapper(and Reducer) implementations (lines 28-43).
• Demonstrates how the DistributedCachecan be used to distribute read-only data needed by the
jobs. Here it allows the user to specify word-patterns to skip while counting (line 104).
• Demonstrates the utility of the Tool interface and the GenericOptionsParser to handle generic
Hadoop command-line options (lines 87-116, 119).
• Demonstrates how applications can use Counters(line 68) and how they can set application-specific
status information via the Reporterinstance passed to the map (and reduce) method (line 72).
Java and JNI are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States
and other countries.
22
参考文献
[1] Hadoop home page. Hadoop Commands Manual.
http://hadoop.apache.org/core/docs/r0.18.0/commands_manual.html%
, September 2008.
23