Copyright © 2013 NTT DATA Corporation
NTT DATA Corporation
Masatake Iwasaki
ストリームデータ分散処理基盤 「Storm」
2012年12月10日
NTTデータ オープンソースDAY 2012 講演資料
2Copyright © 2012NTT DATA Corporation
Index
1 はじめに
2 Stormとは
3 ストリームデータ処理
4 並列分散フレームワーク
5 サーバ構成と耐障害性
6 プロセス構成と並行性
7 アプリケーション開発
8 まとめ
Copyright © 2013 NTT DATA Corporation 3
はじめに
4Copyright © 2013 NTT DATA Corporation
自己紹介
岩崎 正剛(いわさき まさたけ)
株式会社NTTデータ 基盤システム事業本部
OSSプロフェッショナルサービス
HadoopをはじめとするOSS(オープンソースソフトウエア)製品
の技術開発、調査検証、コンサルテーション、サポート、構築支
援などに従事。PostgreSQLの全文検索ツール「Ludia」の開発
をはじめとして、Hadoop以外のOSSも幅広く扱う。
http://oss.nttdata.co.jp/hadoop/
5Copyright © 2013 NTT DATA Corporation
今日の内容
Stormの紹介
技術的な話が中心
Hadoopをある程度知っていることを期待
6Copyright © 2013 NTT DATA Corporation
Stormの特徴
スケールアウトする分散処理基盤
ストリーミングデータ処理に特化
リアルタイムでインクリメンタルなデータ処理
Hadoopに似ている
Hadoopよりシンプル
SPOFがない
MapReduceより柔軟な計算トポロジ
Copyright © 2013 NTT DATA Corporation 7
Stormとは
8Copyright © 2013 NTT DATA Corporation
Storm
ストリームデータ分散処理基盤
並列分散処理のためのフレームワーク
「リアルタイム処理のHadoop」?
Similar to how Hadoop provides a set of general
primitives for doing batch processing, Storm provides a
set of general primitives for doing realtime computation.
https://github.com/nathanmarz/storm/
9Copyright © 2013 NTT DATA Corporation
Hadoopとの併用
補完する関係にある。
Hadoop
very easy to sort/join/pivot large sets of data
and requires very little work to do so. Does not
address any real-time needs.
Storm
similar paradigm, but very easy to do real-time
https://groups.google.com/forum/?fromgroups=#!topic/storm-user/4it768NTvwA
10Copyright © 2013 NTT DATA Corporation
Stormの開発者
Nathan Marz氏
Twitter社(に買収されたBackType社)のエンジニア
Twitterデータを分析するサービスを提供していた
(=> 2013年3月に会社を設立)
https://github.com/nathanmarz
http://manning.com/marz/
11Copyright © 2013 NTT DATA Corporation
Stormの利用事例
http://storm-project.net/
12Copyright © 2013 NTT DATA Corporation
Stormの利用事例
https://github.com/nathanmarz/storm/wiki/Powered-By
Groupon データ結合、クレンジング、正規化
The Weather Channel 気象データサービス: データ整形、DB格納
FullContact
ソーシャルデータサービス:
外部サービスとのアドレスデータ同期
グラフ解析
Twitter
パートナー向け解析データ提供:
Tweetデータ処理、クリックデータ集計
Infochimps データ収集/配信サービス: ETL
Ooyala
動画サービス:
視聴データ解析、レコメンデーション
spider.io インターネットトラフィック解析: 不正検出
GumGum 広告プラットフォーム: イベント処理
Copyright © 2013 NTT DATA Corporation 13
ストリームデータ処理
14Copyright © 2013 NTT DATA Corporation
ストリームデータとはなにか?
ストリーム的なデータ
連続的に入力される
データ量が時間的に一定しない
保存しきれないほどの量がある
例: Tweetデータ、センサデータ
ストリーム的に処理
データを溜めずインクリメンタルに集計/抽出/加工
15Copyright © 2013 NTT DATA Corporation
Hadoopのストリームデータ処理
入力データを分散ファイルシステムに溜めて処理
溜めるためのプロダクトが存在
Flume, Scribe, Fluentd...
バッチ処理
短いバッチジョブの繰り返し
16Copyright © 2013 NTT DATA Corporation
Stormのストリームデータ処理
入力データを溜めずに処理
MQを利用する場合が多い
Kafka, Kestrel, RabbitMQ...
イベントドリブン処理
常駐ジョブが入力データをインクリメンタルに処理
Copyright © 2013 NTT DATA Corporation 17
並列分散処理フレームワーク
18Copyright © 2013 NTT DATA Corporation
フレームワークとはなにか?
「枠組み」
特定のロジックに従うプログラムの開発/実行基盤
ライブラリ: ユーザがライブラリを呼ぶ
フレームワーク: フレームワークがユーザロジックを呼ぶ
型にはめることで共通のパーツが使える
型にはめることで変なことをさせない
19Copyright © 2013 NTT DATA Corporation
なぜ並列分散処理のフレームワークが必要なのか
並列分散処理のプログラムは書くのが難しい
ノード間でのデータ交換と同期
スケーリング
エラー処理
並列分散処理のプログラムは動かすのが面倒
プログラムとデータの配布、起動と結果の回収
エラー時にゴミとして残ったプロセスとデータの消去
並列分散処理のプログラムは網羅的なテストができない
状況を分散環境で再現すること事態が困難な場合も
20Copyright © 2013 NTT DATA Corporation
HadoopのMapReduceの処理の流れ
入力データをタスクに分割
入出力はKeyValueの形式で扱う
Mapは出力をKeyで仕分けてReduceに渡す
データをすべて処理したらジョブは完了
Mapper
Mapper
Mapper
in out
Reducer
Reducer
Reducer
行番号: テキスト
"foo": 3
単語: 1
(単語に分解)
単語: カウント
(単語ごとに集計)
"foo": 1
"bar": 4
"baz": 2
"foo";: 3
"bar": 4
"baz": 2
1000: “foo bar bar”
...
2000: “bar bar baz”
...
3000: “foo foo baz”
...
"bar": 1
"bar": 1
"baz": 1
"foo": 1
"baz": 1
"bar": 1
"bar": 1
"foo": 1
21Copyright © 2013 NTT DATA Corporation
MapReduceのフレームワーク
ユーザはMapperとReducerを実装する
MapperとReducerはフレームワークが呼び出す
そこ以外はフレームワークが世話してくれる
プログラムとデータの受け渡し
状態の監視
エラー時のリトライ
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
protected void map(KEYIN key, VALUEIN value,
Context context) throws IOException, InterruptedException {
public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context
) throws IOException, InterruptedException {
22Copyright © 2013 NTT DATA Corporation
枠組みとしてのMapReduce
縛ることでスケーラビリティと開発容易性を保つ
ユーザはロジックとデータの流れの枠組みを変えられない
一見厳しい制限にみえる
各タスクは他の部分と独立に処理
Map -> Reduceの一方通行
でも意外といろいろなことができる
MapReduceの組みあわせと繰り返し
23Copyright © 2013 NTT DATA Corporation
低レイテンシな並列分散処理の背景
MapReduceの繰り返しは効率が悪い
何もせず終了するジョブでも1つ数十秒
MapReduceジョブ間のデータ受け渡しがHDFSを経由
MapReduceとは異なるフレームワークがでてきた
グラフ処理: Giraph
分散クエリ: Impala
HadoopプロジェクトもYARNを開発
ノード管理部分とアプリケーション管理部分を分離
MapReduce以外へも汎用化
24Copyright © 2013 NTT DATA Corporation
Stormの処理の流れ
Spoutは入力データを後続のBoltに流す
Boltは入力がきたら随時イベントドリブンで処理
HadoopのJob: 与えられたデータセットを処理するバッチ処理
StormのTopology: 常駐してイベントドリブン的にデータを処理
Bolt A
Bolt A
Bolt A
Bolt B
Bolt B
Bolt B
行単位に入力
foo: 3
単語に分割 単語ごとの集計
"foo"
bar: 4
baz: 2
{foo: 3,
bar: 4,
baz: 2}
集約
“foo bar bar”
“bar bar baz”
“foo foo baz”
"bar"
"bar"
"baz"
"foo"
"baz"
Spout Bolt C
"bar"
"foo"
"bar"
25Copyright © 2013 NTT DATA Corporation
Stormの計算トポロジ
MapReduceよりも自由度が高い
BoltとSpoutの組み合わせの定義はTopologyと呼ばれる
入出力はアプリケーション次第
Bolt A
Bolt A
Bolt C
Bolt C
Bolt B
Spout A
Bolt D
Spout B
Spout B
Bolt C
26Copyright © 2013 NTT DATA Corporation
データ入出力
データ入力
MQからデータを取り込むのが定番
スケールアウト可能な製品が好ましい
Kestrel, Apache Kafka, RabbitMQ...
データ出力
ストリームとして下流に流す
集計結果を永続化
KVS, RDBMS, ...
27Copyright © 2013 NTT DATA Corporation
Stormのフレームワーク
ユーザはSpoutとBoltを実装する
Spoutは無限ループで呼ばれ続け、Tupleを流す
BoltはTupleを受け取ったらexecuteを呼び出す
public interface ISpout extends Serializable {
void nextTuple();
public interface IBolt extends Serializable {
void execute(Tuple input);
28Copyright © 2013 NTT DATA Corporation
Stormノード間のデータ受け渡し
Tupleと呼ばれるデータ構造であつかう
フィールド名の付いた値の組
public interface Tuple {
public int size();
public int fieldIndex(String field);
public boolean contains(String field);
public Object getValue(int i);
public String getString(int i);
public Integer getInteger(int i);
public Long getLong(int i);
public Boolean getBoolean(int i);
...
public Object getValueByField(String field);
public String getStringByField(String field);
public Integer getIntegerByField(String field);
public Long getLongByField(String field);
public Boolean getBooleanByField(String field);
...
public List<Object> getValues();
public List<Object> select(Fields selector);
...
29Copyright © 2013 NTT DATA Corporation
Stormノード間のデータ受け渡し
後続のBoltにTupleを渡す
public class Values extends ArrayList<Object>{
public Values() {
}
public Values(Object... vals) {
super(vals.length);
for(Object o: vals) {
add(o);
}
}
}
collector.emit(new Values("foobar", 3, 0.2));
Tupleの実体は単に値の組
30Copyright © 2013 NTT DATA Corporation
グルーピング
Tupleをどの後続Boltに渡すかを決めるロジック
同じデータを複数のBoltに渡すこともできる
FIELDS: 指定されたフィールドのハッシュ値で振り分け
SHUFFLE: ランダムに振り分け
ALL: すべてのBolt同じTupleを送信
DIRECT: 振り分け先のタスクIDを明示的に指定
CUSTOM: 振り分けロジックをユーザが実装
Bolt A
Bolt B
Bolt B
Copyright © 2013 NTT DATA Corporation 31
サーバ構成と耐障害性
32Copyright © 2013 NTT DATA Corporation
Master
Slave
TaskTracker
DataNode
Master
NameNode
Slave
TaskTracker
DataNode
JobTracker
Slave
TaskTracker
DataNode
マスタースレーブ
MapReduceとHDFSの同居がポイント
データ保持ノードにタスクを渡すことで効率的にI/Oを分散
Hadoopのサーバ構成
33Copyright © 2013 NTT DATA Corporation
Slave
Master
Stormのサーバ構成
Slave
Supervisor
Slave
Supervisor
Nimbus
Supervisor
ZooKeeper
ZooKeeper
ZooKeeper
マスタースレーブ
状態管理にZooKeeperクラスタを利用
34Copyright © 2013 NTT DATA Corporation
ZooKeeper
分散システム開発のためのパーツ
小さな分散ファイルシステム
ディレクトリとファイルの区別がない
open/closeもread/writeもない
設定や状態情報を冗長化して保存
ロック、キュー、カウンタとしても
ノード間のコーディネーション
ファイルの読み書きの際にWatcherを登録
ファイルに変化があるとコールバック
35Copyright © 2013 NTT DATA Corporation
Hadoopの対障害性
データブロックのレプリカを複数ノードが持つ
スレーブノードがダウンしたら別ノードでタスクをリトライ
Slave
DataNode
TaskTracker
data block
Task
Slave
DataNode
TaskTracker
data block
Task
Slave
DataNode
TaskTracker
Master
NameNode
Master
JobTracker
36Copyright © 2013 NTT DATA Corporation
Master
Slave
TaskTracker
DataNode
Master
NameNode
Slave
TaskTracker
DataNode
JobTracker
Slave
TaskTracker
DataNode
マスターがSPOF(だった)
巨大で重要な管理情報をマスターが(メモリ上に)保持
HadoopのSPOF
ファイルシステムメタデータ登録ジョブと実行状態
37Copyright © 2013 NTT DATA Corporation
Slave
Master
StormのSPOF
Slave
Supervisor
Slave
Supervisor
Nimbus
Supervisor
ZooKeeper
ZooKeeper
ZooKeeper
SPOFなし
情報は冗長性のあるZooKeeperクラスタ上に保存
Hadoopと違い管理情報が小さくてすむため
トポロジと割り当て状態
38Copyright © 2013 NTT DATA Corporation
Slave
Master
Stormのマスター障害
Slave
Supervisor
Slave
Supervisor
Nimbus
Supervisor
ZooKeeper
ZooKeeper
ZooKeeper
マスターがいなくてもTopologyは動きつづける
単に再起動すればよい
マスターダウン中はタスクの(再)割り当てはできない
39Copyright © 2013 NTT DATA Corporation
Slave
Master
Stormのスレーブ障害
Slave
Supervisor
Slave
Supervisor
Nimbus
Supervisor
スレーブマシンまたはSupervisorプロセスのクラッシュ
マスターがスレーブを監視し、タスクを別のノードに移動
Task
Task
ZooKeeper
ZooKeeper
ZooKeeper
40Copyright © 2013 NTT DATA Corporation
Slave
Master
Stormのアプリケーションプロセス障害
Slave
Supervisor
Slave
Supervisor
Nimbus
Supervisor
SupervisorがWorkerプロセスを監視して再起動
ZooKeeper
ZooKeeper
ZooKeeper
Worker
Worker
41Copyright © 2013 NTT DATA Corporation
Ackを利用したエラー処理
SpoutからTupleを流す際にIDを与えておく
public interface ISpoutOutputCollector {
List<Integer> emit(String streamId,
List<Object> tuple,
Object messageId);
public interface ISpout extends Serializable {
void ack(Object msgId);
void fail(Object msgId);
下流のBoltがack/failを呼ぶと、Spoutに伝わる
Spout BoltMQ
1: キューからデータ取り出し 2: Tupleを流して処理
3: 処理が完了したらack
失敗したらfail
4: ackならキューからの消去をcommit
failならキャンセルしてデータを戻す
42Copyright © 2013 NTT DATA Corporation
ノード間通信とZeroMQ
スレーブ間の通信はWorker単位で管理
通信レイヤでの再送や再接続の管理にZeroMQを利用
Disrupter(後述)導入後はバッファリングなしの設定に
将来的には使わなくなる?
https://github.com/nathanmarz/storm/issues/372
43Copyright © 2013 NTT DATA Corporation
並列分散処理における対障害性の考えかた
並列分散処理ではエラーは例外的な状況ではない
落ちないプログラムではなく、落ちても平気なプログラム
フレームワークがかなり面倒をみてくれる
でも、アプリケーション側でも意識が必要
無限リトライに陥らない
一部のエラーで全体を無駄にしない
Copyright © 2013 NTT DATA Corporation 44
プロセス構成と並行性
45Copyright © 2013 NTT DATA Corporation
Hadoopアプリケーションのプロセス構成
TaskTrackerがスロット数までChildプロセスを起動
スロット数はCPUコア数を目安に設定
適切な並列度はディスクI/Oにもよる
Childプロセスがタスク(Map/Reduce)を処理
Slave
TaskTracker(プロセス)
Child(プロセス)
Task
Child(プロセス)
Task
46Copyright © 2013 NTT DATA Corporation
Stormアプリケーションのプロセス構成
Supervisorがスロット数までWorkerプロセスを起動
WorkerはExecutorスレッドを起動
Executorスレッドがタスク(BoltやSpout)を処理
Task数 > Executor数にすることも可能
Slave
Supervisor(プロセス)
Worker(プロセス) Worker(プロセス)
Executor(スレッド)
Task
Task
Executor(スレッド)
Executor(スレッド)
Task
Task
47Copyright © 2013 NTT DATA Corporation
非同期処理とDisruptor
処理スレッドと通信スレッドの間をキューで非同期化
同じWorker内で動くタスク同士のやり取りにも
キューとしてLMAX Disruptorを利用
http://lmax-exchange.github.com/disruptor/
以前はプロセス内のやりとりにもZeroMQを利用していた
性能上重要な部分
Worker
Executor
Receive
Thread
Transfer
Thread
Disruptor
Queue
Disruptor
Queue
ExecutorDisruptor
Queue
48Copyright © 2013 NTT DATA Corporation
サーバの実装
StormはJavaとClojureで実装されている
プリミティブなデータ型やインタフェースはJavaで定義
Clojureでサーバプロセスを組み上げる
Clojure
LISP系の言語
JVM言語
マルチスレッドプログラミング向きのパーツを提供
Copyright © 2013 NTT DATA Corporation 49
アプリケーション開発
50Copyright © 2013 NTT DATA Corporation
Javaで記述したHadoopアプリケーション
Mapperの定義
Job job = new Job(new Configuration(), "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
job.submit();
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
Jobの定義と起動
51Copyright © 2013 NTT DATA Corporation
Javaで記述したStormアプリケーション
Boltの定義
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("spout", new RandomSentenceSpout(), 5);
builder.setBolt("split", new SplitSentence(), 8)
.shuffleGrouping("spout");
builder.setBolt("count", new WordCount(), 12)
.fieldsGrouping("split", new Fields("word"));
Config conf = new Config();
conf.setNumWorkers(3);
StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
public static class WordCount extends BaseBasicBolt {
Map<String, Integer> counts = new HashMap<String, Integer>();
public void execute(Tuple tuple, BasicOutputCollector collector) {
String word = tuple.getString(0);
Integer count = counts.get(word);
if(count==null) count = 0;
count++;
counts.put(word, count);
collector.emit(new Values(word, count));
}
Topologyの定義と起動
52Copyright © 2013 NTT DATA Corporation
並列分散処理フレームワークにおけるDSL
MapとReduceを書くだけだから開発は簡単だといったな
=>あれは嘘だ
現実には数多くのMapperやReducerを作って組み合わせ
それなりにめんどうくさい
DSL(Domain Specific Language)がよく利用される
HadoopではHiveやPigが代表例
特定の問題に特化した抽象度の高いプログラミング言語
C、Java、Rubyといった汎用言語で実装される
アプリケーション開発の生産性向上のためのツール
53Copyright © 2013 NTT DATA Corporation
Trident
典型的なパターンのTopologyを実装するためのAPI
射影、結合、集約、分岐、フィルタ、トランザクション
Spout/Operation/Partitionの3種のノードでグラフ構造を定義
FluentスタイルのAPI呼び出しでアプリケーションを記述
(内部DSLに分類される)
TridentTopology topology = new TridentTopology();
TridentState wordCounts =
topology.newStream("spout1", spout)
.parallelismHint(16)
.each(new Fields("sentence"), new Split(), new Fields("word"))
.groupBy(new Fields("word"))
.persistentAggregate(new MemoryMapState.Factory(),
new Count(),
new Fields("count"))
.parallelismHint(16);
54Copyright © 2013 NTT DATA Corporation
Clojure DSL
コードをClojureで簡潔に記述できる内部DSL
Clojureのマクロで実装
Clojureで書く利点
Clojureの機能が使える
パターンマッチング、リスト内包表記、遅延評価、マクロ…
並行処理用のパーツはSpoutやBoltでは出番が少なそう
DSL的な何かを作ることに定評のあるLISP系言語
=>恩恵を受けるのはエンドユーザよりも周辺ツール開発者?
55Copyright © 2013 NTT DATA Corporation
Clojure DSLで記述したStormアプリケーション
Boltの定義
(defn -main
([name]
(StormSubmitter/submitTopology
name
{TOPOLOGY-WORKERS 3}
(topology
{"spout" (spout-spec sentence-spout :p 5)}
{"split" (bolt-spec {"spout" :shuffle } split-sentence :p 8)
"count" (bolt-spec {"split" ["word"]} word-count:p 12)}
)))))
(defbolt word-count ["word" "count"] {:prepare true}
[conf context collector]
(let [counts (atom {})]
(bolt
(execute [tuple]
(let [word (.getString tuple 0)]
(swap! counts (partial merge-with +) {word 1})
(emit-bolt! collector [word (@counts word)] :anchor tuple)
(ack! collector tuple)
)))))
Topologyの定義と起動
56Copyright © 2013 NTT DATA Corporation
DSLの特徴
内部DSL: (Clojure DSL、Tridentはこちら)
母体の言語の枠組み上で動く
構文上の工夫などで独自言語風にみせる
呼び出し方に独自ルールにあるライブラリ?
外部DSL: (Hive、Pigはこちら)
独自の言語処理系を実装
構文解析して構文木をつくり実行
自由な構文が定義できる反面:
エディタ、デバッガといった開発ツールがない
任意のロジックを記述できない(または煩雑)
57Copyright © 2013 NTT DATA Corporation
ShellBolt
Boltを外部コマンドとして起動
標準入出力経由のデータ受け渡しで制御
Hadoop Streamingと同じ
任意のプログラミング言語でアプリケーションが書ける
{"command": "next"}
{
"command": "emit",
"id": "12345",
"stream": "1",
"task": 3,
"tuple": ["foobar", 3, 0.2]
}
子プロセスに渡すコマンドの例:
子プロセスからの返却値の例:
58Copyright © 2013 NTT DATA Corporation
ローカル環境でのTopologyの実行
分散環境
StormSubmitter.submitTopology("word-count",
conf,
builder.createTopology());
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("word-count",
conf,
builder.createTopology());
ローカル実行
Copyright © 2013 NTT DATA Corporation 59
まとめ
60Copyright © 2013 NTT DATA Corporation
Stormのよいところ
スケール可能な並列分散処理フレームワーク
Hadoopができないストリームデータの逐次処理をカバー
対障害性が高い
コードベースがコンパクトでシンプル
特に巨大なHadoopのコードベースと比較して
Clojureによる実装
読んで楽しい(人による)
61Copyright © 2013 NTT DATA Corporation
大規模データ処理の広がり
大規模データ処理にHadoopは広く普及した
並列分散処理の開発と運用が実用的に
並列分散処理におけるフレームワークの重要性
汎用のパーツを再利用
複雑さを抑えて(普通の)人間に把握可能に
Hadoop(MapReduce)以外の選択肢も増えてきた
解決すべき問題に応じて使い分け
Copyright © 2011 NTT DATA Corporation
Copyright © 2012 NTT DATA Corporation

ストリームデータ分散処理基盤Storm

  • 1.
    Copyright © 2013NTT DATA Corporation NTT DATA Corporation Masatake Iwasaki ストリームデータ分散処理基盤 「Storm」 2012年12月10日 NTTデータ オープンソースDAY 2012 講演資料
  • 2.
    2Copyright © 2012NTTDATA Corporation Index 1 はじめに 2 Stormとは 3 ストリームデータ処理 4 並列分散フレームワーク 5 サーバ構成と耐障害性 6 プロセス構成と並行性 7 アプリケーション開発 8 まとめ
  • 3.
    Copyright © 2013NTT DATA Corporation 3 はじめに
  • 4.
    4Copyright © 2013NTT DATA Corporation 自己紹介 岩崎 正剛(いわさき まさたけ) 株式会社NTTデータ 基盤システム事業本部 OSSプロフェッショナルサービス HadoopをはじめとするOSS(オープンソースソフトウエア)製品 の技術開発、調査検証、コンサルテーション、サポート、構築支 援などに従事。PostgreSQLの全文検索ツール「Ludia」の開発 をはじめとして、Hadoop以外のOSSも幅広く扱う。 http://oss.nttdata.co.jp/hadoop/
  • 5.
    5Copyright © 2013NTT DATA Corporation 今日の内容 Stormの紹介 技術的な話が中心 Hadoopをある程度知っていることを期待
  • 6.
    6Copyright © 2013NTT DATA Corporation Stormの特徴 スケールアウトする分散処理基盤 ストリーミングデータ処理に特化 リアルタイムでインクリメンタルなデータ処理 Hadoopに似ている Hadoopよりシンプル SPOFがない MapReduceより柔軟な計算トポロジ
  • 7.
    Copyright © 2013NTT DATA Corporation 7 Stormとは
  • 8.
    8Copyright © 2013NTT DATA Corporation Storm ストリームデータ分散処理基盤 並列分散処理のためのフレームワーク 「リアルタイム処理のHadoop」? Similar to how Hadoop provides a set of general primitives for doing batch processing, Storm provides a set of general primitives for doing realtime computation. https://github.com/nathanmarz/storm/
  • 9.
    9Copyright © 2013NTT DATA Corporation Hadoopとの併用 補完する関係にある。 Hadoop very easy to sort/join/pivot large sets of data and requires very little work to do so. Does not address any real-time needs. Storm similar paradigm, but very easy to do real-time https://groups.google.com/forum/?fromgroups=#!topic/storm-user/4it768NTvwA
  • 10.
    10Copyright © 2013NTT DATA Corporation Stormの開発者 Nathan Marz氏 Twitter社(に買収されたBackType社)のエンジニア Twitterデータを分析するサービスを提供していた (=> 2013年3月に会社を設立) https://github.com/nathanmarz http://manning.com/marz/
  • 11.
    11Copyright © 2013NTT DATA Corporation Stormの利用事例 http://storm-project.net/
  • 12.
    12Copyright © 2013NTT DATA Corporation Stormの利用事例 https://github.com/nathanmarz/storm/wiki/Powered-By Groupon データ結合、クレンジング、正規化 The Weather Channel 気象データサービス: データ整形、DB格納 FullContact ソーシャルデータサービス: 外部サービスとのアドレスデータ同期 グラフ解析 Twitter パートナー向け解析データ提供: Tweetデータ処理、クリックデータ集計 Infochimps データ収集/配信サービス: ETL Ooyala 動画サービス: 視聴データ解析、レコメンデーション spider.io インターネットトラフィック解析: 不正検出 GumGum 広告プラットフォーム: イベント処理
  • 13.
    Copyright © 2013NTT DATA Corporation 13 ストリームデータ処理
  • 14.
    14Copyright © 2013NTT DATA Corporation ストリームデータとはなにか? ストリーム的なデータ 連続的に入力される データ量が時間的に一定しない 保存しきれないほどの量がある 例: Tweetデータ、センサデータ ストリーム的に処理 データを溜めずインクリメンタルに集計/抽出/加工
  • 15.
    15Copyright © 2013NTT DATA Corporation Hadoopのストリームデータ処理 入力データを分散ファイルシステムに溜めて処理 溜めるためのプロダクトが存在 Flume, Scribe, Fluentd... バッチ処理 短いバッチジョブの繰り返し
  • 16.
    16Copyright © 2013NTT DATA Corporation Stormのストリームデータ処理 入力データを溜めずに処理 MQを利用する場合が多い Kafka, Kestrel, RabbitMQ... イベントドリブン処理 常駐ジョブが入力データをインクリメンタルに処理
  • 17.
    Copyright © 2013NTT DATA Corporation 17 並列分散処理フレームワーク
  • 18.
    18Copyright © 2013NTT DATA Corporation フレームワークとはなにか? 「枠組み」 特定のロジックに従うプログラムの開発/実行基盤 ライブラリ: ユーザがライブラリを呼ぶ フレームワーク: フレームワークがユーザロジックを呼ぶ 型にはめることで共通のパーツが使える 型にはめることで変なことをさせない
  • 19.
    19Copyright © 2013NTT DATA Corporation なぜ並列分散処理のフレームワークが必要なのか 並列分散処理のプログラムは書くのが難しい ノード間でのデータ交換と同期 スケーリング エラー処理 並列分散処理のプログラムは動かすのが面倒 プログラムとデータの配布、起動と結果の回収 エラー時にゴミとして残ったプロセスとデータの消去 並列分散処理のプログラムは網羅的なテストができない 状況を分散環境で再現すること事態が困難な場合も
  • 20.
    20Copyright © 2013NTT DATA Corporation HadoopのMapReduceの処理の流れ 入力データをタスクに分割 入出力はKeyValueの形式で扱う Mapは出力をKeyで仕分けてReduceに渡す データをすべて処理したらジョブは完了 Mapper Mapper Mapper in out Reducer Reducer Reducer 行番号: テキスト "foo": 3 単語: 1 (単語に分解) 単語: カウント (単語ごとに集計) "foo": 1 "bar": 4 "baz": 2 "foo";: 3 "bar": 4 "baz": 2 1000: “foo bar bar” ... 2000: “bar bar baz” ... 3000: “foo foo baz” ... "bar": 1 "bar": 1 "baz": 1 "foo": 1 "baz": 1 "bar": 1 "bar": 1 "foo": 1
  • 21.
    21Copyright © 2013NTT DATA Corporation MapReduceのフレームワーク ユーザはMapperとReducerを実装する MapperとReducerはフレームワークが呼び出す そこ以外はフレームワークが世話してくれる プログラムとデータの受け渡し 状態の監視 エラー時のリトライ public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> { protected void map(KEYIN key, VALUEIN value, Context context) throws IOException, InterruptedException { public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> { protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context ) throws IOException, InterruptedException {
  • 22.
    22Copyright © 2013NTT DATA Corporation 枠組みとしてのMapReduce 縛ることでスケーラビリティと開発容易性を保つ ユーザはロジックとデータの流れの枠組みを変えられない 一見厳しい制限にみえる 各タスクは他の部分と独立に処理 Map -> Reduceの一方通行 でも意外といろいろなことができる MapReduceの組みあわせと繰り返し
  • 23.
    23Copyright © 2013NTT DATA Corporation 低レイテンシな並列分散処理の背景 MapReduceの繰り返しは効率が悪い 何もせず終了するジョブでも1つ数十秒 MapReduceジョブ間のデータ受け渡しがHDFSを経由 MapReduceとは異なるフレームワークがでてきた グラフ処理: Giraph 分散クエリ: Impala HadoopプロジェクトもYARNを開発 ノード管理部分とアプリケーション管理部分を分離 MapReduce以外へも汎用化
  • 24.
    24Copyright © 2013NTT DATA Corporation Stormの処理の流れ Spoutは入力データを後続のBoltに流す Boltは入力がきたら随時イベントドリブンで処理 HadoopのJob: 与えられたデータセットを処理するバッチ処理 StormのTopology: 常駐してイベントドリブン的にデータを処理 Bolt A Bolt A Bolt A Bolt B Bolt B Bolt B 行単位に入力 foo: 3 単語に分割 単語ごとの集計 "foo" bar: 4 baz: 2 {foo: 3, bar: 4, baz: 2} 集約 “foo bar bar” “bar bar baz” “foo foo baz” "bar" "bar" "baz" "foo" "baz" Spout Bolt C "bar" "foo" "bar"
  • 25.
    25Copyright © 2013NTT DATA Corporation Stormの計算トポロジ MapReduceよりも自由度が高い BoltとSpoutの組み合わせの定義はTopologyと呼ばれる 入出力はアプリケーション次第 Bolt A Bolt A Bolt C Bolt C Bolt B Spout A Bolt D Spout B Spout B Bolt C
  • 26.
    26Copyright © 2013NTT DATA Corporation データ入出力 データ入力 MQからデータを取り込むのが定番 スケールアウト可能な製品が好ましい Kestrel, Apache Kafka, RabbitMQ... データ出力 ストリームとして下流に流す 集計結果を永続化 KVS, RDBMS, ...
  • 27.
    27Copyright © 2013NTT DATA Corporation Stormのフレームワーク ユーザはSpoutとBoltを実装する Spoutは無限ループで呼ばれ続け、Tupleを流す BoltはTupleを受け取ったらexecuteを呼び出す public interface ISpout extends Serializable { void nextTuple(); public interface IBolt extends Serializable { void execute(Tuple input);
  • 28.
    28Copyright © 2013NTT DATA Corporation Stormノード間のデータ受け渡し Tupleと呼ばれるデータ構造であつかう フィールド名の付いた値の組 public interface Tuple { public int size(); public int fieldIndex(String field); public boolean contains(String field); public Object getValue(int i); public String getString(int i); public Integer getInteger(int i); public Long getLong(int i); public Boolean getBoolean(int i); ... public Object getValueByField(String field); public String getStringByField(String field); public Integer getIntegerByField(String field); public Long getLongByField(String field); public Boolean getBooleanByField(String field); ... public List<Object> getValues(); public List<Object> select(Fields selector); ...
  • 29.
    29Copyright © 2013NTT DATA Corporation Stormノード間のデータ受け渡し 後続のBoltにTupleを渡す public class Values extends ArrayList<Object>{ public Values() { } public Values(Object... vals) { super(vals.length); for(Object o: vals) { add(o); } } } collector.emit(new Values("foobar", 3, 0.2)); Tupleの実体は単に値の組
  • 30.
    30Copyright © 2013NTT DATA Corporation グルーピング Tupleをどの後続Boltに渡すかを決めるロジック 同じデータを複数のBoltに渡すこともできる FIELDS: 指定されたフィールドのハッシュ値で振り分け SHUFFLE: ランダムに振り分け ALL: すべてのBolt同じTupleを送信 DIRECT: 振り分け先のタスクIDを明示的に指定 CUSTOM: 振り分けロジックをユーザが実装 Bolt A Bolt B Bolt B
  • 31.
    Copyright © 2013NTT DATA Corporation 31 サーバ構成と耐障害性
  • 32.
    32Copyright © 2013NTT DATA Corporation Master Slave TaskTracker DataNode Master NameNode Slave TaskTracker DataNode JobTracker Slave TaskTracker DataNode マスタースレーブ MapReduceとHDFSの同居がポイント データ保持ノードにタスクを渡すことで効率的にI/Oを分散 Hadoopのサーバ構成
  • 33.
    33Copyright © 2013NTT DATA Corporation Slave Master Stormのサーバ構成 Slave Supervisor Slave Supervisor Nimbus Supervisor ZooKeeper ZooKeeper ZooKeeper マスタースレーブ 状態管理にZooKeeperクラスタを利用
  • 34.
    34Copyright © 2013NTT DATA Corporation ZooKeeper 分散システム開発のためのパーツ 小さな分散ファイルシステム ディレクトリとファイルの区別がない open/closeもread/writeもない 設定や状態情報を冗長化して保存 ロック、キュー、カウンタとしても ノード間のコーディネーション ファイルの読み書きの際にWatcherを登録 ファイルに変化があるとコールバック
  • 35.
    35Copyright © 2013NTT DATA Corporation Hadoopの対障害性 データブロックのレプリカを複数ノードが持つ スレーブノードがダウンしたら別ノードでタスクをリトライ Slave DataNode TaskTracker data block Task Slave DataNode TaskTracker data block Task Slave DataNode TaskTracker Master NameNode Master JobTracker
  • 36.
    36Copyright © 2013NTT DATA Corporation Master Slave TaskTracker DataNode Master NameNode Slave TaskTracker DataNode JobTracker Slave TaskTracker DataNode マスターがSPOF(だった) 巨大で重要な管理情報をマスターが(メモリ上に)保持 HadoopのSPOF ファイルシステムメタデータ登録ジョブと実行状態
  • 37.
    37Copyright © 2013NTT DATA Corporation Slave Master StormのSPOF Slave Supervisor Slave Supervisor Nimbus Supervisor ZooKeeper ZooKeeper ZooKeeper SPOFなし 情報は冗長性のあるZooKeeperクラスタ上に保存 Hadoopと違い管理情報が小さくてすむため トポロジと割り当て状態
  • 38.
    38Copyright © 2013NTT DATA Corporation Slave Master Stormのマスター障害 Slave Supervisor Slave Supervisor Nimbus Supervisor ZooKeeper ZooKeeper ZooKeeper マスターがいなくてもTopologyは動きつづける 単に再起動すればよい マスターダウン中はタスクの(再)割り当てはできない
  • 39.
    39Copyright © 2013NTT DATA Corporation Slave Master Stormのスレーブ障害 Slave Supervisor Slave Supervisor Nimbus Supervisor スレーブマシンまたはSupervisorプロセスのクラッシュ マスターがスレーブを監視し、タスクを別のノードに移動 Task Task ZooKeeper ZooKeeper ZooKeeper
  • 40.
    40Copyright © 2013NTT DATA Corporation Slave Master Stormのアプリケーションプロセス障害 Slave Supervisor Slave Supervisor Nimbus Supervisor SupervisorがWorkerプロセスを監視して再起動 ZooKeeper ZooKeeper ZooKeeper Worker Worker
  • 41.
    41Copyright © 2013NTT DATA Corporation Ackを利用したエラー処理 SpoutからTupleを流す際にIDを与えておく public interface ISpoutOutputCollector { List<Integer> emit(String streamId, List<Object> tuple, Object messageId); public interface ISpout extends Serializable { void ack(Object msgId); void fail(Object msgId); 下流のBoltがack/failを呼ぶと、Spoutに伝わる Spout BoltMQ 1: キューからデータ取り出し 2: Tupleを流して処理 3: 処理が完了したらack 失敗したらfail 4: ackならキューからの消去をcommit failならキャンセルしてデータを戻す
  • 42.
    42Copyright © 2013NTT DATA Corporation ノード間通信とZeroMQ スレーブ間の通信はWorker単位で管理 通信レイヤでの再送や再接続の管理にZeroMQを利用 Disrupter(後述)導入後はバッファリングなしの設定に 将来的には使わなくなる? https://github.com/nathanmarz/storm/issues/372
  • 43.
    43Copyright © 2013NTT DATA Corporation 並列分散処理における対障害性の考えかた 並列分散処理ではエラーは例外的な状況ではない 落ちないプログラムではなく、落ちても平気なプログラム フレームワークがかなり面倒をみてくれる でも、アプリケーション側でも意識が必要 無限リトライに陥らない 一部のエラーで全体を無駄にしない
  • 44.
    Copyright © 2013NTT DATA Corporation 44 プロセス構成と並行性
  • 45.
    45Copyright © 2013NTT DATA Corporation Hadoopアプリケーションのプロセス構成 TaskTrackerがスロット数までChildプロセスを起動 スロット数はCPUコア数を目安に設定 適切な並列度はディスクI/Oにもよる Childプロセスがタスク(Map/Reduce)を処理 Slave TaskTracker(プロセス) Child(プロセス) Task Child(プロセス) Task
  • 46.
    46Copyright © 2013NTT DATA Corporation Stormアプリケーションのプロセス構成 Supervisorがスロット数までWorkerプロセスを起動 WorkerはExecutorスレッドを起動 Executorスレッドがタスク(BoltやSpout)を処理 Task数 > Executor数にすることも可能 Slave Supervisor(プロセス) Worker(プロセス) Worker(プロセス) Executor(スレッド) Task Task Executor(スレッド) Executor(スレッド) Task Task
  • 47.
    47Copyright © 2013NTT DATA Corporation 非同期処理とDisruptor 処理スレッドと通信スレッドの間をキューで非同期化 同じWorker内で動くタスク同士のやり取りにも キューとしてLMAX Disruptorを利用 http://lmax-exchange.github.com/disruptor/ 以前はプロセス内のやりとりにもZeroMQを利用していた 性能上重要な部分 Worker Executor Receive Thread Transfer Thread Disruptor Queue Disruptor Queue ExecutorDisruptor Queue
  • 48.
    48Copyright © 2013NTT DATA Corporation サーバの実装 StormはJavaとClojureで実装されている プリミティブなデータ型やインタフェースはJavaで定義 Clojureでサーバプロセスを組み上げる Clojure LISP系の言語 JVM言語 マルチスレッドプログラミング向きのパーツを提供
  • 49.
    Copyright © 2013NTT DATA Corporation 49 アプリケーション開発
  • 50.
    50Copyright © 2013NTT DATA Corporation Javaで記述したHadoopアプリケーション Mapperの定義 Job job = new Job(new Configuration(), "word count"); job.setJarByClass(WordCount.class); job.setMapperClass(TokenizerMapper.class); job.setCombinerClass(IntSumReducer.class); job.setReducerClass(IntSumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(otherArgs[0])); FileOutputFormat.setOutputPath(job, new Path(otherArgs[1])); job.submit(); public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{ private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(Object key, Text value, Context context ) throws IOException, InterruptedException { StringTokenizer itr = new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, one); } } Jobの定義と起動
  • 51.
    51Copyright © 2013NTT DATA Corporation Javaで記述したStormアプリケーション Boltの定義 TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("spout", new RandomSentenceSpout(), 5); builder.setBolt("split", new SplitSentence(), 8) .shuffleGrouping("spout"); builder.setBolt("count", new WordCount(), 12) .fieldsGrouping("split", new Fields("word")); Config conf = new Config(); conf.setNumWorkers(3); StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); public static class WordCount extends BaseBasicBolt { Map<String, Integer> counts = new HashMap<String, Integer>(); public void execute(Tuple tuple, BasicOutputCollector collector) { String word = tuple.getString(0); Integer count = counts.get(word); if(count==null) count = 0; count++; counts.put(word, count); collector.emit(new Values(word, count)); } Topologyの定義と起動
  • 52.
    52Copyright © 2013NTT DATA Corporation 並列分散処理フレームワークにおけるDSL MapとReduceを書くだけだから開発は簡単だといったな =>あれは嘘だ 現実には数多くのMapperやReducerを作って組み合わせ それなりにめんどうくさい DSL(Domain Specific Language)がよく利用される HadoopではHiveやPigが代表例 特定の問題に特化した抽象度の高いプログラミング言語 C、Java、Rubyといった汎用言語で実装される アプリケーション開発の生産性向上のためのツール
  • 53.
    53Copyright © 2013NTT DATA Corporation Trident 典型的なパターンのTopologyを実装するためのAPI 射影、結合、集約、分岐、フィルタ、トランザクション Spout/Operation/Partitionの3種のノードでグラフ構造を定義 FluentスタイルのAPI呼び出しでアプリケーションを記述 (内部DSLに分類される) TridentTopology topology = new TridentTopology(); TridentState wordCounts = topology.newStream("spout1", spout) .parallelismHint(16) .each(new Fields("sentence"), new Split(), new Fields("word")) .groupBy(new Fields("word")) .persistentAggregate(new MemoryMapState.Factory(), new Count(), new Fields("count")) .parallelismHint(16);
  • 54.
    54Copyright © 2013NTT DATA Corporation Clojure DSL コードをClojureで簡潔に記述できる内部DSL Clojureのマクロで実装 Clojureで書く利点 Clojureの機能が使える パターンマッチング、リスト内包表記、遅延評価、マクロ… 並行処理用のパーツはSpoutやBoltでは出番が少なそう DSL的な何かを作ることに定評のあるLISP系言語 =>恩恵を受けるのはエンドユーザよりも周辺ツール開発者?
  • 55.
    55Copyright © 2013NTT DATA Corporation Clojure DSLで記述したStormアプリケーション Boltの定義 (defn -main ([name] (StormSubmitter/submitTopology name {TOPOLOGY-WORKERS 3} (topology {"spout" (spout-spec sentence-spout :p 5)} {"split" (bolt-spec {"spout" :shuffle } split-sentence :p 8) "count" (bolt-spec {"split" ["word"]} word-count:p 12)} ))))) (defbolt word-count ["word" "count"] {:prepare true} [conf context collector] (let [counts (atom {})] (bolt (execute [tuple] (let [word (.getString tuple 0)] (swap! counts (partial merge-with +) {word 1}) (emit-bolt! collector [word (@counts word)] :anchor tuple) (ack! collector tuple) ))))) Topologyの定義と起動
  • 56.
    56Copyright © 2013NTT DATA Corporation DSLの特徴 内部DSL: (Clojure DSL、Tridentはこちら) 母体の言語の枠組み上で動く 構文上の工夫などで独自言語風にみせる 呼び出し方に独自ルールにあるライブラリ? 外部DSL: (Hive、Pigはこちら) 独自の言語処理系を実装 構文解析して構文木をつくり実行 自由な構文が定義できる反面: エディタ、デバッガといった開発ツールがない 任意のロジックを記述できない(または煩雑)
  • 57.
    57Copyright © 2013NTT DATA Corporation ShellBolt Boltを外部コマンドとして起動 標準入出力経由のデータ受け渡しで制御 Hadoop Streamingと同じ 任意のプログラミング言語でアプリケーションが書ける {"command": "next"} { "command": "emit", "id": "12345", "stream": "1", "task": 3, "tuple": ["foobar", 3, 0.2] } 子プロセスに渡すコマンドの例: 子プロセスからの返却値の例:
  • 58.
    58Copyright © 2013NTT DATA Corporation ローカル環境でのTopologyの実行 分散環境 StormSubmitter.submitTopology("word-count", conf, builder.createTopology()); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("word-count", conf, builder.createTopology()); ローカル実行
  • 59.
    Copyright © 2013NTT DATA Corporation 59 まとめ
  • 60.
    60Copyright © 2013NTT DATA Corporation Stormのよいところ スケール可能な並列分散処理フレームワーク Hadoopができないストリームデータの逐次処理をカバー 対障害性が高い コードベースがコンパクトでシンプル 特に巨大なHadoopのコードベースと比較して Clojureによる実装 読んで楽しい(人による)
  • 61.
    61Copyright © 2013NTT DATA Corporation 大規模データ処理の広がり 大規模データ処理にHadoopは広く普及した 並列分散処理の開発と運用が実用的に 並列分散処理におけるフレームワークの重要性 汎用のパーツを再利用 複雑さを抑えて(普通の)人間に把握可能に Hadoop(MapReduce)以外の選択肢も増えてきた 解決すべき問題に応じて使い分け
  • 62.
    Copyright © 2011NTT DATA Corporation Copyright © 2012 NTT DATA Corporation