docker image を使って spark-shell を実行してみるまで

#### 1. はじめに
Apache Sparkは巨大なデータに対して高速に分散処理を行うオープンソースのフレームワークです。
本格的に使うためには、複数のノードからなる Hadoop クラスタの構築が必要となりますが、手元のローカル環境でお試しに実行してみることも可能です。
特に spark-shell と呼ばれるツールは REPL (Read-Eval-Print-Loop) がベースになっており、Spark プログラムをインタラクティブに作成し、その場で実行結果をすぐに確認ができます。
今回は、Apache spark の Docker Image を使用し、ローカル環境で spark-shell を実行していきます。

#### 2.（参考）実行環境

##### 2-1. docker と docker-compose がインストールされている
それぞれがインストールされていることが前提になります。

```bash
$ docker -v
Docker version 19.03.6, build 369ce74a3c
```

```bash
$ docker-compose -v
docker-compose version 1.17.1, build unknown
```

##### 2-2. スペック
メモリはトータルで8GB搭載されているマシンを使用しましたが、4GB程度でも問題なく動作します。

```bash
$ cat /etc/issue
Ubuntu 18.04.2 LTS
```

```bash
$ cat /proc/meminfo | grep Mem
MemTotal:        8168284 kB
MemFree:         6812556 kB
MemAvailable:    7545960 kB
```

#### 3. docker network の作成
専用の docker network を使用し、コンテナに割り当てるIPアドレスを固定させるため、以下のコマンドで定義します。

```bash
$ docker network create spark-nw --subnet=172.30.0.0/16 --gateway=172.30.0.254
```

docker-compose.yml ファイル内で定義することも可能ですが、将来的に別の docker-compose.yml ファイルと共有することも想定し、敢えて予め作成する手段を取っています。



#### 4. docker-compose の準備
以下のようなファイル(docker-compose.yml)を用意します。
ここでは、Apache spark の docker image として、"blueskyareahm/spark-base:latest" を指定していますが、他にも多くの image が公開されており、なんでも構いません。
ちなみに本イメージは alpine:3.10 を元に作成した spark version 2.3.2 になります。

```docker-compose.yml
version: '2'

services:
  spark-master:
    image: blueskyareahm/spark-base:latest
    hostname: doc-spark-master101.local
    container_name: spark-master101
    ports:
      - 4040:4040
      - 8080:8080
      - 7077:7077
    mem_limit: 1g
    networks:
      spark-nw:
        ipv4_address: 172.30.1.2
    command: /spark/bin/spark-class org.apache.spark.deploy.master.Master --host 0.0.0.0
    extra_hosts:
      - "doc-spark-worker101.local:172.30.1.3"

  spark-worker:
    image: blueskyareahm/spark-base:latest
    hostname: doc-spark-worker101.local
    container_name: spark-worker101
    depends_on:
      - spark-master
    ports:
      - 8081:8081
    mem_limit: 1g
    networks:
      spark-nw:
        ipv4_address: 172.30.1.3
    command: /spark/bin/spark-class org.apache.spark.deploy.worker.Worker spark://spark-master:7077 --host 172.30.1.3
    extra_hosts:
      - "doc-spark-master101.local:172.30.1.2"
 
networks:
  spark-nw:
    external: true
```
こちらの docker-compose.yml ファイルからは、spark-master と spark-worker それぞれ一つずつコンテナが生成されることになります。

#### 5. コンテナのビルド・開始
docker-compose.yml が配置されているディレクトリで以下のコマンドを実行します。

```bash
$ docker-compose up --build -d
Pulling spark-master (blueskyareahm/spark-base:latest)...
(省略)
Creating spark-master101 ... 
Creating spark-master101 ... done
Creating spark-worker101 ... 
Creating spark-worker101 ... done
```

コマンドで確認すると、docker-compose.yml 内で指定した docker image がローカルにダウンロードされたことが確認できます。

```bash
$ docker images
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
blueskyareahm/spark-base                        latest              434f57cb14db        3 days ago          344MB
```

また、spark-master と spark-worker それぞれ一つずつコンテナが起動されていることが確認できます。

```bash
$ docker ps
CONTAINER ID        IMAGE                             COMMAND                  CREATED             STATUS              PORTS                                                                    NAMES
8643575faa0b        blueskyareahm/spark-base:latest   "/spark/bin/spark-cl…"   7 minutes ago       Up 7 minutes        8080/tcp, 0.0.0.0:8081->8081/tcp                                         spark-worker101
1ac9774f5ff9        blueskyareahm/spark-base:latest   "/spark/bin/spark-cl…"   8 minutes ago       Up 7 minutes        0.0.0.0:4040->4040/tcp, 0.0.0.0:7077->7077/tcp, 0.0.0.0:8080->8080/tcp   spark-master101
```

#### 6. spark-shell の開始
以下のコマンドで spark-shell を開始します。
spark-master コンテナ内の spark-shell コマンドより開始しています。

```bash
$ docker-compose exec spark-master /spark/bin/spark-shell --master spark://localhost:7077
```

以下のようなインタラクティブ　シェルが起動します。

```scala
Spark context Web UI available at http://doc-spark-master101.local:4040
Spark context available as 'sc' (master = spark://localhost:7077, app id = app-20200727141606-0000).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.3.2
      /_/
         
Using Scala version 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_242)
Type in expressions to have them evaluated.
Type :help for more information.

scala> 
```

以下のようにコマンドを実行してみます。

```scala
scala> val list = List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> val rdd = sc.parallelize(list)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:26

scala> rdd.count
res0: Long = 3                                                                  

scala> rdd.collect
res1: Array[Int] = Array(1, 2, 3)
```

val list = List(1,2,3) で配列を宣言しています。
そして、その配列を元に Apache spark の特徴的なデータである RDD を生成しています。
生成した RDD のインスタンスに対して、count を呼び出すことで、その配列のサイズが返ることが確認できます。
また、collect を呼び出すことで、その配列の中身が返ることが確認できます。

#### 7. さいごに

Apache spark の特徴的なデータ型 RDD(など) には大きく２つに分けて "変換"と"アクション"と呼ばれるメソッド(API)群が用意されています。
※count と collect は "アクション"に分類されます。
これは Apache spark の特徴であり、適切に使用するにあたって、非常に大事な要素の一つになります。

提供されているAPIは spark-shell から実行させることが可能で、その動作を確認するのに便利です。
Streaming 処理や Machine learning のAPIを試用することも可能です。
docker image を使用することで、最短5分ほどで hbase-shell 実行環境まで用意することができます。