Asakusa on Sparkの開発環境構築


この記事は[Asakusa Framework Advent Calendar 2015](http://www.adventar.org/calendars/1166)の9日目です。

入門から先の記事はひしだまさんが大量に書いているので、ここでは箸やすめ的な入門記事を書いてみようと思います。

## Asakusa on Sparkとは

Hadoop環境で実績のあるAsakusaFrameworkをSpark上でも動かせるようにしたものです。現在ベータ版でプロダクションで使うのをおすすめしませんと書いてありつつ、実際の稼働事例が出ていたりしますね。

* [Asakusa on Spark](http://docs.asakusafw.com/preview/ja/html/asakusa-on-spark/index.html)
* [原価計算への適用事例](http://www.publickey1.jp/blog/15/post_99.html)

説明を読むとAsakusaFrameworkはHadoopでもSparkでもどちらでも同じDSLで良いとあります。AsakusaやSparkを触ったことはないのですが、いきなり新しいSparkで開発を始めることにします。

以降の作業の前提として、Hadoop/Sparkの知識はなくとも構いません。Javaが動作して、環境変数JAVA_HOMEが設定してあることが条件です。

私の実行環境は、OSX(Capitan)、Java 1.8.0_65でした。

## まず最初の落とし穴

AsakusaFrameworkの開発環境としては、[Jinrikisha](http://docs.asakusafw.com/jinrikisha/ja/html/)があります。スターターパッケージと書いてあり、その名の通りこれをインストールすればHadoopもAsakusaもEclipseも入るというすぐれものです。

**が**

現時点においてJinrikishaはAsakusa on Sparkに対応していません。いろいろやればできる（できた）のですが、私自身はIntelliJユーザーということもあり、無理にここからスタートする必要もないので、今回はJinrikishaを使いません。

## Hadoopのインストール

まずはHadoopをインストールしましょう。Sparkにhadoop同梱のパッケージもあるのですが、AsakusaFrameworkのジョブコントローラーである[YAESS](http://docs.asakusafw.com/latest/release/ja/html/yaess/user-guide.html)はhadoopコマンドを使います。これはHadoopディストリビューションに含まれているので、(SparkではなくHadoopを)ちゃんと入れることにします。
インストールは、[hadoopサイト](http://hadoop.apache.org/releases.html)からバイナリアーカイブをダウンロードして、適当なディレクトリに展開します。

展開が終わったら、インストールのテストをしてみます。
Hadoopをインストールしたディレクトリに移動し、
(https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html) のスクリプトを実行します。

```bash
  $ mkdir input
  $ cp etc/hadoop/*.xml input
  $ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.1.jar grep input output 'dfs[a-z.]+'
  $ cat output/*
```

`1 dfsadmin` と表示されると思います。

好みですが、私はバージョン付きのディレクトリに対してシンボリックリンクを貼ります。

```bash
  $ ln -s hadoop-2.7.1 hadoop
```


## Sparkのインストール

次にSparkをインストールします。Hadoop同様に[ダウンロードサイト](http://spark.apache.org/downloads.html)からダウンロードしますが、幾つか選択肢がありますが、下記を選択します。

* Spark Release: **1.5.2** を選択
* Package Type: **Pre-Built for Hadoop 2.6 and later** を選択

Hadoopを既にインストールしているので、 **Pre-build with user-provided Hadoop** でもいいような気がしますが、かなりハマったのでここではHadoop同梱版を使います。

ダウンロードしたアーカイブを先ほどHadoopを展開したあたりに展開します。展開後、動作確認をしましょう。

```bash
  $ ./bin/spark-shell
```

http://spark.apache.org/docs/latest/quick-start.html にあるスクリプトを幾つか実施してみます。

正しく動作したら、hadoop同様シンボリックリンクを貼ります。

```bash
  $ ln -s spark-1.5.2-bin-hadoop2.6 spark
```

## Asakusa Gradle Pluginのインストール

[Asakusa Gradle Plugin](http://docs.asakusafw.com/latest/release/ja/html/application/gradle-plugin.html)は、AsakusaFrameworkを使ったアプリケーションのビルドをgradleから行えるようにしたものです。

ちょうど[サンプルプロジェクト](http://www.asakusafw.com/download/gradle-plugin/asakusa-example-project-0.7.6.tar.gz)もあるので、ダウンロードしてhadoop / sparkと同じように展開します。

### build.gradleの修正

このサンプルプロジェクトはAsakusa on Hadoop1の前提で作られているので、Asakusa on Spark用に修正します。修正箇所は

* 7行目: asakusa-spark-gradleプラグインを依存関係に追加
* 19行目: asakusafw-sparkプラグインの有効化
* 22行目: asakusafwVersionの修正
* 43行目: hadoop-clientのバージョンの修正

です。

なお、`apply plugin: 'idea' ` を追記することで、IntelliJ用のプロジェクトファイルを生成できます。

```gradle:build.gradle
buildscript {
    repositories {
        maven { url 'http://asakusafw.s3.amazonaws.com/maven/releases' }
    }
    dependencies {
        //classpath group: 'com.asakusafw', name: 'asakusa-gradle-plugins', version: '0.7.6'
        classpath group: 'com.asakusafw.spark', name: 'asakusa-spark-gradle', version: '0.2.1'
    }
}

task wrapper(type: Wrapper) {
    distributionUrl 'http://services.gradle.org/distributions/gradle-2.8-bin.zip'
    jarFile file('.buildtools/gradlew.jar')
}

apply plugin: 'asakusafw'
apply plugin: 'asakusafw-organizer'
apply plugin: 'eclipse'
apply plugin: 'asakusafw-spark'

asakusafw {
    asakusafwVersion '0.7.6-hadoop2'

    modelgen {
        modelgenSourcePackage 'com.example.modelgen'
    }
    compiler {
        compiledSourcePackage 'com.example.batchapp'
    }
}

asakusafwOrganizer {
    profiles.prod {
        asakusafwVersion asakusafw.asakusafwVersion
    }
}

dependencies {
    compile group: 'com.asakusafw.sdk', name: 'asakusa-sdk-core', version: asakusafw.asakusafwVersion
    compile group: 'com.asakusafw.sdk', name: 'asakusa-sdk-directio', version: asakusafw.asakusafwVersion
    compile group: 'com.asakusafw.sdk', name: 'asakusa-sdk-windgate', version: asakusafw.asakusafwVersion

    provided (group: 'org.apache.hadoop', name: 'hadoop-client', version: '2.7.1') {
        exclude module: 'junit'
        exclude module: 'mockito-all'
        exclude module: 'slf4j-log4j12'
    }
}
```

### 環境変数の設定

AsakusaFrameworkを動かすのに必要な環境変数を設定します。`.asakusa_profile` ファイルに定義して、`source`するか、起動時に読み込むと良いでしょう。

```bash:.asakusa_profile
export ASAKUSA_DEVELOP_HOME=[your woking directory]
export ASAKUSA_HOME=${ASAKUSA_DEVELOP_HOME}/asakusafw
export HADOOP_CMD=${ASAKUSA_DEVELOP_HOME}/hadoop/bin/hadoop
export HADOOP_CLIENT_OPTS=-Xmx512m
export PATH=$JAVA_HOME/bin:$PATH:${ASAKUSA_DEVELOP_HOME}/hadoop/bin:${HIVE_HOME}/bin:$ASAKUSA_HOME/yaess/bin:$PATH
export HADOOP_OPTS="-Dorg.xerial.snappy.tempdir=/tmp -Dorg.xerial.snappy.lib.name=libsnappyjava.jnilib $HADOOP_OPTS"
```

上記で設定した、`ASAKUSA_HOME` にasakusaFrameworkがインストールされます。

### AsakusaFrameworkのインストール

gradleを使ってAsakusaFrameworkのインストールを行います。

```bash
  $ ./gradlew installAsakusaFw
```

gradleとAsakusaFrameworkがインストールされます。

### AsakusaFramework 設定
AsakusaFrameworkにSparkの`spark-submit`コマンドの場所を設定します。

```bash
  $ cd $ASAKUSA_HOME
  $ echo "export SPARK_CMD=[Sparkをインストールしたディレクトリ]/bin/spark-submit"
```

## ビルドとデプロイ

さて、サンプルアプリケーションをビルドします。

```bash
  $ cd [サンプルプロジェクトホーム]
  $ ./gradlew assemble
```

ビルドが終わると、`build/spark-batchapps` ディレクトリに `spark.example.summarizeSales` ディレクトリができているはずです。

確認できたら、デプロイを行います。

```bash
  $ cp -r build/spark-batchapps/spark.example.summarizeSales $ASAKUSA_HOME/batchapps/
```

以上でAsakusaのSpark用バッチを実行する準備ができました。

## サンプルデータの配置と実行

実行自体の手順は、Asakusa on Hadoopと変わりません。詳細な説明は(http://docs.asakusafw.com/latest/release/ja/html/introduction/start-guide.html#startguide-running-example) に記載されています。ここではコマンドのみ転載します。

### サンプルデータの配置

```bash
# スタンドアロンモードに対応するため、ホームディレクトリに移動しておく
  $ cd ~
# ファイルシステムパス上のデータをクリアしておく
  $ hadoop fs -rmr target/testing/directio
# サンプルデータを配置する
  $ hadoop fs -put [サンプルプロジェクトホーム]/src/test/example-dataset/master target/testing/directio/master
  $ hadoop fs -put [サンプルプロジェクトホーム]/src/test/example-dataset/sales target/testing/directio/sales
```

### YAESSの実行
```bash
$ASAKUSA_HOME/yaess/bin/yaess-batch.sh example.summarizeSales -A date=2011-04-01
```

わーっとログがでますが、最後に SUCCESSと表示されたら無事実行完了です。

### 結果の確認
```bash
  $ hadoop fs -text file:~/target/testing/directio/result/category/result.csv
```

これでAsakusa on Sparkの開発環境ができました。Asakusaの開発をエンジョイしましょう。

## おわりに

Asakusa on Sparkはベータということもあり、Getting Startedのようなドキュメントがなかったのでやってみましたが、思いの他苦労しました。このエントリではすんなり行く手順のみ書いてありますが、未解決の課題もあります。

1. ~~IntelliJのGradleプラグインに環境変数を設定する方法がわからず、IntelliJからAsakusaビルドが動かない。~~ (aoetkさんありがとうございます)
2. Hadoop同梱版のSparkを使わないと、snappyのネイティブライブラリが見つからないエラーが出る
3. 環境変数 HADOOP_OPTSを設定しないと、snappyのネイティブライブラリが見つからないエラーが出る

単に解決方法が見つからなかっただけですので、解決したらエントリを修正することにしたいと思います。

なおここまで書いておいてなんですが、AsakusaDSLは再コンパイルだけでSparkで動きますので、AsakusaFrameworkに慣れるだけであれば、Jinrikishaが楽でいいと思います(^^;;