ビミョーデータにおけるEMRの効率的な使い方

## おきまりのやつ
こんにちは。[CYBIRDエンジニア Advent Calendar](http://qiita.com/advent-calendar/2014/cybird)
16日目のトレンディ小松です。
データ分析のエンジニアをしています。
インフルエンザが流行ってますが、当然私も療養中です。
皆さん、うがい・手洗いなど予防はしっかりと・・・。
さて昨日は[megadreams14](http://qiita.com/megadreams14)さんの[CYBIRDの技術者社内交流イベントについて](http://qiita.com/megadreams14/items/5134329738101fd4cea9)でした。
デブコミュは多くの刺激とパワーを与えてくれる場なので今後も大いに盛り上がって行きたいですね！

## 本日の要約
ビッグデータでなくとも効率的に処理すればEMRは低コストで痒い所に手が届く！

## こんな状況であれば参考になるかも
* 使える予算が少ない
* 分析（集計）対象のサービス、分析（集計）項目が多い
* データ（DB）へのアクセス権が限られている、もしくは下手なことができない
* ビッグではないがSQLでは対応し難いビミョーなデータ量がある

## EMRとは
簡単に言えばAmazon版のHadoopです。
EC2上でHadoopクラスターを構成します。
※概要は[コチラ](http://aws.amazon.com/jp/elasticmapreduce/)をご確認ください。

## 料金
ノード分のEC2料金とEMR料金が時間単位で掛かります。
EMR料金はEC2料金の約18%程度なので、クラスター起動における料金はEC2料金が大部分を占めることになります。
もちろんデータソースをS3やDynamoDB等にすればその分の利用料金も掛かります。

## EMRの操作
Amazon Management Consoleからも操作できますが、バッチ処理を想定するならばAWS SDKもしくはAmazon EMR CLIを使うのが基本になると思います。
個人的に何かとPerlを使うことが多いので、今回はCLIを使うパターンです。
Perlは他にNet::Amazon::EMRを使う選択肢もありますが、大したことはしないのでCLIでシンプルにやりたいと思います。
※Amazon EMR CLIのインストール及び設定の詳細は[コチラ](http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-cli-install.html)

## EMRの起動（クラスターの作成）
今回はHiveでの処理についてです。
HiveはHiveQLというSQLライクな言語で分散処理を実現できるプロダクトです。
CLIからの起動にはインタラクティブモードとバッチモードの2つがあります。
インタラクティブモードは対話型で処理を進め、全ての処理が終わっても明示的にTerminateされるまでクラスターは稼働し続けます。
一方でバッチモードは全ての処理が完了するとクラスターは自動的にTerminateされます。
（例）インタラクティブモード（Linux）
`elastic-mapreduce --create --alive --hive-interactive --name abc-job --instance-type m1.large --num-instance 1`

（例）バッチモード（Linux）
`elastic-mapreduce --create --name abc-job --instance-type m1.large --num-instance 1 --hive-script -arg s3://xxxxxx/xxx/xxx.hql`

※CLIオプションの詳細は[コチラ](http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-cli-commands.html#common-options)

今回はこのバッチモードの話です。
では実際の処理の流れです。

## Hiveでのデータ処理フロー
1. S3に処理対象データを格納（定期的にDB及びその他必要なデータを取得）
2. HiveQLを動的に生成しS3に格納（スクリプトで生成）
3. ジョブフローを作成（EMRを起動）
4. ジョブフローにステップを追加

こんな感じでバッチ処理を走らせています。
で、今回のメインテーマです。

## 効率化ポイント
「4.ジョブフローに**ステップを追加**」するプロセスが重要です。
ステップとはデータの処理単位のことです。
実際には下記のように追加することができます。

`elastic-mapreduce --jobflow j-XXXXXXXXX --hive-script --arg s3://xxxxxx/xxx/xxx.hql`

ここでのステップはHiveQL単位です。
--hive-script --argオプションでステップに追加するHiveQLファイルのパスを指定します。
指定したステップはジョブフローに順次追加されていきます。
ここで注意すべきことが1点あります。
一つのジョブフローで処理できる最大ステップ数は256までです。
常時起動でない限りなかなかそのようなケースはないでしょうが、その場合は[コチラ](http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/AddMoreThan256Steps.html)を参考にして下さい。
j-XXXXXXXXXはジョブフローIDで、下記コマンドで確認することができます。

`elastic-mapreduce --list --active`

これらを利用して下記のようなコード(perl)で動的に制御することもできます。
（ちょっと強引なのであくまでも参考程度で、、）

```pl:sample.pl
# ジョブフロー名「abc-job」のアクティブなジョブを確認
my $emr = `/home/xxxxxx/emrclient/elastic-mapreduce --list --active | grep abc-job`;

# ジョブフローIDを抽出
my @job_status = split(/ /,$emr);
if($job_status[0]){
	$job_status[0]=~ s/\n//;
	$job_status[0] =~ s/\r//;
}

if($job_status[0]){
	# ジョブフローIDが取得できた場合=該当ジョブフローが生きている場合
	# ステップを追加
	system("/home/xxxxxxx/emrclient/elastic-mapreduce --jobflow $job_status[0] --hive-script --arg s3://xxxxxx/xxx/xxx.hql");
}else{	
	# ジョブフローIDが取得できなかった場合=該当ジョブフローが初回起動の場合
	# ジョブフローを新規に生成
	system("/home/xxxxxx/emrclient/elastic-mapreduce --create --name abc-job --instance-type m1.large --num-instance 1 --hive-script --arg s3://xxxxxx/xxx/xxx.hql");
}
```

各ステップが1時間に満たないものが多数ある場合はまとめてシングルタスク的に使うのがキモとなります。
EMR、EC2は1時間単位での課金となるのでその枠を目一杯使うということです。
もちろん時間的な制約が緩い処理に限って使える手ですが。
DBサーバのスペックやテーブル設計などによるところはありますが、いつ戻ってくるかわからないようなクエリと同等の処理をHiveで実行すると1台でも10分やそこらで完了するケースもあります。
DB権限の制約や実際のデータ規模次第ではEMRを使って処理した方が都合が良いことが結構あったりします。

またこのような効率化をしないと特にサービスや処理項目が多い場合はかなりのコスト増になります。

例えば、
15分の処理が60個あった場合、m1.largeを1台使うとしても（この程度のものは逆に複数台あっても処理時間が変わらない、もしくは遅かったりする）完了までに

* それぞれでEMRを起動する場合：(60 \* \$0.243) + (60 \* \$0.044) = \$17.22
* ステップ追加でまとめる場合：\$17.22 / 4 = \$4.305

となり、ステップ追加でまとめる場合に比べて約\$13のコスト増で、これが30日分だと\$390もの差額になります（まあキレイに4等分される訳はないのであくまでも机上の目安ですが）。
これは円安の昨今、痛い出費です。

## まとめ
* EMRは小規模な処理をステップでまとめることによって低コストで実行可能！
* SQLやスクリプトで対応できないときはEMRが使えるかも！？
* EMRのコンセプトに逆行しているようだが気にしない

## 最後に
上記は一例ですが、こんな感じで状況に応じてSQLやスクリプト、HiveやImpalaなどを使い分けながらかなりの低コストで分析基盤を構築できています。
もちろん結果もしっかりとアウトプットできています！！（たぶん）
これが実現できているのもAmazon様々です、ほんと良い時代になった。。
今期はかなりデータ量が増えそうなのでマエショリストとしては非常に楽しみです。
それとやはり今のトレンドはSpark！
近いうちに情報共有できればと思います。

[CYBIRD エンジニア Advent
Calendar](http://qiita.com/advent-calendar/2014/cybird)
明日は、サイバードの若手のホープ！[RYad0902](http://qiita.com/RYad0902)さんです！何が飛び出してくるか楽しみです！