Apache YARN 上で Apache Spark を動かす。

複数マシン上で Spark アプリケーションをいくつか動かすとき、リソースの割り当てをその都度人が制御していると大変である。 そんなとき、YARN クラスタを構築して、リソースの割り当てを YARN に任せてしまおう。

環境
- Apache Hadoop 3.1.0
- Apache Spark 2.3.0
- OS : Ubuntu 16.04.3 LTS

# YARN クラスタの構築
設定ファイルを編集し、YARN サーバ、クライアントを始動する。

## 設定ファイルの編集

編集する設定ファイル

- ${HADOOPHOME}/etc/hadoop/core-site.xml
    - HDFS （分散ファイルシステム）を制御するサーバをどのマシンにするか指定したりする。
- ${HADOOPHOME}/etc/hadoop/hdfs-site.xml
    - HDFS 関連の設定ができる。
- ${HADOOPHOME}/etc/hadoop/yarn-site.xml
    - マシンのリソースマネージャーの設定ができる。
    - どのマシンをリソースマネージャーのサーバにするか、各マシンが YARN に割けるメモリ量をどのくらいにするか、などを指定できる。
- ${HADOOPHOME}/etc/hadoop/workers
    - HDFS や YARN の各種サーバのクライアントとなるマシンのIPアドレスを指定する。

それぞれの中身

``` core-site.xml
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://192.168.0.1:9000</value>
  </property>
  <property>
    <name>hadoop.tmp.dir</name>
    <value>/work1/user/hadoop/datanode</value>
  </property>
</configuration>
```

``` hdfs-site.xml
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>
```

``` yarn-site.xml
<configuration>

<!-- Site specific YARN configuration properties -->
  <property>
    <name>yarn.resourcemanager.hostname</name>
    <value>192.168.0.1</value>
  </property>
  <property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>28672</value>
  </property>
  <property>
    <name>yarn.scheduler.maximum-allocation-mb</name>
    <value>28672</value>
  </property>
  <property>
    <name>yarn.nodemanager.pmem-check-enabled</name>
    <value>false</value>
  </property>
  <property>
    <name>yarn.nodemanager.vmem-check-enabled</name>
    <value>false</value>
  </property>
</configuration>
```

``` workers.
node01
node02
node03
node04
node05
node06
node07
node08
```



## YARN クラスタの始動

まずは、各マシンが HDFS のデータ置き場として扱うディレクトリ（hadoop.tmp.dir で設定したディレクトリ）を ``` mkdir -p /work1/user/hadoop/datanode ```　で作る。 これは各マシンが異なるデータを格納するので、共有ディレクトリだとまずい。

次に、作成したディレクトリを HDFS 用のファイルシステムにフォーマットする。

```
$ hdfs namenode -format   # 作成したディレクトリを HDFS ようにフォーマット
$ ${HADOOPHOME}/sbin/start-dfs.sh
$ ${HADOOPHOME}/sbin/start-yarn.sh
```

起動できたかを確認する。
各ノードにログインして ```jps``` コマンドを打つか、次のコマンドを入力（dshのインストールが必要）

```
dsh -M -g cluster01 jps

node01: 22168 Jps
node01: 3641 NameNode
node01: 4082 SecondaryNameNode
node01: 4341 ResourceManager
node01: 21739 DataNode
node01: 21885 NodeManager
node02: 14867 Jps
node02: 14422 DataNode
node02: 14567 NodeManager
node03: 26995 Jps
node03: 26550 DataNode
node03: 26695 NodeManager
node04: 18628 NodeManager
node04: 18952 Jps
node04: 18478 DataNode
node05: 16592 Jps
node05: 16120 DataNode
node05: 16270 NodeManager
node06: 23204 DataNode
node06: 23354 NodeManager
node06: 23677 Jps
node07: 29779 DataNode
node07: 29929 NodeManager
node07: 30250 Jps
node08: 9056 Jps
node08: 4694 NodeManager
node08: 3846 DataNode
```

サーバで５つ、クライアントで３つずつマネージャーが動作していればOK!



# Spark アプリケーションの実行

アプリケーションを YARN 上で正しく実行するための設定をし、 YARN のリソースマネージャーにアプリケーションを渡して、実行してもらう。

## Spark の設定

${SPARKHOME}/conf/spark-defaults.conf を編集する

``` spark-defaults.conf
#アプリケーションのログを吐くようにする。
spark.eventLog.enabled    true

# ログを吐く場所を設定
spark.eventLog.dir        file:/work1/user01/tmp/spark-events

# history-server（後述）が読み込むログのディレクトリを設定：基本ログを吐く場所に合わせる。
spark.history.fs.logDirectory  file:/work1/user01/tmp/spark-events

# おそらく実行中の一時ファイルの保管場所。 hadoop fs -mkdir とかで作っておくか、file:// のどこかを指定する。

spark.yarn.stagingDir=hdfs:///user01
```

実行済みのアプリケーションのログをWebUIでチェックするためのサーバを始動する。

```
${SPARKHOME}/sbin/start-historyserver.sh
```

## YARN に Spark job を投げる。

Spark job の実行の仕方は、spark-submit と spark-shell があるが、これらを実行するときに、「YARN 上で実行してください」、と命令する必要がある。
普通、リソースマネージャーが稼働しているノードの IP & port を明示的に指定するのかなと思うだろうが、worker 上で実行する場合はなぜかその設定をせずに実行できる。

例として、以下のように実行する。

```
spark-submit \
    -master yarn \
    --deploy-mode client \
    --num-executors 8 \
    --executor-cores 4 \
    --executor-memory 23g \
    --class example.SparkApp \
    spark-application.jar
```

IP アドレスとか指定して、リモートの YARN にジョブを投げる場合はどうすればいいのだろうか。分かる方がいらっしゃいましたら教えてください。

## webUI でジョブの状態を確認する。

YARN クラスタの状態はデフォルト設定では、web ブラウザで http://node01:8088 にアクセスすると確認できる。ここから実行中のジョブを監視できる。
一方で、実行後のジョブは、デフォルト設定で http://node01:18080 にアクセスすることで確認できる。