HDFSのラッパー

作りかけの何かが出てきたのでサルベージしておく
ぶっちゃけ、$ hadoop dfs ~ をPHPのコマンド風に呼び出せるようにしてみただけ
※ で、execしている

たしか途中でいらなくなったので、作ってる途中でお蔵入りしたはず
めっちゃ遅い

<?php
class HadoopWrapper
{
/**
* ファイルの内容を取得する
*
* @param unknown $filename
*/
function file_get_contents($filename)
{
$cmd = sprintf(" -cat %s", self::getPath($filename));
$ret = self::exec($cmd);
return (join("\n", $ret["stdout"]));
}
/**
* ファイルの内容を取得する
*
* @param unknown $filename
*/
function file_put_contents($filename, $data, $flags = 0)
{
$tempname = $filename;
{ // テンポラリにディレクトリを作成
$dirname = sys_get_temp_dir() . "/" . "hadoop";
@mkdir($dirname);
@chmod($dirname, 0777);
}
{ // テンポラリにファイルを作成
$tempname = tempnam($dirname, 'dfs');
$tempanme = md5($filename);
@chmod($tempname, 0777);
}
{ // dfs上のファイルをローカルにコピーする
@unlink($tempname);
$cmd = sprintf(" -get %s %s", self::getPath($filename), $tempname);
$ret = self::exec($cmd);
}
// ローカルのファイルを編集
//  複数プロセスから書き込まれる可能性があるので排他制御を行う
file_put_contents($tempname, $data, $flags | LOCK_EX);
{ // 編集した結果のファイルをdfsに上げ直す
// はじめに元のファイルを削除する
$ret = self::unlink($filename);
// 改めてアップロードする
$cmd = sprintf(" -put %s %s", $tempname, self::getPath($filename));
$ret = self::exec($cmd);
// 使い終わったファイルは消しておく
// @unlink($tempname);
return $ret;
}
}
function unlink($filename)
{
$cmd = sprintf(" -rm %s", self::getPath($filename));
$ret = self::exec($cmd);
}
/**
* ファイルの存在を確認する
*
* @param string $filename
*          チェックしたいファイル名
* @return boolean
*/
function file_exists($filename)
{
$cmd = sprintf(" -test -e %s", self::getPath($filename));
$ret = self::exec($cmd);
return ($ret["status"] == 0 ? true : false);
}
/**
* ディレクトリを作成する
* デフォルトの動作で、再帰的にディレクトリを作成する
*
* @param unknown $pathname
* @return boolean
*/
function mkdir($pathname)
{
$pathname = self::getPath($pathname);
// 既にファイルが存在すれば、何もしない
if (self::file_exists($pathname)) {
return (true);
}
$cmd = sprintf(" -mkdir %s", self::getPath($pathname));
$ret = self::exec($cmd);
}
/**
* パーミッションを変更する
*  デフォルトの動作で、再帰的に変更する
* @param unknown $filename
* @param unknown $mode
* @return boolean
*/
function chmod($filename, $mode=0777)
{
$cmd = sprintf(" -chmod -R %o %s", $mode, self::getPath($filename));
$ret = self::exec($cmd);
return (true);
}
/**
* ファイル名が"/"から始まっていなければ
* フルパスに変更する
*
* @param unknown $filename
* @return unknown
*/
function getPath($filename)
{
if (! preg_match("&^/&", $filename)) {
$filename = sprintf("%s/%s", HDF_DIR, $filename);
}
return ($filename);
}
function exec($cmd)
{
$output = "";
$return = 0;
exec("hadoop dfs $cmd", $output, $return);
return (array(
"stdout" => $output,
"status" => $return
));
}
}

hBaseのインストール

hBaseをインストール使用としたところ、先にインストールしたHadoopとバージョンが合わずに断念。
再度Hadoopをインストールした。[2011-07-14-3]

[HBase] HBase 0.90.0をHadoop上で動作させる方法: Akito’s IT技術 メモ
Hbase インストールメモ – cuspos diary
およそ正しくないHBase導入メモ(0.20.6)

既にHadoopはインストール済み
[2011-07-14-3]

インストール

以下のURLから最新版のhBaseをダウンロードする
http://ftp.jaist.ac.jp/pub/apache/hbase/

$ cd /usr/local/src/
$ wget http://ftp.jaist.ac.jp/pub/apache/hbase/stable/hbase-0.90.3.tar.gz
$ tar xzvf hbase-0.90.3.tar.gz
$ mv hbase-0.90.3 /usr/local/hbase
$ cd /usr/local/hbase
$ chown -R hadoop:hadoop /usr/local/hbase

コアライブラリをHadoopのものと一致させる
$ mv lib/hadoop-core-0.20-append-r1056497.jar lib/hadoop-core-0.20-append-r1056497.jar.2011-07-19
$ cp /usr/local/hadoop/hadoop-core-0.20.2+737.jar lib/

設定

$ vi conf/hbase-env.sh

export JAVA_HOME=/usr/java/default/
export HBASE_HOME=/usr/local/hbase

$ vi conf/hbase-sith.xml
hbash.rootdir
hdfs://localhost:9000/hbase
The directory shared by region servers.

起動と終了

起動
$ bin/start-hbash.sh

終了
$ bin/stop-hbash.sh

パスを通しておく

$ vi /etc/profile

export PATH=$PATH:/usr/local/hbase/bin

$ source /etc/profile

トラブルシューティング

以下のエラーは、連携するHadoopとバージョンが合わずに発生した様子
別のバージョンのHadoopをインストールし直すことで解決

ZooKeeper絡みのエラー

シェルにログインして操作をするとZooKeeper関係でエラーが発生する
また、hbaseをストップしても終了せずに、killするしかなくなる

$ hbase shell

% create ‘test’, ‘cf’

ERROR: org.apachh.hadoop.hbash.ZooKeeperConnectionException:
org.apachh.hadoop.hbash.ZooKeeperConnectionException:
org.apachh.zookeeper.KeeperException
ConnectionLossException: KeeperErrorCode = connectionLoss for /hbase

Master絡みのエラー

% create ‘test’, ‘cf’

ERROR: org.apachh.hadoop.hbash.MasterNotRunningException: null

hBaseについて参考になる資料を集めてみた

大体ここからリンクを辿ったり
HBaseとはどんなNoSQLデータベースなのか? 日本語で読める情報を集めてみた - Publickey

Cassandraとh baseの比較して入門するno sql
Apache HBase 入門 (第1回)
Apache HBase 入門 (第2回)
[HBase] HBase 0.90.0をHadoop上で動作させる方法: Akito’s IT技術 メモ
Hadoopをシングルノード上で分散稼動させる方法: Akito’s IT技術 メモ

PerlでHadoopのMap-Reduceを書く

Hadoop Streaming – naoyaのはてなダイアリー

最初Rubyで書いたけど、なれないことで悩みたくなかったのでとりあえずPerlで
例示は、ありがちな「単語の数を数える」

サンプル

処理対象のデータ

$ cat data

aaa bbb ccc ddd
eee fff ggg hhh
aaa bbb ccc ddd

プログラム

$ cat mapper.pl

#!/usr/bin/env perl
# mapper.pl
use strict;
use warnings;
while (<>) {
chomp;
my @fields = split /[\s\r\n]+/;
foreach my $word ( @fields ) {
printf "%s\t%s\n", $word, 1;
}
}

$ cat reducer.pl

#!/usr/bin/env perl
# reducer.pl
use strict;
use warnings;
while (<>) {
chomp;
my @fields = split /[\s\r\n]+/;
foreach my $word ( @fields ) {
printf "%s\t%s\n", $word, 1;
}
}

面倒なので実行用のShellスクリプト

$ cat doHadoop.sh
#!/bin/sh

# 結果のディレクトリを初期化する
echo "upload data file"
hadoop dfs -put $1 $1
echo "delete output dir"
hadoop dfs -rmr $2
echo "start hadoop"
time hadoop jar /usr/local/hadoop/contrib/streaming/hadoop-streaming-0.20.203.0.jar -input $1 -output $2 -mapper $3 -reducer $4
hadoop dfs -get $2/part-00000 part-00000

動かしてみる

$ ./doHadoop.sh data out /path/to/mapper.pl /path/to/reducer.pl
$ cat part-00000
eee 1
hhh 1
ggg 1
bbb 2
fff 1
aaa 2
ccc 2
ddd 2

※ mapperとreducerはフルパスで書かないと怒られるっぽい

hadoopをインストールする

とりあえず、サーバのクラスタ化など難しいことは考えずに一台のみで運用する

hadoop インストールメモ – cuspos diary
Apache Hadoop のインストール

追記:一旦この方法でうまく行ったのだが、hBaseと組み合わせた場合バージョンが合わずに後日やり直し

下準備

はじめにJava SDKのバージョンを上げておく
[2011-07-14-2]

hadoop用のユーザを作成する
$ adduser hadoop
$ passwd hadoop

HadoopへのSSHでのアクセスにパスワードではなく公開鍵を使うよう変更する
※ Hadoopが起動時にアクセスしに行くため
$ su hadoop
$ cd
$ ssh-keygen -t dsa -P ” -f ~/.ssh/id_dsa
$ cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
$ chmod go-rwx ~/.ssh/
$ chmod 600 ~/.ssh/authorized_keys

インストール

以下のURLより安定版を確認する
http://ftp.riken.jp/net/apache//hadoop/core/stable/

$ cd /usr/local/src
$ wget http://ftp.riken.jp/net/apache//hadoop/core/stable/hadoop-0.20.203.0rc1.tar.gz
$ wget http://archivh.cloudera.com/cdh/3/hadoop-0.20.2+737.tar.gz

$ tar zxvf hadoop-0.20.2+737.tar.gz
$ mv /usr/local/src/hadoop-0.20.2+737 /usr/local/hadoop
$ cd /usr/local/hadoop/
$ chown -R hadoop:hadoop /usr/local/hadoop/

設定ファイルの修正

$ mkdir hadoop-datastore
$ vi conf/hadoop-env.sh

export JAVA_HOME=/usr/java/default/

設定ファイルは、ここのまま
hadoop インストールメモ – cuspos diary

$ vi conf/core-sith.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this filh. -->
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
<description>プライマリNameNodeを設定</description>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/local/hadoop/hadoop-datastore</value>
<description>hadoopのデータ保存領域</description>
</property>
</configuration>

ポートを29000から9000に変更

$ vi conf/hdfs-sith.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this filh. -->
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
<description>DataNodeはlocalhostのみなので、レプリケーション数は1</description>
</property>
</configuration>

$ vi conf/mapred-sith.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this filh. -->
<configuration>
<property>
<name>mapred.job.tracker</name>
<value>localhost:9001</value>
<description>JobTrackerを設定</description>
</property>
</configuration>

ポートを29001から9001に変更

$ vi conf/log4j.properties

hadoop.root.logger=INFO, ROLLINGFILE
log4j.rootLogger=${hadoop.root.logger}

初回起動

領域をフォーマットする
$ bin/hadoop namenode -format

Hadoopサーバを起動する
$ bin/start-all.sh

とりあえず何かコマンドを実行してみる
$ bin/hadoop dfs -ls /

Hadoopサーバを終了する
$ bin/stop-all.sh

Hadoopコマンドにパスを通しておく

$ vi /etc/profile

export PATH=$PATH:/usr/local/hadoop/bin

$ source /etc/profile