WindowsでApache HiveとApache Sparkを動かす

気軽に、HiveやSparkで遊ぶのにいいかと思って、MSYS/MinGW で HiveとSparkを動かした。Cygwinを使わずにMSYS (とJDK)だけで動作する。
ここでは、クラスターを作ることは意図せず、シングルノードモードだけで確認しているので、実用性はあまりないかもしれないが、hadoopのバージョンを 2.6.0にすればクラスターも作れる(＊)。

Hive 最新バージョンではないが、GREEさんの[hive-ruby-scripting](https://github.com/gree/hive-ruby-scripting)も動作する 0.12.0 を動かす。Spark は、比較的最新の 1.2.0を動かす(＊＊)。

ローカルで手軽に使えるよう、ベースとなる hadoop は簡単にビルドできる古いバージョンで 0.23.11を使う。ギリギリ hadoop2 だが APIが古くて、YARNには期待が持てない。が、ローカルモードは実に簡単に動作して軽快に利用できる。
もともと数年前からWindowsでPigをローカルモードで動かすのにhadoop 0.23を使っていたもので、Hiveを使う機会に、シングルノードモードのHDFSも動作させた（hadoop 0.23.11でのクラスターモードは未確認）。

(＊) Hadoopについては、[Windows 8.1で Hadoop 2.6.0 をビルドする](http://qiita.com/qtwi/items/a6d282ad49685ce62b64)でビルドした Hadoop 2.6.0 を使えば、まともなYARNで Hiveを使うこともできる。
つまり、気軽にローカルモードで使うにはhadoop 0.23.11が簡単だし、本格的に使うなら、hadoop 2.6.0を選択できる。ここでは、主に hadoop 0.23.11での利用について記述する。

(＊＊) Spark は、より新しい 1.4.0 について、[Spark 1.4.0 の SparkR を動かす](http://qiita.com/qtwi/items/7abcfc53ba9568f20004) でビルドから扱っているので、参考にしてほしい。


最後のSparkは、hadoopを使わずにクラスターが組めるはずなので、実用性を持って使える場面があるだろう。

これらをビルド、実行するためのパッチをそれぞれ作成した。

ビルドが面倒な場合、以下から、Hadoop 2.6.0やそれに対応したHiveなども含め、ビルド済みバイナリを入手できる。

(http://sourceforge.jp/projects/win-hadoop/releases/62852)


## まずは、Hadoopをビルドする
MSYSとJDKだけで動かすことを目標にしたので、MSYS/MinGWではなくて、msysGitでもよい。ただビルドには、プロトコルバッファコンパイラが必要なので、protoc バージョン2.5.0のバイナリ(https://code.google.com/p/protobuf/downloads/detail?name=protoc-2.5.0-win32.zip) をダウンロードして、C:\Windowsフォルダにでもコピーしておく。

Hadoop 2.6.0を使う場合は、ここでのビルド方法は無視して、[こちら](http://qiita.com/qtwi/items/a6d282ad49685ce62b64)を参照すること。Hadoop 2.6.0は、MSYSではなく、Windowsコマンドプロンプトから利用する。

Hadoop Archives (https://archive.apache.org/dist/hadoop/core/hadoop-0.23.11/) からhadoop-0.23.11-src.tar.gzを入手して、展開する。長いパスがあるので、C:\work などのような短い名前の親フォルダにすること。
Hadoop 0.23.11をMSYSでビルドするためのパッチ(https://github.com/qtwi/msys-hadoop-0.23.11/raw/master/patch/hadoop-0.23.11-src-mingw-0002r.patch) を入手して、パッチをあてる。

```
cd hadoop-0.23.11-src
patch -u -p1 < .../hadoop-0.23.11-src-mingw-0002r.patch
```

あとは、Mavenでビルドする。

```
mvn clean package -Pdist -DskipTests
```

ビルドが終了したら、hadoop-dist/target/hadoop-0.23.11にできあがっているので、適当な場所にコピーする。（例えば、C:\AppsやC:\Apacheなどのような）いろいろ手を抜いているので、**すべて、C:ドライブ**でなければならない。

ローカルモードなら、JAVA_HOMEが正しく（**HadoopではJAVA_HOMEに空白文字が使えない**ので c:/Progra~1/Java/jdk1.7.0_75のようなパスである必要がある）設定されていれば、そのまま動作するはず。
シングルノードモードで利用するときは、etc/hadoop/core-site.xmlに、fs.defaultFSとしてhdfs://localhost:9000を設定する。

```
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
  </property>
```

hdfs-site.xml にも dfs.replication を設定する。

```
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
```

通常の場合は、sbin/start-dfs.sh を使うが、今回は利用できないので、ネームノード、データノードをそれぞれ個別のウィンドウで、手作業で起動する。

```
bin/hdfs namenode -format   # 最初の１回だけHDFSのフォーマット
bin/hdfs namenode
```

```
bin/hdfs datanode
```

もっと簡単に常時起動したいなら、シェルスクリプトを使って、

```
#!/bin/bash
start bash -c "$HADOOP_HOME/bin/hdfs namenode"
start bash -c "$HADOOP_HOME/bin/hdfs datanode"
```

のような形で用意すればよいと思う。このほか、終了させるために、kill する必要があるので、pid を保存しておかないといけないけれど。

HDFSの操作は、また別のウィンドウから行うが、MSYSには、奇妙なパス文字列変換特性があり、ルートからのパスを指定するときは、先頭の "/" を二重にする必要がある。

```
bin/hdfs dfs ls //
bin/hdfs dfs -mkdir -p //user/yourname

# サンプルの実行
bin/hdfs dfs -put etc/hadoop input
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-0.23.11.jar grep input output 'dfs[a-z.]+'
bin/hdfs dfs -cat output/\*
```


## 次に、Hiveをビルドする
Hive 0.12.0もアーカイブからソースをダウンロードする。(https://archive.apache.org/dist/hive/hive-0.12.0/)
Hive 0.12.0 をMSYSでビルドするためのパッチ(https://raw.githubusercontent.com/qtwi/msys-hadoop-0.23.11/master/patch/hive-0.12.0-src-mingw-0002r.patch) を入手して、パッチをあてる。

```
cd hive-0.12.0/src
patch -u -p1 < .../hive-0.12.0-src-mingw-0002r.patch
```

Hive 0.12.0は、antでビルドする。

```
ant clean package
```

オリジナルの Hive 0.12.0は、デフォルト Hadoop1でビルドされるが、パッチにより、Hadoop 0.23.11でビルドされる。Hadoop 2.6.0に対して、ビルドするには、以下のようにする。

```
ant clean package -Dhadoop.version=2.6.0 -Dhadoop-0.23.version=2.6.0 -Dhadoop.mr.rev=23
```

ビルド結果は、build/dist にあるので、適当なフォルダにコピーする。

conf/hive-env.shとconf/hive-site.xml を設定する。
ローカルモードで設定するとき、hive-site.xmlに設定する hive.metastore.warehouse.dir は注意がいる。先頭に "/"をつけた"/"区切りのWindowsフルパスである必要がある。くれぐれも、先頭の "/" を忘れないこと。例えば、

```
  <property>
    <name>hive.metastore.warehouse.dir</name>
    <value>/c:/apps/hive-0.12.0/data/user/hive/warehouse</value>
  </property>
```

シングルノードモードであれば、上記は、特に気にする必要も設定する必要もないが、今度は、HDFS上に、 "/user/hive/warehouse" フォルダと /tmp を作成しておく必要がある。

```
# hadoop
bin/hdfs dfs -mkdir -p //user/hive/warehouse
bin/hdfs dfs -mkdir //tmp
```

Hadoop 0.23.11 の場合は、YARNを起動せずに hive を利用できるが、Hadoop 2.6.0 を使う場合は、YARNも起動する必要がある。（Hadoop 2.6.0 は、HDFSもYARNも、管理者権限のWindowsコマンドプロンプトから起動する）

```
# hadoop 2.6.0
start bin\yarn resourcemanager
start bin\yarn nodemanager
```

hive-site.xml にメタデータのフォルダを正確に指定しておくとSparkからSparkQLで使うときに設定が、簡単になる。（Sparkから、hiveのデータを読み込んでSparkで処理したり、HiveQLライクなSparkQLで処理できる）

```
  <property>
    <name>javax.jdo.option.ConnectionURL</name>
     <value>jdbc:derby:;databaseName=c:/apache/hive-0.12.0/data/metastore_db;create=true</value>
  </property>
```

サンプルデータをHDFSにおいてから、hiveのコマンドラインで動作を確認する。

```
# hadoop
bin/hdfs dfs -put ../hive-0.12.0/examples/files/flights_tiny.txt
```

```
# hive
bin/hive --service cli

hive> create table flight (departure string, arrive string, year int, month int, day int, delay float, number int);
hive> load data inpath 'flights_tiny.txt' into table flight;
hive> select count(*) from flight;
```

コマンドラインインターフェイス以外の hwiやhiveserverなども動作するはずなので、試してみてほしい。

hive-ruby-scripting を使うなら、hive-ruby-scripting をビルドして、jarファイルを hiveのlibフォルダにコピーして、JRuby 1.7.4 (http://jruby.org/files/downloads/1.7.4/) をダウンロードして jruby.jar も libフォルダにコピーしておけば、利用が簡単になる。

```
git clone https://github.com/gree/hive-ruby-scripting
mvn clean package
cp hive-ruby-scripting.jar /c/apache/hive-0.12.0/lib

curl -LO https://s3.amazonaws.com/jruby.org/downloads/1.7.4/jruby-bin-1.7.4.tar.gz
tar xzf jruby-bin-1.7.4.tar.gz
cp jruby-1.7.4/lib/jruby.jar /c/apache/hive-0.12.0/lib
```


## 最後に、Apache Spark をビルドする
Spark 1.2.0のソースをダウンロードする。(https://archive.apache.org/dist/spark/spark-1.2.0/spark-1.2.0.tgz)
Spark は、ビルドしたフォルダで、そのまま使うことになるので、hadoopやhiveのバイナリをコピーしたフォルダに展開しておくとよい。
Spark 1.2.0をMSYSで使えるようにするパッチ(https://raw.githubusercontent.com/qtwi/msys-hadoop-0.23.11/master/patch/spark-1.2.0-mingw-0002r.patch) を入手して、パッチをあてる。

```
cd spark-1.2.0
patch -u -p1 < .../spark-1.2.0-mingw-0002r.patch
```

Sparkは、少し長めのオプションをつけて、Mavenでビルドする。

```
export MAVEN_OPTS="-Xmx2g -XX:MaxPermSize=512M -XX:ReservedCodeCacheSize=512m"

mvn package -DskipTests -Phadoop-0.23 -Dhadoop.version=0.23.11 \
  -Pyarn-alpha -Phive -Phive-0.12.0 -Phive-thriftserver
```

hadoop 2.6.0に対してビルドするには以下のようにする。

```
mvm package -DskipTests -Phadoop-2.4 -Dhadoop.version=2.6.0 \
  -Pyarn -Phive -Phive-0.12.0 -Phive-thriftserver
```

ノートPCでのビルドは、30分ぐらいで終了する。

設定は、conf/spark-env.sh にHADOOP_HOMEとHADOOP_CONF_DIRを書く。SparkQLでHiveのデータベースを読み込むなら、HIVE_CONF_DIRを設定するが、hive-site.xml は、Sparkのconfフォルダに作成し、SparkのconfフォルダをHIVE_CONF_DIRに設定する。

```
export HADOOP_HOME=`cd $SPARK_HOME/../hadoop-0.23.11; pwd`
export HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-"$HADOOP_HOME/etc/hadoop"}

export HIVE_CONF_DIR=$SPARK_HOME/conf
```

あとは、spark-shell で動作確認をする。

```
# Spark
bin/spark-shell

scala> val tf = sc.textFile("README.md")
scala> tf.count()

scala> val qc = new org.apache.spark.sql.hive.HiveContext(sc)
scala> qc.sql("SELECT COUNT(*) FROM flight").collect().foreach(println)
```

## まとめ
クラスターを作って、使わないと Hadoopの本来の使い方も性能も期待できないが、純粋に HiveQLやHiveのUDF開発を楽しんだり、あるいは、Sparkを利用するのであれば、Hadoopのように重たいことばかりではないので、単体Windowsでの利用であっても、それなりに実用的な利用方法はあると思う。（まじめにHadoopクラスターを作るには、途中割り込んで記述した、Hadoop 2.6.0を利用すればよい。）

メインをWindows利用しているケースでは、GitのためにmsysGitをインストールしている場合も多いと思うし、Cygwinを使わないことで、かなり気軽にHiveやSparkを利用できると思う。