Spark on Dockerで分散型機械学習を始める - Part 4: AmbariでHadoopとSparkのsingle nodeセットアップ


## このシリーズ

* [Spark on Dockerで分散型機械学習を始める - Part 1: インストール](http://qiita.com/masato/items/fe93157293114999b0d8)
* [Spark on Dockerで分散型機械学習を始める - Part 2: UbuntuでIPython Notebookを使う](http://qiita.com/masato/items/be383a81e323f3b5b42e)
* [Spark on Dockerで分散型機械学習を始める - Part 3: ローカルモードでspark-shellを起動する](http://qiita.com/masato/items/9398cccde46cf62aa609)
* [Spark on Dockerで分散型機械学習を始める - Part 4: AmbariでHadoopとSparkのsingle nodeセットアップ](http://qiita.com/masato/items/34cf33b6a956a2f723f9)


## Apache Ambari

　Sparkの勉強は[ローカルモード](http://qiita.com/masato/items/9398cccde46cf62aa609)でもspark-shellやspark-submitを実行することができますが、分散型機械学習のためには最初から分散環境で動かす方がやる気が出ます。
　今回はHadoopクラスタ管理ツールの[Apach Ambari](http://incubator.apache.org/ambari/)でHadoopをDocker上に構築します。AmbariエージェントのコンテナにセットアップしたSparkからYARNとHDFSを使ってみます。
　[Apach Ambari](http://incubator.apache.org/ambari/)は[HDP](http://jp.hortonworks.com/hdp/)クラスタ管理ツールの[Cloudbreak](https://github.com/sequenceiq/cloudbreak)の中心コンポーネントです。開発元の[SequenceIQ](http://sequenceiq.com/)2015年4月に[Hortonworks](http://hortonworks.com/)されているので、[HDP](http://jp.hortonworks.com/hdp/)をクラウドとDockerで構築する場合に安心して使えます。


### Blueprint
　
　Ambariの特徴に[Blueprint](https://cwiki.apache.org/confluence/display/AMBARI/Blueprints)があります。JSONのBlueprint定義されたクラスタは[Ambari Shell](https://github.com/sequenceiq/ambari-shell)のCLI、[Cloudbreak](http://docs.cloudbreak.apiary.io)のAPIなどから使いクラウドの仮想マシンやDockerコンテナにデプロイできます。

## single-node-hdfs-yarn

　SequenceIQの[リポジトリ](https://github.com/sequenceiq/ambari-rest-client/tree/master/src/main/resources/blueprints)にある[single-node-hdfs-yarn](https://github.com/sequenceiq/ambari-rest-client/blob/master/src/main/resources/blueprints/single-node-hdfs-yarn)のBlueprintを試してみます。他にもCloudbreakの[リポジトリ](https://github.com/sequenceiq/cloudbreak/tree/master/core/src/main/resources/defaults/blueprints)もあるのでもいろいろ試してみたいと思います。
　[Ambari provisioned Hadoop cluster on Docker](http://blog.sequenceiq.com/blog/2014/06/17/ambari-cluster-on-docker/)を読みながら作業を進めます。

## Docker
　Dockerは[MesosをDocker Composeを使い起動する - Part 1: 1つの仮想マシンにセットアップする](http://qiita.com/masato/items/12960a98eb95f3cacb05)で構築したDebianの仮想マシンを使います。

## Ambari

　Ambariのセットアップはとても簡単です。[j.mp/docker-ambari](https://raw.githubusercontent.com/sequenceiq/docker-ambari/master/ambari-functions)からダウンロードしたシェルスクリプトを読み込み関数や環境変数の設定を行います。インストール関数の引数は`2 single-node-hdfs-yarn`を指定すると`amb-server`と`amb-agent`で計2台のコンテナができます。`single`ですが引数は2なので注意が必要です。

```bash
$ curl -Lo .amb j.mp/docker-ambari && . .amb && amb-deploy-cluster 2 single-node-hdfs-yarn
...
ambari-shell>cluster build --blueprint single-node-hdfs-yarn
  HOSTNAME             STATE
  -------------------  -------------------
  amb1.service.consul  amb1.service.consul

  HOSTGROUP  COMPONENT                                                   Installation: 6.81% ----------
  ---------  -------------------
  master     ZOOKEEPER_CLIENT
  master     MAPREDUCE2_CLIENT
  master     RESOURCEMANAGER
  master     TEZ_CLIENT
  master     SECONDARY_NAMENODE
  master     HISTORYSERVER
  master     APP_TIMELINE_SERVER
  master     HDFS_CLIENT
  master     DATANODE
  master     NAMENODE
  master     NODEMANAGER
  master     ZOOKEEPER_SERVER
  master     YARN_CLIENT
CLUSTER_BUILD:single-node-hdfs-yarn>cluster autoAssign
  HOSTGROUP  HOST
  ---------  -------------------
  master     amb1.service.consul

CLUSTER_BUILD:single-node-hdfs-yarn>cluster create --exitOnFinish true
Successfully created the cluster
CLUSTER:single-node-hdfs-yarn>
```

　このインストール関数ではDockerイメージのダウンロードとコンテナの起動、Ambari Shellを使った設定まで自動的に行ってくれます。シェルの右上にプログレスバーが表示されしばらく待つと終了します。
　作成されたDockerコンテナを確認します。Ambari Server、Ambari Aagent、[Consul](https://www.consul.io/)の3つのコンテナが起動しました。　

```bash
$ docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                                                              NAMES
70cbaaf536d9        sequenceiq/ambari:2.1.2-v1    "/start-agent"           9 minutes ago       Up 9 minutes        8080/tcp                                                           amb1
682212a98724        sequenceiq/ambari:2.1.2-v1    "/start-server"          9 minutes ago       Up 9 minutes        8080/tcp                                                           amb-server
04bf47cee35e        sequenceiq/consul:v0.5.0-v6   "/bin/start -server -"   9 minutes ago       Up 9 minutes        53/tcp, 53/udp, 8300-8302/tcp, 8400/tcp, 8301-8302/udp, 8500/tcp   amb-consul
```

## Spark

　Ambari Agentのamb1コンテナに入りSparkのインストールを行います。

```bash
$ docker exec -it amb1 bash
```

　Single nodeなのでこのコンテナにHDPのコンポーネントが全てインストールされています。 HDPのバージョンは2.3.2です。

```bash
$ rpm -qa | grep hdp-select
hdp-select-2.3.2.0-2950.el6.noarch
$ hdp-select versions
2.3.2.0-2950
$ /usr/hdp/2.3.2.0-2950
```

　[Install Apache Spark with Cloudbreak](http://blog.sequenceiq.com/blog/2015/01/30/spark-with-cloudbreak/)を読みながら作業を進めていきます。シェルスクリプトをダウンロードして関数と環境変数を読み込みます。Ambariのインストールと同じような手順でインストール関数を実行します。

```bash
$ curl -Lo .spark-install j.mp/spark-hdp-install && . .spark-install
$ install-spark
upload assembly jar to hdfs
```

　HDFSにSparkのjarがアップロードされました。このスクリプトを読み込むとSparkの起動に必要な環境変数も設定されています。

```bash
$ echo $YARN_CONF_DIR
/usr/hdp/2.2.0.0-2041/hadoop/conf
$ echo $SPARK_JAR
hdfs:///spark/spark-assembly-1.2.0.2.2.0.0-82-hadoop2.6.0.2.2.0.0-2041.jar
```

　次にspark-env.shはテンプレートをコピーして`HADOOP_CONF_DIR`環境変数を指定します。

```bash
$ cd /usr/local/spark/conf
$ cp spark-env.sh.template spark-env.sh
$ vi spark-env.sh
HADOOP_CONF_DIR=/usr/hdp/current/hadoop-client/conf
```

### spark-submit

　spark-submitを実行して円周率の計算をします。

```bash
$ spark-submit --class org.apache.spark.examples.SparkPi   \
  --master yarn-cluster  \
  --num-executors 3 \
  --driver-memory 512m  \
  --executor-memory 512m  \
  --executor-cores 1  \
  /usr/local/spark/lib/spark-examples*.jar
...
15/11/30 02:04:13 INFO yarn.Client:
         client token: N/A
         diagnostics: N/A
         ApplicationMaster host: amb1.service.consul
         ApplicationMaster RPC port: 0
         queue: default
         start time: 1448850560018
         final status: SUCCEEDED
         tracking URL: http://amb1.service.consul:8088/proxy/application_1448849730081_0004/
         user: hdfs
```

YARNのlogに計算された円周率が出力されています。

```bash
$ yarn logs -applicationId application_1448849730081_0004
...
LogType:stdout
Log Upload Time:Mon Nov 30 02:48:54 +0000 2015
LogLength:23
Log Contents:
Pi is roughly 3.140252
End of LogType:stdout
```

 * HDFS

```bash
$ hadoop fs -ls /user
Found 2 items
drwxrwx---   - ambari-qa hdfs          0 2015-11-30 02:14 /user/ambari-qa
drwxr-xr-x   - hdfs      hdfs          0 2015-11-30 02:18 /user/hdfs
$ hadoop fs -ls /user/hdfs
Found 1 items
drwxr-xr-x   - hdfs hdfs          0 2015-11-30 02:35 /user/hdfs/.sparkStaging
```

### spark-shell

　次にspark-shellの起動を確認します。デフォルトだとログがたくさん出るので`rootCategory`のログレベルをINFOからWARNに変更します。

```bash
$ cd /usr/local/spark/conf
$ cp log4j.properties.template log4j.properties
$ sed -i 's/log4j.rootCategory=INFO/log4j.rootCategory=WARN/' log4j.properties
```

　`--master`フラグに`yarn`を指定してYARN上でspark-shellを実行します。

```bash
$ spark-shell --master yarn
tput: No value for $TERM and no -T specified
Spark assembly has been built with Hive, including Datanucleus jars on classpath
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 1.2.0
      /_/

Using Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_67)
Type in expressions to have them evaluated.
Type :help for more information.
15/11/30 03:06:02 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
15/11/30 03:06:09 WARN DomainSocketFactory: The short-circuit local reads feature cannot be used because libhadoop cannot be loaded.
15/11/30 03:06:09 WARN ClientBase: SPARK_JAR detected in the system environment. This variable has been deprecated in favor of the spark.yarn.jar configuration variable.
15/11/30 03:06:09 WARN ClientBase: SPARK_JAR detected in the system environment. This variable has been deprecated in favor of the spark.yarn.jar configuration variable.
Spark context available as sc.

scala>
```

　SPARK_JAR環境変数がdeprecatedになった警告が出ています。環境変数をunset、環境設定スクリプトもコメントアウトして次回実行時にはロードしないようにします。

```bash
$ echo $SPARK_JAR
hdfs:///spark/spark-assembly-1.2.0.2.2.0.0-82-hadoop2.6.0.2.2.0.0-2041.jar
$ unset SPARK_JAR
$ sed -i 's/^ *\(export SPARK_JAR.*\)$/# \1/' /tmp/.spark-install}}}
```

　再度spark-shellを実行すると警告が消えました。

```bash
spark-shell --master yarn
tput: No value for $TERM and no -T specified
Spark assembly has been built with Hive, including Datanucleus jars on classpath
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 1.2.0
      /_/

Using Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_67)
Type in expressions to have them evaluated.
Type :help for more information.
15/11/30 03:14:08 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
15/11/30 03:14:14 WARN DomainSocketFactory: The short-circuit local reads feature cannot be used because libhadoop cannot be loaded.
Spark context available as sc.

scala>
```

### 次にSparkを使うとき

　セットアップをしたシェルとコンテナを抜けた後にSparkを実行する場合は、環境設定スクリプトを読み込み必要な環境変数を読み込みます。

```bash
$ cd /tmp
$ source spark-install
$ set-spark-envs
$ echo $SPARK_HOME
/usr/local/spark
$ echo $HADOOP_USER_NAME
hdfs
$ which spark-submit
/usr/local/spark/bin/spark-submit
```

　`set-spark-envs`関数を実行すると以下の環境変数をexportします。

```bash
set-spark-envs() {
  export SPARK_HOME=/usr/local/spark
  export YARN_CONF_DIR=/usr/hdp/$HDP_VERSION/hadoop/conf
  export PATH=$PATH:$SPARK_HOME/bin
  export HADOOP_USER_NAME=hdfs
#  export SPARK_JAR=hdfs:///spark/spark-assembly-$SPARK_ASSEMBLY_VERSION.jar
}
```

## まとめ

　次回はAmbariのBlueprintにある[multi-node-hdfs-yarn](https://github.com/sequenceiq/ambari-rest-client/blob/master/src/main/resources/blueprints/multi-node-hdfs-yarn)や、[Apache Zeppelin](https://zeppelin.incubator.apache.org/)をセットアップする[datascientist.bp](https://github.com/sequenceiq/cloudbreak/blob/master/core/src/main/resources/defaults/blueprints/datascientist.bp)などもう少し大きめのクラスタを構築してみようと思います。


## 参考

以下のサイトを参考にしました。

* [Ambari provisioned Hadoop cluster on Docker](http://blog.sequenceiq.com/blog/2014/06/17/ambari-cluster-on-docker/)
* [Install Apache Hadoop And Spark On Docker (aim for cluster)](http://blog.iamtao.me/2015/08/13/Install-Apache-Hadoop-And-Spark-On-Docker/)
* [Install Apache Spark with Cloudbreak](http://blog.sequenceiq.com/blog/2015/01/30/spark-with-cloudbreak/)