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
© Copyright 2024 Paperzz