Apache Sparkストリーミングアプリケーションを理解するための新たな可視化機能

[New Visualizations for Understanding Apache Spark Streaming Applications \- The Databricks Blog](https://databricks.com/blog/2015/07/08/new-visualizations-for-understanding-apache-spark-streaming-applications.html)の翻訳です。

:::note warn
本記事はSpark 1.4.0を前提としており、最新バージョンのSpark UIとは構成が異なる部分があります。
:::


前回は、Sparkアプリケーションの動作を理解するために、Apache Spark 1.4.0で導入された[新たな可視化機能](https://qiita.com/taka_yayoi/items/7d63795bbe6a0b443468)をご紹介しました。同じテーマのもと、この記事では特にSparkストリーミングのアプリケーションを理解できるように導入された新たな可視化機能をハイライトします。我々はSpark UIのストリーミングタブを以下のようにアップデートしました。

- イベントの速度、スケジューリングの遅れ、過去のバッチの処理時間のタイムライン、統計情報
- それぞれのバッチの全てのSparkジョブの詳細

さらに、ストリーミングのオペレーションの文脈におけるジョブ実行を理解するために、[実行DAGの可視化](https://qiita.com/taka_yayoi/items/7d63795bbe6a0b443468#%E5%AE%9F%E8%A1%8Cdag)が、ストリーミングの情報で拡張されています。

ストリーミングアプリケーションを分析するエンドツーエンドのサンプルを用いて、より詳細に見ていきましょう。

# 処理のトレンドに対するタイムライン、ヒストグラム

Sparkストリーミングアプリケーションをデバッグする際、ユーザーは多くの場合、データを受け取る速度と、それぞれのバッチの処理時間に興味を持ちます。ストリーミングタブの新たなUIでは、現在のメトリクスと過去の1,000個のバッチにおけるトレンドを容易に確認することができます。ストリーミングアプリケーションを実行している間、Spark UIのストリーミングタブを開くと、以下の図1のような画面が表示されることでしょう。(赤字の[A]は注釈で、UIの一部ではありません)
![](https://databricks.com/wp-content/uploads/2015/07/image1-1024x822.png)
図1: Spark UIのストリーミングタブ

最初のライン(**[A]**のマーク)にはストリーミングアプリケーションの現在の状態が表示されます。この例では、アプリケーションは、1秒のバッチ間隔でおおよそ40分稼働しています。その下の、**Input Rate**(**[B]**のマーク)には、ストリーミングアプリケーションが、ソースから49イベント/秒のスピードでデータを受け取っていることが表示されています。この例では、タイムラインは中央(**[C]**でマーク)で平均速度が少々減少しており、タイムラインの終端に向けて回復傾向が認められます。より詳細を知りたい場合には、**Input Rate**(**[B]**の近く)の側のドロップダウンをクリックして、以下の図2のように、それぞれのソースから構成されるタイムラインを表示することができます。
![](https://databricks.com/wp-content/uploads/2015/07/image2.png)
図2

図2では、アプリケーションには二つのソース(***SocketReceiver-0***と***SocketReceiver-1***)が存在し、そのうちの一つが短い期間データの受信をストップしたため、全体的な受信速度を引き下げたことがわかります。

このページの下(図1の**[D]**)をさらに見ていくと、***Processing Time***には、平均20msでバッチが処理されていることが表示されています。バッチ間隔(この例では1秒)よりも短い処理時間は、バッチは作成されるとすぐに処理されており、(図1で**[E]**で示されており、前回のバッチが完了するまでのバッチの待ち時間として定義される)***Scheduling Delay***がほぼゼロであることを意味しています。Scheduling delayはお使いのストリーミングアプリケーションが安定しているか否かを示す重要なインジケーターであり、このUIによって容易にモニタリングすることができます。

# バッチの詳細

もう一度図1を見てみると、なぜか右側に行くに従って、いくつかのバッチが完了するのに長い時間を要している(図1の**[F]**)ことを不思議に思うかもしれません。このUIを用いることで容易に分析することができます。まず初めに、タイムラインのグラフでバッチ処理時間が高い値を示している部分をクリックします。これによって、ページ内の完了バッチ一覧に移動することができます。
![](https://databricks.com/wp-content/uploads/2015/07/image3.png)
図3

これはここのバッチの詳細を表示します(図3で緑でハイライトされています)。こちらからわかるように、このバッチは他のバッチよりも長い処理時間を要しています。次に来る疑問は、どのようなSparkジョブが、このバッチの処理時間を長いものにしたのかというものです。これを調査するためには、バッチ処理時間(最初の列のブルーのリンク)をクリックします。これによって、対応するバッチの詳細情報、出力オペレーションとSparkジョブを表示します。(図4)
![](https://databricks.com/wp-content/uploads/2015/07/image4.png)
図4

上の図4からは、3つのSparkジョブを生成する出力オペレーションがあったことがわかります。さらに分析を進めるために、ジョブIDをクリックしてステージやタスクを調べることができます。

# ストリーミングRDDの実行DAG

バッチジョブのタスク、ステージの分析を始めると、実行グラフに対する深い理解が役立つことになります。[前回の記事](https://qiita.com/taka_yayoi/items/7d63795bbe6a0b443468)で説明したように、Spark 1.4.0では、RDDの依存関係および、一連の依存関係のあるステージを用いて、どのようにRDDが処理されるのかを表現するために、実行DAG(有効非巡回グラフ)の可視化が追加されました。これらのRDDがストリーミングアプリケーションのDStreamsで生成された場合には、この可視化にストリーミングのセマンティクスが追加されます。それぞれのバッチで受け取る単語をカウントする、シンプルなストリーミングのワードカウントプログラムからスタートします。[NetworkWordCount](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/NetworkWordCount.scala)のサンプルをご覧ください。ここでは、ワードカウントを計算するためにDStreamのオペレーションの***flatMap, map, reduceByKey***を使用します。Sparkジョブの実行DAGは以下の図5のようになります。
![](https://databricks.com/wp-content/uploads/2015/07/image5.png)
図5

黒のドットは、16:06:50のバッチのDStreamによって生成されたRDDを表現しています。青で塗られたボックスはRDDを変換するために使われたDStreamオペレーションを参照しており、ピンクのボックスはこれらの変換が行われるステージを表現しています。これは全体的に以下のことを示しています。

- 16:06:50のバッチにおける単一の***socket text stream***からデータを受け取っている。
- データからワードカウントを行うために、***flatMap, map, reduceByKey***の変換を用いる2つのステージから構成されたジョブ。

これはシンプルなグラフですが、より多くの入力ストリームや、***window***や***updateStateByKey***オペレーションのような高度なDStream変換が必要になってくるとグラフはさらに複雑になります。例えば、２つのsocket text streamからのデータを用いて、3つのバッチの移動ウィンドウに対してカウントを行う際には、バッチジョブの一つの実行DAGは以下の図6のようになるでしょう。
![](https://databricks.com/wp-content/uploads/2015/07/image6.png)
図6

図6は、3つのバッチからのデータに対するワードカウントを行うSparkジョブに関して多くの情報を提供します。

- 最初の3つのステージは、ウィンドウ内の3つのバッチのそぞれでワードカウントを行います。これは、***map***と***flatMap***オペレーションを用いた上述のシンプルな***NetworkWordCount***の最初のステージと同じようなものです。しかし、以下の相違点には注意してください。
    1. 2つの***socket text stream***のそれぞれから得られる2つの入力RDDが存在します。これら2つのRDDは単一のRDDに***union***され、さらにバッチごとの中間カウントに変換されます。
    1. これらのステージの2つはグレーになっていますが、これは古い2つのバッチはすでにメモリーにキャッシュされており、再計算を必要としないためです。最新のバッチのみが最初からの計算を必要とします。
- 右の最後のステージは、バッチごとのワードカウントを「ウィンドウ内の」ワードカウントに結合するために***reduceByKeyAndWindow***を使用しています。

これらの可視化によって、開発者はストリーミングアプリケーションのステータスと傾向をモニタリングできるようになり、内部のSparkジョブと実行計画の関係を理解することができます。

# 今後の方向性

Spark 1.5.0で予定されている大きな改善点は、それぞれのバッチの入力データに関してより情報を提供するというものです([JIRA](https://issues.apache.org/jira/browse/SPARK-8701), [PR](https://github.com/apache/spark/pull/7081))。例えば、Kafkaを使っている場合、バッチの詳細ページにはトピック、パーティション、バッチで処理されたオフセットが表示されることになるでしょう。こちらはプレビューです。
![](https://databricks.com/wp-content/uploads/2015/07/image7.png)
図7

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)