そろそろ決着つけようぜ？ Amazon EMR vs Google Cloud Dataproc (2016年Ver)

# 0.はじめに

Distributed computing (Apache Hadoop, Spark, ...) Advent Calendar 2016の14日目です。(投稿が過ぎてしまって本当にすいませんでした。。。)
タイトルは若干煽り目ですが、今年はAmazon EMRとGoogle Dataprocを使う機会が多かったので、実際に利用しての現時点における機能比較と使ってみての所感についてまとめてみようかと思います。
補足や誤り等あればぜひお願いいたします。


## 先に結論
現時点においては適材適所なのでどちらが絶対的優勢とは言えないのですが、以下が現時点におけるベターな使いかたかなと思います。あくまで所感です。
次から詳細な比較に入っていきます。

### EMR
>* バッチ処理等のワンショット的な使いかた、ストリーム処理等の常時稼働させておく系の使い方の両方をそつなくこなせる
* 常時稼働させておく場合、マスターノードが単一障害点なためEMRFSを利用するなどしてクラスタ内部にはデータや状態を持たせない設計を意識する必要あり

### Dataproc
>* 現時点ではバッチ処理等のワンショット的な使いかたが無難かも
* マスターノードは冗長化可能であり、一見すると常時稼働させるならEMRより気が利いているが、運用監視面が弱く自分で頑張る必要があり、マネージドの恩恵ガガガガorz



## 比較のポイント
比較のポイントは以下の通りです。

1. **クラスタ構成全般**
	* ノード構成
	* ネットワーク配置
	* 利用可能なアプリケーション
	* プロビジョニング
	* 稼働中クラスタサイズの変更
2. **プロセッシング**
	* ジョブ実行
	* オブジェクトストレージ連携
3. **運用監視系**
	* メトリクス
	* ロギング
4. **コスト**


# 1.クラスタ構成全般

## ノード構成
クラスタを構成するノード構成について両者をまとめてみます。

### EMR
>* マスターインスタンス
	* NameNode、ResourceManager、Zookeeperなどの管理系プロセスが集約されたノード
	* マスターノードは１台のみで、このノードがダウンするとクラスタそのものもダウンする単一障害点
* コアインスタンス
	* DataNode、NodeManagerといった処理系プロセスが稼働するノード
* タスクインスタンス
	* HDFSはなくNodeManagerのみが稼働するノード
	* スポットインスタンスも利用することが可能

### Dataproc
 > * マスターノード
	* NameNode、ResourceManager、Zookeeperなどの管理系プロセスが集約されたノード
	* クラスタモードを高可用性モードにすることで3台のノードで上記管理系プロセスを冗長化可能
* ワーカーノード
	* DataNode、NodeManagerといった処理系プロセスが稼働するノード
*  プリエンプティブワーカーノード
	* HDFSはなくNodeManagerのみが稼働するノード
	* プリエンティブインスタンス(AWSのスポットインスタンスのようなもの)で構成

クラスタを構成するノード構成は呼び方は違えど役割はどちもほぼ同じですね。
両者の一番の違いは管理系プロセスが集約されているマスターインスタンス(ノード)をDataprocだと冗長化できる点でしょうか。
DataProcではクラスタモードというものが選択可能で、標準モードだとEMR同様にマスタノードが1台のみで構成されるのに対して、高可用性モード(現時点ではβ版)ではマスターノードが3台構成となります。

実際に高可用性モードでクラスタを起動すると、以下のように3台のマスタノードが立ち上がってきます。


![cluster-1_-_My_First_Project.png](https://qiita-image-store.s3.amazonaws.com/0/48330/6fbef722-7993-daa1-5601-20da36e68a3b.png "cluster-1_-_My_First_Project.png")


この3台におけるプロセス配備は以下のようになっています。
プロセス配置の詳細などDataprocの高可用性モードの詳細については下記ドキュメントに記載があります。
>High Availability Mode
https://cloud.google.com/dataproc/docs/concepts/high-availability

![名称未設定_key.png](https://qiita-image-store.s3.amazonaws.com/0/48330/bd6f9e00-5ddf-0c85-ae30-28903621e6cc.png "名称未設定_key.png")


高可用性モードとすることでクラスタを常時稼働させておくような使い方でも、マスターノードの単一障害点を避けられることができますね。
ただし、詳しくは後述しますがせっかくDataprocはこういった高可用性モードが使えるにもかかわらず、運用管理面がワンショットな使いかた向けで、なんだか残念な感じです。。。


## ネットワーク配置
クラスタ作成時のネットワーク配備について比較してみます。

### EMR	
>* 起動するVPCを指定可能
* 特定の1つのAZにクラスタが展開され、配置するAZは指定可能

### Dataproc	
>* クラスタを起動するネットワークは指定可能
* 特定の1つのゾーンにクラスタが展開され、配置するゾーンは指定可能

クラスタのネットワーク配置についてはどちらも同じで、データセンター間をまたいだクラスタ構成は現状だといずれも組めないようです。
そのため、AZ/ゾーン障害時においてはいずれも利用可能なゾーンでのクラスタ再立ち上げが必要となります。


## 利用可能なアプリケーション
ここでいう利用可能なアプリケーションとは、クラスタ起動時に一緒にインストールまでを行ってくれるアプリケーションを指します。
現時点ではそれぞれ以下がサポートされています。
※()内の数値はバージョンです

| EMR (バージョン5.2.0) | DataProc (バージョン1.1) |
|-----------------------|--------------------------|
| Hadoop (2.7.3)        | Hadoop (2.7.3)           |
| Spark (2.0.2)         | Spark (2.0.2)            |
| Hive (2.1.0)          | Hive (2.1.0)             |
| Pig (0.16.0)          | Pig (0.16.0)             |
| Ganglia (3.7.2)       | -                        |
| HBase (1.2.3)         | -                        |
| Tez (0.8.4)           | -                        |
| Zeppelin (0.6.2)      | -                        |
| Presto (0.152.3)      | -                        |
| ZooKeeper (3.4.8)     | -                        |
| Sqoop (1.4.6)         | -                        |
| Mahout (0.12.2)       | -                        |
| Hue (3.10.0)          | -                        |
| Phoenix (4.7.0)       | -                        |
| Flink (1.1.3)         | -                        |
| HCatalog (2.1.0)      | -                        |

詳細はこちらに。

>EMR
http://docs.aws.amazon.com//ElasticMapReduce/latest/ReleaseGuide/emr-release-components.html
Dataproc
https://cloud.google.com/dataproc/docs/concepts/dataproc-versions


サポート数だけで比較してしまうと両者でかなりの差が出ています
ただ両者ともコアなアプリケーションはサポートしているので、通常のユースケースにおいては遜色はないかもしれません。
とはいえ、使い出すと色々とやりたくなるのが常でして、そういう点ではオプションの多いEMRは魅力的だったりします。
Google Cloud自体が、必要最低限の機能に止めたシンプルさが特徴となっているのもあり、こういったところにもその思想が伺えます。

なお、上記のアプリケーションはコンフィグの設定値も起動時に変更することが可能です。
それぞれのやり方については下記のドキュメントに詳細があります。

>EMR
http://docs.aws.amazon.com//ElasticMapReduce/latest/ReleaseGuide/emr-configure-apps.html
DataProc
https://cloud.google.com/dataproc/docs/concepts/cluster-properties


## クラスタの初期プロビジョニング
クラスタの初期プロビジョニングの方法について整理してみます。
ここでいうプロビジョニングとは例えば、前述した自動インストール可能なアプリケーション以外のものを入れたり、OS周りの設定を行ったりなど、クラスタを構成するサーバに直接設定を行うことを指します。

### EMR	
> * ブートスラップアクションでS3に配置したスクリプトやコマンドを実行可能なため、そこで必要な設定を記述

### Dataproc	
>* 初期化操作でCloud Storageに配置したスクリプトを実行可能

いずれも基本的なやり方は同じで、

1. 事前にプロビジョニング用のスクリプトを作成
2. 作成したスクリプトをオブジェクトストレージに配置
3. 起動時のブートストラップアクション/初期化操作で上記の配置したパスを指定

EMRのブートストラップアクションの場合、ある条件の時にだけ実行など簡単な条件設定ができます。
一方Dataprocの場合、実行するスクリプトの中に自身で実行条件を組み込む必要があります。

雰囲気をつかむために、それぞれのドキュメントにある初期プロビジョニングの例を並べてみます。

EMRの場合

```クラスタ作成時コマンド
aws emr create-cluster \
--name "Test cluster" \
~省略~
--bootstrap-action Path=s3://elasticmapreduce/bootstrap-actions/run-if,Args=["instance.isMaster=true","echo running on master node"]
```

GCPの場合

```クラスタ作成時コマンド
gcloud dataproc clusters create my-dataproc-cluster \
--initialization-actions gs://my-bucket/download-job-jar.sh
```

```実行スクリプト
#!/bin/bash
ROLE=$(/usr/share/google/get_metadata_value attributes/role)
if [[ "${ROLE}" == 'Master' ]]; then
  gsutil cp gs://my-bucket/jobs/sessionalize-logs-1.0.jar home/username
fi
```

両方のサンプルで、マスタインスタンス(ノード)であれば処理を実行するといった内容です。
EMRの場合、run-ifを利用することでスクリプト内部で分岐処理を書かなくても実行判別ができる一方、Datprocではスクリプトの中身で判別を行っています。


小言を言うと、いずれの操作も実行結果がオブジェクトストレージ上にログとして出るため、デバッグがしずらいというか待つのがしんどいといか。。
結局起動後にSSH経由で設定用のスクリプト流してっていう方がすぐ結果を確認できたりと何かとはかだるシーンも多いのが本音です。

それぞれの詳細については下記のドキュメントをご参照ください。

> EMRのブートストラップアクション
> http://docs.aws.amazon.com/ElasticMapReduce/latest/ManagementGuide/emr-plan-bootstrap.html

> Dataprocの初期化操作
> https://cloud.google.com/dataproc/docs/concepts/init-actions




## 稼働中クラスタのリサイズ
クラスタ作成後のリサイズについて両者を比較してみます。

### EMR
>* コアインスタンス、タスクインスタンスともにノード数の増減可能
* オートスケーリングが利用可能で、CloudWatchへ連携されるYARNやHDFSのメトリクスをトリガーにインスタンス数の変更が可能

### Dataproc	
>* API経由でのみワーカーノード、プリエンプティブノードともにノード数の増減可能
* EMRのような、ミドルウェア・アプリケーション固有のメトリクスをトリガーにしたオートスケールは明示的にサポートされていない。(自身でつくり込む必要がある)

処理系のノードを増減させることはいずれも対応しています。
ただ、リソース使用状況などをトリガーにしたスケールアウトという観点ではEMRは作り込まれてますね。
EMRの場合、もとよりミドルウェア固有のメトリクスもCloudWatchへ自動で連携されていることもあり、例えばYARNのリソース状況に応じた綿密なオートスケール戦略がとれます。
しかも、スケールダウンもちゃんと考慮した振る舞いを設定できるので、どういった条件でクラスタをリサイズするのかを考えることに専念できます。

>Using Automatic Scaling in Amazon EMR
>http://docs.aws.amazon.com/ElasticMapReduce/latest/ManagementGuide/emr-automatic-scaling.html

> **Configure Cluster Scale-Down**
> http://docs.aws.amazon.com/ElasticMapReduce/latest/ManagementGuide/emr-scaledown-behavior.html

一方Dataprocですが、こちらはEMRのようなオートスケールはできないようです。
なので自身でリソースの閾値をもとに、クラスタのリサイズAPIを発行するような仕掛けをつくり込む必要があります。(実は裏技があったりするんでしょうか)


# 2.プロセッシング

## ジョブ実行
ここでいうアプリケーション実行とは、HadoopやSparkアプリケーションをEMRやDataprocで提供されている機能を使って実行することを指します。
もちろん上記以外のアプリケーションも外部実行することは可能で、もっと言えば起動後はSSH経由などで自由に操作は可能です。

### EMR	
>* ステップでアプリケーションを実行可能

### Dataproc	
>* ジョブでアプリケーションを実行可能

どちらも同じような仕組みが提供されています。
これは完全に個人的な意見になりますが、EMRのステップはクラスタ起動時のシーケンスの中で処理されることを想定した作りとなっている一方、Dataprocの方はクラスタ起動のシーケンスとジョブ実行が切り離されているような作りな印象です。そのため、クラスタ起動後にベットジョブ実行用のAPIを都度実行して動かすような使いかたになりそうです。

バッチ処理の時だけ起動して、処理が正常終了したらクラスタをそのまま自動で終了させるような使い方の場合、EMRの方がクラスタ起動処理APIを実行するだけで先ほどの一連の処理を組み込めるので向いているかもしれないです。


## オブジェクトストレージ連携

オブジェクトストレージとの連携、特にHDFSの拡張機能についてみてみます。
最近のトレンドとしても、データをオブジェクトストレージに格納することで、クラスタを構成するサーバから処理データを切り離して、データローカリティーを意識させないような構成がよく取られるようになってきています。

### EMR
>* S3をEMRFSとして、透過的にデータの読み書きが可能
>* 整合性ビューという機能により、EMRFS経由でのS3へのデータ読み書きにて読み取り整合性を維持できる。ファイル操作に伴うメタデータをDynamoDBを使ってこの機能を実現

### Dataproc	
>* Gloud Storageのパスを指定することでデータのやり取りが可能>
>* それ以外詳しいことは不明

いずれもデータストア層をオブジェクトストレージとする構成は可能です。とはいえ、この点もEMRが一歩進んでいる印象です。
そもそもDataprocの場合、ドキュメント上は「Cloud Storageとも連携できる！」と記載しているものの、肝心な詳細情報が見当たらない・・(見落としているだけでしたらすいません。。)
インストールソフトウェアの情報を見てみると、以下のコネクタを利用してHadoopとCloud Storage間のデータ連携を可能としているようです。

>  https://github.com/GoogleCloudPlatform/bigdata-interop

試しにHiveで以下のようにロケーションとしてCloud Storageのパスを指定することで、データをやり取りすることができました。

```HiveQL
CREATE TABLE gcs_test(col INT) LOCATION 'gs://gcs_test_bucket/gcs_test';
```

あとは通常のテーブル操作を通じて、指定したCloud Storage上のディレクトリにデータ(ファイル)が配置されます。

# 3.運用監視系

## メトリクス

個人的には現時点での決定打となっている箇所かなと思います。
まずは両者をざくっと比較してみましょう。

### EMR	
>* CloudWatchへ自動で各種メトリクスを連携可能で、ミドルウェア、アプリケーション固有のメトリクスまで取得可能
* Gangliaをクラスタ作成時にインストール可能で、より細かいメトリクスはGanglia経由で確認可能

### Dataproc	
>* StackdriverへOSレイヤの基本メトリクス(CPU使用率やディスク使用率など)は自動で連携可能
* ミドルウェア、アプリケーション固有のメトリクスは自分で取得する必要あり

自動で取得してくれるメトクスの幅が圧倒的に違いますね。
Google Cloudの場合、Stackdriverというサービスで監視は行い、プラグインを利用することで特定のミドルウェアに特化したメトリクスの取得は可能なようです。
ただ、対応可能なプラグインも今だと種類も少なく、ここはEMRが圧倒的優勢なのかなと思いました。
念のためStackdriverの監視対象リソースのドキュメントをみてみても、まだDataprocとしてのサポートはされていないようです。

>https://cloud.google.com/monitoring/api/resources

前述したように、Dataprocはマスターノードを冗長化できるのもあり、クラスタを常時稼働させるような使い方により向いていそうなのにもかかわらず、運用周りの整備がまだまだといった感じですごく惜しいです。。



## ロギング(主に外部へのアーカイブ機能)
各種ログの扱われ方などについて比較してみます。メトリクス同様、ここも両者で大きな開きがあるように思います。

### EMR
>* EMRサービス固有のログ(ブートストラップアクションやスッテプの実行結果など)、ミドルウェア固有(Hadoop, Hive等)の両方のログをS3に自動アーカイブ可能

### Dataproc	
>* Dataproc固有のログ(初期化操作やジョブ経由でのアプリケーション実行結果など)はCloud Storageへ自動連携されるが、ミドルウェア固有のログは自動ではアーカイブされない
	
もしかすると私がDataprocを使い倒せていないだけかもしれませんが、自動でオブジェクトストレージにアーカイブしてくれるログのサポート具合がだいぶ違います。
EMRもDataprocもクラスタ停止後はサーバ内部のログも消えてしまうので、どうログを外に退避させるかを考える必要があります。
EMRの場合、定期的に主要なミドルウェア群のログもアーカイブしてくるので、自身でつくり込むポイントがだいぶ減るのですが、Dataprocは自身で必要な必要なミドルウェアのログも外だしする必要がありそうです。

メトリクスしかり、ロギングしかり、AWSの方が断然行き届いているようには思えます。ただし、これも利用用途次第なので、バッチ処理だけの起動であればDataprocのような割り切った仕様でも問題ないのかもしれません。
とはいえ、何かあった時の切り分け時を考慮すると申し少しDataprocは考慮が欲しいところです。


# 4.コスト
コストに関していうと、単純な価格差だけでいうと現時点ではただGoogle Cloudの方が安いという話で終わってしまうので、それぞれの課金特性について着目してみます。

### EMR	
>* 時間単位での従量課金なため、例えば20分しか起動していなくても1時間分の料金が発生

### Dataproc	
>* 分単位での従量課金

コストは「利用時間×時間当たりの利用料金」の面積で決まりますが、AWSのように1時間単位での課金の場合、どうしても利用時間が時間(Hour)単位な以上、
数十分の処理時間削減では費用対効果が悪いケースも出てきます。
GCPであれば分単位での処理時間削減がそのまま費用削減につながるので、費用対効果がダイレクトに表現しやすいという性質もあります。

規模によっては正直誤差の範囲なのかもしれませんが、チューニングや処理時間短縮のための施策に対する費用対効果などを説明するような場面では、分単位でもダイレクト効果が見える方が都合がよかったりするんですよね。。(これは完全に愚痴です)	



# さいごに
個人的な意見や思いも入ってしまっていますが、現時点では一概にどっちが優勢とも言い難い状況かと思います。
最近のEMRのアップデートスピードも早いですし、Dataprocもちょいちょい機能追加されているし、今後に目が離せないです。
よってタイトルで煽っておいてなんですが、2017年の両者の進化に期待です！