SparkをREST APIから実行するApache Livy


あまり日本語の記事がなかったので、書いてみました。

Livyをざっくり要約すると、Sparkの処理をRESTサーバ経由(API)でリクエストできるソフトウェアになります。

**[本家サイトより](https://livy.incubator.apache.org/)**
**Submit Jobs from Anywhere**
> Livy enables programmatic, fault-tolerant, multi-tenant submission of Spark jobs from web/mobile apps (no Spark client needed). So, multiple users can interact with your Spark cluster concurrently and reliably.

**Use Interactive Scala or Python**
> Livy speaks either Scala or Python, so clients can communicate with your Spark cluster via either language remotely. Also, batch job submissions can be done in Scala, Java, or Python.

![livy-architecture.png](https://qiita-image-store.s3.amazonaws.com/0/203061/d00b2b5a-0907-4fe4-bdee-387b04947156.png)


本記事では、インストールとAPIでのSpark実行方法について簡単に記載しています。
全てローカルのMacマシンで実行しています。

なお、本記事ではSparkのインストール方法については割愛していますので、Sparkのインストール方法については、以下記事をご参考にしていただければと思います。

(参考)
[Spark/ScalaをJupyter Notebookから実行する - Qiita](https://qiita.com/48hands/items/c945a01e43cdfc82e98b)

## Livyのインストールと起動

以下のサイトからzipファイルをダウンロード・解答します。

- [Apache Livy Download Mirrors](https://www.apache.org/dyn/closer.lua/incubator/livy/0.5.0-incubating/livy-0.5.0-incubating-bin.zip)

zipファイル解凍後、ディレクトリを移動してLivyプロセスを起動します。

```bash
# 起動
./bin/livy-server start

# 起動状態確認
./bin/livy-server status
```

デフォルトで`localhost:8998`で起動しています。

## Livy APIを試してみる


- (参考) [Livy Docs - REST API](https://livy.incubator.apache.org/docs/latest/rest-api.html)

### インタラクティブに処理を実行する

Sparkコンテキストを生成し、インタラクティブに処理を実行してみます。
Sparkコンテキストはセッションを払い出してセッションで共有されるようなので、まずはセッションを作成します。

#### セッションの作成

```bash
# リクエスト
curl -XPOST -H 'Content-Type: application/json' localhost:8998/sessions -d \
'{
  "driverMemory": "2G",
  "driverCores": 1,
  "executorMemory": "2G",
  "executorCores": 1,
  "numExecutors": 1
}'

# レスポンス
{"id":0,"appId":null,"owner":null,"proxyUser":null,"state":"starting","kind":"shared","appInfo":{"driverLogUrl":null,"sparkUiUrl":null},"log":["stdout: ","\nstderr: "]}
```

`"id":0`の`0`がセッションIDなので、このセッションIDを使って、次節でコードを実行してみます。

#### コードの実行

払い出されたセッションID`0`を使ってコードを何回か実行してみます。

```bash
curl localhost:8998/sessions/0/statements -X POST -H 'Content-Type: application/json' -d \
'{
  "code":"val x = 1 + 2", 
  "kind": "scala"
}'

curl localhost:8998/sessions/0/statements -X POST -H 'Content-Type: application/json' -d \
'{
  "code":"val y = 1 + x", 
  "kind": "scala"
}'
```

UI（今回の場合は`http://localhost:8998`）にアクセスすると、実行結果が確認できます。


<img width="920" alt="スクリーンショット 2019-03-22 0.23.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/203061/48ebc634-c2bc-b1c1-52a9-cfaeaf8b92f7.png">


#### セッションの削除

使い終わったらセッションを削除しておきます。

```bash
curl -XDELETE localhost:8998/sessions/0
```

### バッチ処理を実行する

次に`jar`ファイルを使ってバッチ処理を起動してみます。

以下、Livyサーバに`/work`ディレクトリを作成し、そこに`jar`ファイルを置いてバッチアプリを実行してみます。

#### 事前の設定

まずは準備が必要になります。

```bash
# 実行したいjarファイルを今回はSparkに同梱されているexampleのものを利用する
# /workディレクトリは、このあとのconfファイルでホワイトリストに追加する必要がある
mkdir -p /work
cp $SPARK_HOME/examples/jars/spark-examples_2.11-2.4.0.jar /work
```

ローカルで実行するためには、以下の設定が必要です。

```bash
cp conf/livy.conf.template conf/livy.conf
vi conf/livy.conf
```

以下の2行を設定します。

```conf/livy.conf
livy.spark.master = local
livy.file.local-dir-whitelist = /work
```

`conf/livy.conf`ファイルを設定後、設定を反映するためにLivyプロセスを再起動します。

```bash
# プロセスの再起動
bin/livy-server stop
bin/livy-server start
```

#### バッチ処理実行

準備が整ったので実行します。

```bash
# バッチ処理のリクエスト
curl localhost:8998/batches -XPOST -H 'Content-Type: application/json' -d \
'{
  "file": "/work/spark-examples_2.11-2.4.0.jar",
  "className": "org.apache.spark.examples.SparkPi",
  "conf": {
    "spark.master": "local[1]"
  },
  "name": "example-app"
}'

# レスポンス
{"id":1,"state":"running","appId":null,"appInfo":{"driverLogUrl":null,"sparkUiUrl":null},"log":["stdout: ","\nstderr: "]}%
```

実行すると、バッチ処理のIDが払い出されます。
バッチIDを指定して実行ログを取得することができます。

```bash
# リクエスト
# ログを見やすくするためにPythonのjson.toolで整形しています
curl localhost:8998/batches/1/log | python -m json.tool

# レスポンス
{
    "id": 1,
    "from": 3,
    "total": 103,
    "log": [
        "2019-03-21 23:45:48 INFO  SparkContext:54 - Submitted application: Spark Pi",
        "2019-03-21 23:45:48 INFO  SecurityManager:54 - Changing view acls to: chocomint",
        "2019-03-21 23:45:48 INFO  SecurityManager:54 - Changing modify acls to: chocomint",
        "2019-03-21 23:45:48 INFO  SecurityManager:54 - Changing view acls groups to: ",
        ...
```

ログはLivyのUIからも確認できます。

<img width="918" alt="スクリーンショット 2019-03-21 23.58.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/203061/60010c24-3e0d-cefe-8e37-ae1ee82c0896.png">

## まとめ

バッチ処理をAPIで起動できるようになるので、便利かなと思いました。実業務で使えれば使っていきたいです。