WebHDFSの性能評価

Hadoop 1.0.0がリリースされましたが、1.0.0にはWebHDFSの機能が最初から入っています。
WebHDFSとはHTTP REST APIでHDFSにアクセスできる機能ですが、httpfs(Hoop)との違いは以下のブログが分かりやすいです。
Hoop(httpfs)とwebhdfsの違い – tagomorisのメモ置き場

Hoopはスケーラビリティに疑問が残りますが、WebHDFSはDatanodeから直接データを取得するのでスケーラビリティの問題はありません。さっそく使ってみました。

使い方
設定はconf/hdfs-site.xmlに以下を書くだけ。デフォルトはオフなので、書く必要がある。

  <property>
    <name>dfs.webhdfs.enabled</name>
    <value>true</value>
  </property>

詳細は公式ドキュメントを見て頂くとして、curlの例は、

% curl -i "http://hostname:50070/webhdfs/v1/user/mikami/input/part-00000?op=OPEN"
HTTP/1.1 307 TEMPORARY_REDIRECT
Content-Type: application/octet-stream
Location: http://hostname2:60075/webhdfs/v1/user/mikami/input/part-00000?op=OPEN&offset=0
Content-Length: 0
Server: Jetty(6.1.26)
% curl -i "http://hostname2:60075/webhdfs/v1/user/mikami/input/part-00000?op=OPEN&offset=0" > tmp

このようにOPENのリクエストを出すとロケーションが帰ってくるので、あとは直接リクエストすれば良い。
もしくは、リダイレクトされるので以下のように-Lオプションでそのまま取得も出来る。

curl -i -L "http://hostname:50070/webhdfs/v1/user/mikami/input/part-00000?op=OPEN" > tmp2

評価方法
Teragenで生成した1GBのファイルを1クライアントで読み込む。native, curl, wgetで評価したが、その際のコマンドは、

native(timeで計測した時間からMB/secを計算)

time bin/hadoop fs -get hdfs://hostname:9000/user/mikami/input/part-00000 tmp

webhdfs-curl

curl -i -L "http://hostname:50070/webhdfs/v1/user/mikami/input/part-00000?op=OPEN" > tmp

webhdfs-wget

wget "http://hostname:50070/webhdfs/v1/user/mikami/input/part-00000?op=OPEN" -O tmp

性能評価に関する注意点としては、以下があります。

実験結果

見ての通りどの環境でもWebHDFSの圧勝です。

考察
以下のブログではローカルではnativeが早く、リモートではWebHDFSが早いという結果だったので、それを予想していたが、実際にはWebHDFSの圧勝だった。
WebHDFS Performance – Randomly Distributed

curlに関しては関しては性能が出なかったのでバッファサイズを変更して試してみたがあまり効果はなかった(WebHDFSのリクエストのパラメータでbuffersizeを指定できる)

結論としては、WebHDFSでのオーバーヘッドはほとんどないということと、curlとwgetの性能差の原因はよく分からないが、クライアントの実装が性能に大きな影響がある(HTTPの一般的な話の気がする。オプションを色々と試せば劇的に性能が変わる可能性もある。)といった所でしょうか。

3Dクラスタ監視ツール

最近クラスタの負荷状況を3Dで表示するようなツールを作ってます。

クラスタ監視ツールって既存のものもあるのに、なぜ今更?と思うかもしれませんが、
既存のもので足りないと思っているのは、

1. 十数台くらいまでなら良いがそれ以上になるとグラフを見るのがつらい
2. CPU毎、ディスク毎に見れない

自分の目的としては数百台とかの規模でHadoopを実行し、負荷状況を見れること、
後からも実行時の状況を調べてボトルネック等を分析出来るようにすることです。

そこでこんな感じのものを作りました。

構成はこんな感じです。使用技術は、
fluentd + node.js + websocket + webgl (Three.js)
といった感じでかなり流行りの技術を使ってる感じです。

各サーバーの負荷状況をfluentdで集めています。自作プラグインfluent-plugin-statでCPU、ディスク、ネット、メモリーの情報を集めてます。(まだ汎用性が低かったり、自分の環境用スクリプトが入ってたりして一般に使えるものではありません)
集めた情報をとりあえずファイルに書きだして、websocketのサーバーでその情報をブラウザに送ります。
ここらへんはmongodbに突っ込むとかして全部保存して後からも振り返れるようにしたいなとは思ってます。
ブラウザでは Three.js を使ってリアルタイムに3D表示します。

ここまでだけだとお遊び感がありますが、本当にやりたいのは、下の図みたいに複数ノードの情報を一つのグラフに書いたり、

Hadoop等アプリケーションの実行情報をとってきて可視化したり

みたいなことをしたい思っていて、その情報収集の基盤としてfluentdが使えるのでは!
ということと、可視化は全部ブラウザでやりたくてWebGL等を使ってます。

ここまで書きましたが、すでにちゃんと使えるものが欲しいと思った人はvgxpがおすすめです。
CPU毎、ディスク毎で見たいのとjre入れないといけないのがちょっと不満ですが。

まだまだ完成度が低くオープンソースで公開などという状況ではありませんが、https://github.com/shun0102/cluster_moniter でソース晒しながら作っています。コメント等もらえたら嬉しいです!

Hadoop Conference Japan 2011でLTしてきました

先日開かれた Hadoop Confenence Japan 2011 でLTしてきました。
発表スライドは以下です。

ポイントは、HDFSの大体となる汎用的なファイルシステムを探す場合候補としては、
Gfarm, GlusterFS, Ceph, Lustre, PVFS2などがありますよ。試した所では、

こんな感じです。
実際に性能評価を出してるのは一部の人にウケがいいみたいです。
こういう実際の性能評価をやって公開できる環境にある人も少ないと思うので、今のうちに評価はとって公開していきたいと思ってます!

Hadoopインストール時に作成されるユーザーに関する注意点(CDH3b3)

CDH3b3ではapt-get やyumでインストール時に hdfs, mapred ユーザーを作成してくれます。
しかし、クラスタではNISやLDAPなどでユーザーを管理していて勝手にユーザーを作られると困る時があるかと思います。

そこで、先にhdfs, mapred ユーザーを作ってからapt-getを実行すると、、、

...
Do you want to continue [Y/n]?
WARNING: The following packages cannot be authenticated!
  hadoop-0.20

Install these packages without verification [y/N]? y
Get:1 http://archive.cloudera.com jaunty-cdh3/contrib hadoop-0.20 0.20.2+737-1~jaunty-cdh3b3 [39.6MB]
Fetched 39.6MB in 6s (6024kB/s)
(Reading database ... 24993 files and directories currently installed.)
Preparing to replace hadoop-0.20 0.20.2+320-1~jaunty-cdh3b2 (using .../hadoop-0.20_0.20.2+737-1~jaunty-cdh3b3_all.deb) ...
usermod: user hdfs exists
dpkg: error processing /var/cache/apt/archives/hadoop-0.20_0.20.2+737-1~jaunty-cdh3b3_all.deb (--unpack):
 subprocess pre-installation script returned error exit status 9
Errors were encountered while processing:
 /var/cache/apt/archives/hadoop-0.20_0.20.2+737-1~jaunty-cdh3b3_all.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

原因はhadoop ユーザーが存在すると、それで hdfs ユーザーに変更しようとし、
そこでエラーが発生し、インストールが止まってしまうということです。。。
既にバグ報告もあります DISTRO-51

これは単純に hadoop ユーザーを消してしまえば解決します。

しかし、自分の環境では別の問題があって、OSインストール時にHadoopを自動インストールしたいという時、
Hadoopのインストールの前にユーザー管理システムでユーザーを作成出来ないということです。
結局は各ノードでuid と gid がユーザー管理システムと被らないものを指定してインストールスクリプト内で作成してしまうことにしました。

if ! getent group hadoop >/dev/null; then
     groupadd -g 10283 hadoop
fi

if ! getent group mapred >/dev/null; then
     groupadd -g 10343 mapred
     useradd mapred -g mapred -G hadoop -u 10343
     mkdir -p /home/mapred
     chown -R mapred:mapred /home/mapred
fi

if ! getent group hdfs >/dev/null; then
     groupadd -g 10344 hdfs
     useradd hdfs -g hdfs -G hadoop -u 10344
     mkdir -p /home/mapred
     chown -R hdfs:hdfs /home/hdfs
fi
...
apt-get -y install hadoop-0.20
...

こんな感じです。

他の人に役に立つのかわかりませんが、この時に作成したインストールスクリプトは

https://github.com/shun0102/hadoop_installer

に置いておきました。

sc10参加レポート(展示会)

11月13日から19日までアメリカ、ニューオーリンズで開かれたsc10に参加してきました。
まずは展示会の様子を写真メインで紹介しようと思います。

まず、SCというのはスパコン関連で最大の学会&展示会です。
展示会では、IBM, Microsoft, Intel などを初めとした企業や大学、研究機関が
各自の製品や研究をブースで展示しています。

Intel ブース

IBMブース

研究機関では、NASAブース

アルゴンヌ国立研究所
かっこいいですね

など、どの機関も力を入れて参加しています。
そして、各ブースでは工夫を凝らした様々なデモやパフォーマンスがありました。

面白かったのは、Voltaireではクイズに参加できるなど

質問は「エクサフロップスに達成すると見込まれている年代は?」というHPCらしい質問。

こんな感じで向こうの人はパフォーマンスがうまいですね。
マイクロソフトではゲームのデモをしていました。
体に何もつけなくてもテレビの下に置いてある装置から動きを感知して、キャラを動かせます。

同じくマイクロソフトでは、LINQのデモをやっていました。
マイクロソフトの人が一対一でLINQとその開発環境とかの説明をしてくれて、すごいラッキーでした。
ただ、性能評価の結果とかないか聞いてみたところまだ in progress で出せるものがないとのことでした。
出してないというよりはHadoop とかと比較して同等までに達してないから発表できないのではとか勝手に予想。

そういえば、自分たち筑波大学 計算科学研究センター(CCS)の紹介を忘れていました。
しかもあまり写真を撮っていない、、、
CCSブースではT2Kを使った気象、物理、宇宙、素粒子などの色々な研究紹介や、
HPCS研究室でやっている研究の紹介をしていました。

そして、SCといえば Top500 が発表される学会として有名です。
あの「2位じゃだめなんでしょうか?」の発言でスパコンの順位が注目を浴びるようになりましたが、
その順位を発表しているのがこのscの Top500 です。
その場に参加できるなんて貴重な体験。

今年の一位は中国のスパコン。
日本では東工大のTsubame 2.0 が4位を取りました。
Tsubame 2.0 はGreen 500という省エネ性能を競うランキングでも2位
という結果を出していて素晴らしい。
Green 500のトップのIBMのマシンはまだ工場にあって出荷されてなく、
実稼働中のスパコンとしは一位なのに、ランキングでは2位になってしまって残念。

といった感じで、scの展示と top500 の様子を紹介しました。
これだけだとかなりお祭り的というか遊んでいるんじゃないかと思われるかもしれませんが、
裏では朝10時から18時くらいまで、分野別に論文発表が並列で行われ
僕ら学生の参加の目的は、CCSのブースの紹介もありますが、
自分の分野の発表を聞くというのがメインで、僕はストレージ関係の発表を主に見てきました。
論文などに関しては次回の記事で書こうと思っています。

Hadoopのジョブのパフォーマンスチューニング

Hadoop 0.21ではCounterでGCに使っている時間が見れるようになりました。
こんな感じです。

この例では5秒程度ですが、ジョブによってはもっとGCに時間を使っている場合があり、
もっと詳細を調べてチューニング出来ないかという話です。
まずはGCのログを取ります。

  <name>mapred.child.java.opts</name>
  <value>-Xloggc:/tmp/hadoop-mikami/@taskid@.gc -Xmx1024m</value>

このように-Xloggc で指定した場所にログを取れます。
@taskid@ には attempt_201010311624_0037_m_000000_0 みたいな感じでattempt_id が入ります。

以下が先程のジョブのあるMapタスクでのGCログです

0.164: [GC 3072K->416K(8896K), 0.0020670 secs]
0.307: [GC 3488K->680K(11968K), 0.0040630 secs]
0.547: [GC 6824K->1161K(11968K), 0.0028070 secs]
0.796: [GC 7305K->1353K(18112K), 0.0025450 secs]
3.100: [GC 670921K->667105K(683904K), 0.0209300 secs]
3.121: [Full GC 667105K->666928K(711872K), 0.0387990 secs]

一行目を見ると、GCでオブジェクトサイズを 3072K から 416K に落としていることが分かりますが、
全体のヒープサイズが 8896K となっています。
そして、GCが発生するたびにヒープサイズを増やしていくようですが、
ヒープは1G設定しているのに、なぜ最初は 8896K しか使ってないのかと思ったら、
ヒープの設定には、ヒープの最大サイズ -Xmx とヒープの初期値 -Xms がある。
初期値をもっと上げた方が良いということで512mに設定。

  <name>mapred.child.java.opts</name>
  <value>-Xloggc:/tmp/hadoop-mikami/@taskid@.gc -Xmx1024m -Xms512m</value>

この設定で先程のGCログを出したのと同じジョブを実行したところ、GC が発生しなくなりました。
しかも、カウンターではジョブ全体でもGCでは5秒ほどしかかかってないことになっていましたが、
実際にはジョブの実行時間が30秒ほど速くなりました。(全体の実行時間は500秒ほど)

次に、ヒープのメモリ使用状況を調べたいということもあるでしょう。
HPROFを試してみたところ出来ました。(参考サイト)
ただ、当たり前ですがジョブの実行時間はかなり遅くなります。
mapred.child.java.opts に -agentlib:hprof=file=/tmp/hprof/@taskid@.hprof,format=b を追加します。
そして、書き出させれたファイルに、

% jhat attempt_201011020939_0028_m_000000_0.hprof
Reading from attempt_201011020939_0028_m_000000_0.hprof...
Dump file created Tue Nov 02 19:34:35 JST 2010
Snapshot read, resolving...
Resolving 278422 objects...
Chasing references, expect 55 dots.......................................................
Eliminating duplicate references.......................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

これでポート7000番でアクセスするとGUIでサマリーが見れます。

このようにHadoopというかjavaの話になりましたが、ヒープダンプも取ることが出来ました。

Hadoop 0.21でのHDFSの変更点

8/23にリリースされた0.21ですが、多くの変更点があり、特徴的な部分がclouderaのブログで紹介されています。
What’s New in Apache Hadoop 0.21
その中でもHDFSの変更点を紹介していきます。リリースノートはこちら

support appends
append は0.19.0 で導入された後に安定性の問題で0.19.1からは無効になっていましたが、0.21.0で新しい実装(HDFS-265)が導入されました。HDFS-265にはappendDesign3.pdfというファイルにappendやHfulshのデザインの詳細がドキュメント化されているので、合わせて読んでおきたい所です。

new filesystem API
FileContextと呼ばれる新しいAPIが導入されました。これの説明はこのスライドがわかりやすいと思います。
HDFS以外の複数のファイルシステムをアプリケーションから扱いやすくするためのものです。
しかし、MapReduceではまだこのAPIは使われてないようです。
古いfilesystem APIではサポートされていない機能も使えるようになっており、シンボリックリンク(HADOOP-6421, HDFS-245)が使えるようになっています。

secondary namenode has been deprecated
セカンダリネームノードがdeprecatedになって、checkpoint node か、backup node (HADOOP-4539)を代わりに使うようになりました。
checkpoint nodeは基本的にはセカンダリネームノードと同じ。backup nodeは常にネームノードと同じ状態をメモリ内とディスクに書き込みます。
今まではセカンダリーネームノードでは定期的に状態を保存していただけなので、最新の状態を保存するにはネームノードでNFSをマウントしてそこにもメタデータを書き出す必要があったのですが、backup nodeを使えばその必要はなくなります。
しかし、backup nodeで完全なHAが達成されるわけではありません。backup nodeはブロックレポートを受け取らないので、backup node へ切り替えるときに、全てのブロックレポートを受け取ってブロックが壊れたりしてないかを確認してからでないとリスタートできません。
例えば、2000万ファイル、4000万ブロックがある状態でのbackup nodeからのリスタートは30分かかります。(参照 ※pdfファイル)

pluggable block placement(HDFS-385)
ブロック(レプリカ)の配置のポリシーを変更できるようになりました。一つ目はローカルに書いて、二つ目は違うラックのノードで、三つ目はその違うラックの他のノードに書くってやつですね。普通は変更する必要はないんですが、特殊な構成のクラスタを使ってる場合は有効かもしれないです。

Distributed RAID filesystem (HDFS-503)
erasure coding を使ってレプリカ3と同等の耐故障性をレプリカ2で実現できる。使用するディスク容量を減らすことができる。
性能がどうなるのかも気になるところ。書き込むデータ量が減るので書き込みは速くなりそう。一方で読み込みは単純に読み込むだけじゃなくなるので遅くなる?ちょっとerasure codeを理解していないので的外れなことを言ってるかもしれませんが、気になります。

SwoPP2010で発表してきました

8/3 – 8/5の3日間、並列/分散/協調処理に関するサマー・ワークショップ(SWoPP 2010)のために金沢へ来ています。
初日に自分の研究を発表してきました。
発表スライドはこちら

内容はスライドを見ていただくとして、あった質問は、

Q: 実際には耐故障性のため複製が使われるが、今回の評価では複製なしの評価で、単にローカルディスクの性能を図っているだけに見えるが、複製は考慮していないのか?

A: もちろん考慮する必要はあります。Gfarmも複製の作成が可能です。しかし、今回複製ありの評価を出していない理由は、HDFSで複製を作成する時は、複製を作成するノード全てにデータを転送し終わってからcloseするが、Gfarmの場合は一つめを書いて、closeしてから非同期でレプリカを作成する。
Gfarmの場合、ジョブが終了してもレプリカの作成が完了していないことになる。
この状況で比較するのはファアではなく、何かしら同じ条件での比較が必要だが、まだそれを考えられていない。
今回は両者の基本的な性能を見るために、あえて複製なしでの評価をとって詳しくみたかった。

Q: ソートの性能においてGfarmが遅かったりするが、アプリケーションの性質による性能への影響の関係はわかったいるのか?
(正確な質問を思い出せないが、このような趣旨だったかと)

A: ソートでは Mapタスク側でGfarmがHDFSより遅い。つまり読み込みで遅いことになるが、単純な並列読み込み性能はほぼ同等だったので、アプリケーションになると負けてしまう理由はわかってない。プラグイン側の改良で同等までいけるかもしれない。
Reduceではデータの書き出し処理がメインになるが、この部分ではGfarmの方が速くて、これは並列書き込み性能がGfarmの方が速いという結果と合っているので妥当だと考えられる。

Gfarmのインストール

centos のマシンに0から最新版のGfarmをインストールしたメモ.

まずは postgres を入れる
XML拡張属性を利用する前には8.3以降が必要.postgres の前にlibxml2を入れる必要がある

$ yum install libxml2 libxml2-devel
$ wget ftp://ftp2.jp.postgresql.org/pub/postgresql/source/v8.4.4/postgresql-8.4.4.tar.gz
$ tar zxfp postgresql-8.4.4.tar.gz
$ cd postgresql-8.4.4
$ ./configure -prefix=/usr/local/pgsql-8.4.4 --with-libxml --without-readline --without-zlib
$ make
$ make install

次にGfarmをインストール

$ wget http://sourceforge.net/projects/gfarm/files/gfarm_v2/2.4.0/gfarm-2.4.0.tar.gz/download
$ tar zxfp gfarm-2.4.0.tar.gz
$ cd gfarm-2.4.0
$ yum install openssl-devel openldap-devel
$ ./configure --prefix=/usr/local/gfarm-2.4.0 --enable-xmlattr --with-postgresql=/usr/local/pgsql-8.4.4
$ make
$ make install

次から色々と設定をしていきます.gfarmとpostgresのパスを通しておきます.
.bashrcに追記して source .bashrc しておく

export GFARM_HOME=/usr/local/gfarm-2.4.0
export PGSQL_HOME=/usr/local/pgsql-8.4.4
export PATH=$PATH:/usr/java/default/bin:$GFARM_HOME/bin:$PGSQL_HOME/bin

次にgfarm-configやろうとしたけど,postgres ユーザを作っておく必要があるみたい
ここを参考して

useradd postgres
passwd postgres
su - postgres

んで postgres の方の.bashrc に

export PGSQL_HOME=/usr/local/pgsql-8.4.4
export PATH=$PATH:$PGSQL_HOME/bin
export MANPATH=$MANPATH:$PGSQL_HOME/man
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PGSQL_HOME/lib
export PGDATA=$PGSQL_HOME/data

postgres を初期化しておく

$ exit
$ mkdir /usr/local/pgsql-8.4.4/data
$ chown postgres:postgres /usr/local/gfsql-8.4.4/data
$ su - postgres
$ initdb --encoding=UTF8 --no-locale

そして postgres のユーザ名を指定してconfig-gfarm

$ config-gfarm -u postgres

次にスレーブサーバの設定。通常メタデータサーバとスレーブはわけるけど、今回は同じノードでインストール。
認証の設定で、_gfarmfsユーザーを作成し、そのホームディレクトリに認証鍵をおく。
以下の例では鍵の期限は約1年(31,536,000秒)に設定。

$ useradd -c "Gfarm gfsd" _gfarmfs
$ su _gfarmfs
$ gfkey -f -p 31536000

次にデータを実際に保存するディレクトリを指定してconfig-gfsd

$config-gfsd /mnt/gfarm
created /mnt/gfarm
created /etc/init.d/gfsd
config-gfsd success
Please ask admin_user to register your host by the following command:
/usr/local/gfarm-2.4.0/bin/gfhost -c -a x86_64-fedora8-linux -p 600 -n 2 ip-10-202-45-139.ec2.internal
After that, start gfsd by the following command as a root:

スレーブの登録をするように言われるので、登録してから起動

$ /usr/local/gfarm-2.4.0/bin/gfhost -c -a x86_64-fedora8-linux -p 600 -n 2 ip-10-202-45-139.ec2.internal
$ service gfsd start

そしてgfdfで確認すると約400GB利用可能になってます。

$ gfdf
     1K-blocks          Used         Avail Capacity       Files
     411437600        203160     411234440     0%             2

けっこうステップが多いのですが、最近はdebian のパッケージにも入ったようです。
http://packages.debian.org/ja/source/sid/gfarm

あとはマウントして利用するには gfarm2fs をインストールする必要がありますが、それは次回以降ということで

分割可能なLZO圧縮をhadoopで使う

Twitterでは基本的にファイルはLZO圧縮しているようで,

などのメリットがあると言っています.これは使わない手はないということで試してみました.

clouderaのこのブログ記事を参考にして進めます.
code.google.com/p/hadoop-gpl-compressionもありますが,Twitterが公開している分割可能なのを使います.
http://github.com/kevinweil/hadoop-lzo

今回の環境はclouderaのamiをベースにしました.
cloudera-ec2-hadoop-images/cloudera-hadoop-fedora-20090623-x86_64 ami-2359bf4
CDH3で,hadopoのバージョンはhadoop-0.20.2+228
まずlzoはGPLライセンスでhadoopに含めることが出来ないので自分でとってくる必要があります.
yumでlzoのライブラリと,コマンドで圧縮解凍できるようにlzopも入れておきます.

$ yum install -y lzo-devel lzop

次にhadoop-lzoをとってきて,ビルド用にantも入れておく

$ git clone git://github.com/kevinweil/hadoop-lzo.git
$ yum install -y ant

ここを参考にしてビルド
パスの設定とlzoライブラリがインストールされてるか確認

$ export JAVA_HOME=/usr/java/default
$ export CFLAGS=-m64
$ export CXXFLAGS=-m64
$ ls -l /usr/lib*/liblzo2*
lrwxrwxrwx 1 root root     16 Jul  8 05:23 /usr/lib/liblzo2.so -> liblzo2.so.2.0.0
lrwxrwxrwx 1 root root     16 Jul  8 05:23 /usr/lib/liblzo2.so.2 -> liblzo2.so.2.0.0
-rwxr-xr-x 1 root root 130876 Aug 22  2007 /usr/lib/liblzo2.so.2.0.0
lrwxrwxrwx 1 root root     16 Jul  8 05:23 /usr/lib64/liblzo2.so -> liblzo2.so.2.0.0
lrwxrwxrwx 1 root root     16 Jul  8 05:23 /usr/lib64/liblzo2.so.2 -> liblzo2.so.2.0.0
-rwxr-xr-x 1 root root 124504 Aug 22  2007 /usr/lib64/liblzo2.so.2.0.0

そしてビルド

$ cd hadoop-lzo
$ ant compile-native tar
/usr/bin/build-classpath: error: Could not find xml-commons-apis Java extension for this JVM
/usr/bin/build-classpath: error: Some specified jars were not found
Buildfile: build.xml
...
    [javac] 12 warnings

compile-native:
    [mkdir] Created dir: /root/work/hadoop-lzo/build/native/Linux-amd64-64/lib
    [mkdir] Created dir: /root/work/hadoop-lzo/build/native/Linux-amd64-64/src/com/hadoop/compression/lzo

BUILD FAILED
/root/work/hadoop-lzo/build.xml:235: Problem: failed to create task or type javah
Cause: the class org.apache.tools.ant.taskdefs.optional.Javah was not found.
        This looks like one of Ant's optional components.
Action: Check that the appropriate optional JAR exists in
        -/usr/share/ant/lib
        -/root/.ant/lib
        -a directory added on the command line with the -lib argument

Do not panic, this is a common problem.
The commonest cause is a missing JAR.

This is not a bug; it is a configuration problem

おやっ、何かがおかしいです。
しばらく経っても解決しそうにないから自分で解決しないと.
antのJNI用のオプションタスクがないらしい.

$ yum install ant-nodeps
$ ant compile-native tar

これで成功した.yumで入れたantは1.70だけど,1.8以降がいいって話もどこかにあったので,ソースから入れた方が良いのかも.
そして jar ファイルをclasspathの通っている所へ, nativeライブラリを java.libray.path の通ってるところへ置く.
JAVA_LIBRARY_PATHを設定しろって話もあるけど,今回の環境では$HADOOP_HOME/lib/native/Linux-amd64-64 以下におけばnativeライブラリの読み込みは問題なかった.

$ tar -cBf - -C build/hadoop-lzo-0.4.4/lib/native . | tar -xBvf - -C /usr/lib/hadoop/lib/native
$ cp build/hadoop-lzo-0.4.4.jar /usr/lib/hadoop/lib/

core-site.xmlに

<property>
 <name>io.compression.codecs</name>
 <value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.ha\
doop.io.compress.BZip2Codec</value>
</property>
<property>
 <name>io.compression.codec.lzo.class</name>
  <value>com.hadoop.compression.lzo.LzoCodec</value>
</property>

LZOはもともとは分割不可なので,分割を可能にするためにはインデックスを付ける必要がある.

普通に1プロセスでインデックスするなら

$ hadoop jar /usr/lib/hadoop/lib/hadoop-lzo-0.4.4.jar com.hadoop.compression.lzo.LzoIndexer big_file.lzo

hadoopのジョブとしてやるなら

$ hadoop jar /usr/lib/hadoop/lib/hadoop-lzo-0.4.4.jar com.hadoop.compression.lzo.DistributedLzoIndexer big_file.lzo

実行すると big_file.lzo.index っていのが生成されて,LzoTextInputFormat で読み込んだ時にこのインデックスファイルを見て分割してくれるらしい.
実際に試してみる.teragenで1Gのファイルを作って圧縮してHDFSにおいて,それにインデックスをつけようとしてみる.

$ hadoop jar /usr/lib/hadoop/hadoop-0.20.2+228-examples.jar teragen 10000000 1G
...
$ hadoop fs -copyToLocal 1G/part-00000 .
$ lzop part-00000
$ hadoop fs -copyFromLocal part-00000.lzo 1G.lzo
$ hadoop jar /usr/lib/hadoop/lib/hadoop-lzo-0.4.4.jar com.hadoop.compression.lzo.LzoIndexer 1G.lzo
10/07/09 09:06:57 INFO lzo.GPLNativeCodeLoader: Loaded native gpl library
10/07/09 09:06:57 INFO lzo.LzoCodec: Successfully loaded & initialized native-lzo library [hadoop-lzo rev 5c25e0073d3dae9ace4bd9eba72e4dc43650c646]
10/07/09 09:06:57 INFO lzo.LzoIndexer: LZO Indexing directory lzo...
10/07/09 09:06:57 INFO lzo.LzoIndexer:   [INDEX] LZO Indexing file hdfs://localhost:9000/user/hadoop/1G.lzo, size 0.18 GB...
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
        at com.hadoop.compression.lzo.LzopInputStream.readInt(LzopInputStream.java:92)
        at com.hadoop.compression.lzo.LzopInputStream.readHeaderItem(LzopInputStream.java:103)
        at com.hadoop.compression.lzo.LzopInputStream.readHeader(LzopInputStream.java:189)
        at com.hadoop.compression.lzo.LzopInputStream.<init>(LzopInputStream.java:55)
        at com.hadoop.compression.lzo.LzopCodec.createInputStream(LzopCodec.java:70)
        at com.hadoop.compression.lzo.LzoIndex.createIndex(LzoIndex.java:224)
        at com.hadoop.compression.lzo.LzoIndexer.indexSingleFile(LzoIndexer.java:117)
        at com.hadoop.compression.lzo.LzoIndexer.indexInternal(LzoIndexer.java:98)
        at com.hadoop.compression.lzo.LzoIndexer.indexInternal(LzoIndexer.java:86)
        at com.hadoop.compression.lzo.LzoIndexer.index(LzoIndexer.java:52)
        at com.hadoop.compression.lzo.LzoIndexer.main(LzoIndexer.java:137)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.hadoop.util.RunJar.main(RunJar.java:186)

おやっ、何かがおかしいです。
Successfully loaded & initialized native-lzo library なのに,,,
結局原因はまだわかっていませんが,mapの出力や結果の出力に使うのには問題なく使えた.

mapの中間出力にLZOを使うなら,

<property>
  <name>mapred.compress.map.output</name>
  <value>true</value>
</property>
<property>
  <name>mapred.map.output.compression.codec</name>
  <value>com.hadoop.compression.lzo.LzoCodec</value>
</property>

結果の出力に使うにはプログラム内で,

FileOutputFormat.setOutputCompressorClass(job,com.hadoop.compression.lzo.LzopCodec.class);
FileOutputFormat.setCompressOutput(job, true);

mapred-site.xml にmapred.output.compress:ture と mapred.output.compression.codec:com.hadoop.compression.lzo.LzoCodec を書けば出来るのかなと思ったけど,なぜか無視された.

ということで,一応hadoop-lzo使えましたが,インデックス作成ができず.
どなたか,インデックス作成出来た方教えてください...

– 追記[2010/07/12] –
ファイル名が3文字以下の時にこのエラーが発生することがわかりました.それ以外の時は発生しません.
lzopで圧縮する前のファイル名が3文字以下の時です.
実は上記の手順はエラーを発生した時と少し違っていたようで,上記の手順ではエラーはでません.

$ hadoop jar /usr/lib/hadoop/hadoop-0.20.2+228-examples.jar teragen 10000000 1G
...
$ hadoop fs -copyToLocal 1G/part-00000 1G
$ lzop 1G
$ hadoop fs -copyFromLocal 1G.lzo .
$ hadoop jar /usr/lib/hadoop/lib/hadoop-lzo-0.4.4.jar com.hadoop.compression.lzo.LzoIndexer 1G.lzo

このような手順だと発生してしまいます.
これはバグっぽいので issues へ報告しました. http://github.com/kevinweil/hadoop-lzo/issues/issue/3