可視化を通じたApache Sparkアプリケーションの理解

[Spark Visualizations: DAG, Timeline Views, and Streaming Statistics](https://databricks.com/blog/2015/06/22/understanding-your-spark-application-through-visualization.html)の翻訳です。

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

*我々が決して気づかないものを気づかせてくれる時に絵画の価値は最大のものとなる - John Tukey*

かつては、Apache Spark UIはアプリケーションのデバッグを支援するものでした。[最新のSpark 1.4リリース](https://databricks.com/blog/2015/06/11/announcing-apache-spark-1-4.html)(2015年の記事です)において、Spark UIにおけるデータの可視化を発表できることを嬉しく思います。このリリースで追加された新たな可視化は以下のコンポーネントを含んでいます。

- Sparkイベントのタイムラインビュー
- 実行DAG
- Sparkストリーミングの統計情報の可視化

この記事は、2つの記事のシリーズの最初の記事となります。この記事では、最初の2つのコンポーネントをカバーし、最後の機能については[2つ目の記事](https://qiita.com/taka_yayoi/items/99bba16e95dacf0bc278)でカバーします。

# Sparkイベントのタイムラインビュー

Sparkのイベントは、以前のバージョンのSparkにおいてもユーザーに接するAPIの一部となっていました。最新のリリースでは、Spark UIはこれらのイベントを一目でわかるように、イベントの相対的な順番や配置をタイムラインとして表示します。

このタイムラインビューは3つのレベルで利用できます：*全てのジョブのタイムライン、1つのジョブのタイムライン、1つのステージのタイムライン*です。ランディングページでは、タイムラインはアプリケーションにおける全てのジョブの全てのSparkイベントを表示します。以下の例で考えてみます。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-1.55.07-PM-1024x481.png)

ここでのイベントの流れは非常にわかりやすいものです。全てのエグゼキューターが登録されたすぐ後に、アプリケーションは4つのジョブを並列で実行し、そのうちの1つが処理に失敗しますが、他のジョブは成功しています。そして、全てのジョブが完了し、アプリケーションが終了し、エグゼキューターが削除されます。それでは、1つのジョブをクリックしてみましょう。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-1.56.30-PM-1024x426.png)

このジョブは3つのファイルに対してワードカウントを実行し、最後に結果を結合(join)しています。タイムラインからは、3つのワードカウントのステージは互いに依存しないため、並列で実行されていることが明らかになっています。しかし、最後の結合は3つのステージの結果に依存しているため、対応するステージ(最後のcollect)は直前のステージが全て完了するまでは開始しません。さらに、ステージの1つを見ていきましょう。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-1.57.36-PM.png)

このステージは、4台のマシンにまたがる20のパーティション(全てを表示していません)を有しています。それぞれのバーは、ステージ内の単一のタスクを表現しています。このタイムラインビューからは、このステージに関するいくつかの洞察を得ることができます。まず、パーティションはマシンにうまく分散しています。次に、タスク実行時間の大部分は、ネットワークやI/Oのオーバーヘッドではなく純粋な計算によって占められており、ここでは非常に小さいデータをシャッフルしているので、このことは驚くことではありません。3つ目に、より多くのエグゼキューターやコアを配置することで並列度を増加させることができることがわかります。現状では、それぞれのエグゼキューターは同時に2つのタスクしか処理できていないように見えます。

このタイムラインを使ったSparkの別の機能:*ダイナミックアロケーション*を紹介させていただきたいと思います。この機能によってSparkは、クラスターがより効率的にリソースを共有できるように、ワークロードに応じてエグゼキューターの数を動的にスケールさせることができます。タイムラインで実際に見ていきましょう。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-1.59.30-PM-1024x424.png)

最初に気づくのは、事前にエグゼキューターを準備するのではなく、ジョブの途中でアプリケーションがエグゼキューターを獲得しているということです。そして、最初のジョブが完了するとすぐに、ジョブに使用された一連のエグゼキューターはアイドル状態となり、クラスターに返却されています。これによって、同じクラスターで稼働する他のアプリケーションが同時にリソースを利用できるようになり、クラスターの利用率を向上することができます。新たなジョブが投入された時にのみ、Sparkアプリケーションは実行に必要な新たなエグゼキューターのセットを獲得します。

Sparkのイベントをタイムラインで参照できる機能は、アプリケーションのボトルネックの特定に役立ちます。アプリケーションをデバッグする次のステップは、特定のタスクやステージを、それらを起動したSparkのオペレーションにマッピングするというものです。

# 実行DAG

最新のSparkリリースに追加された2番目の可視化機能はジョブごとの実行DAGを表示する機能です。Sparkでは、ジョブは以下のような有向非巡回グラフ(DAG)で表現される[RDD](https://databricks.com/blog/2016/07/14/a-tale-of-three-apache-spark-apis-rdds-dataframes-and-datasets.html)の依存関係の連鎖によって関連づけられます。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-2.00.59-PM.png)

このジョブはシンプルなワードカウントを実行します。最初にHDFSにある入力ハイルを読み込むために*textFile*オペレーションを実行します。つぎに、それぞれの行を単語に分割するために*flatMap*オペレーションを実行し、(word, 1)のペアを生成するために*map*オペレーションを実行します。そして、最後に単語のカウントを合計するために、*reduceByKey*オペレーションを実行します。

可視化において青色に塗られているボックスは、ユーザーのコードから呼び出すSparkオペレーションを示しています。ボックス内のドットは対応するオペレーションによって聖施されるRDDを表現しています。オペレーション自身は実行されるステージにグルーピングされます。

この可視化からいくつかの観察を行うことができます。最初に、パイプラインオペレーションがシャッフルによって分割されておらず、Sparkによるパイプラインの最適化が明らかとなっています。特に、HDFSから入力パーティションを読み込んだ後に、別のステージを起動しなくて済むように、それぞれのエグゼキューターは後続の*flatMap*と*map*関数を同じタスクのパーティションに直接適用しています。

次に、RDDの一つは最初のステージでキャッシュされています(緑でハイライトされています)。HDFSから読み込みを行うオペレーションが含まれているため、このRDDをキャッシュすることで、このRDDに対する今後の計算においては、HDFSからではなく、メモリー上にあるオリジナルのファイルのサブセットにアクセスすることになります。

DAGによる可視化の価値は、複雑なジョブの際に発揮されます。例として、MLlibのAlternating Least Squares(ALS)の実装では、二つの要素行列の咳を繰り返し計算することで近似値を計算します。これには、一連の*map*、*join*、*groupByKey*の処理が含まれます。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-2.02.25-PM.png)

ALSでは、それぞれの繰り返しで前回の計算結果を再利用するため、適切な場所でのキャッシュが性能の観点から重要となります。DAGの可視化によって、ユーザーや開発者は特定のRDDが適切にキャッシュされているのかどうかを一目で確認でき、キャッシュされていない場合には、なぜ実装が遅いのかを迅速に理解することができます。

タイムラインビューと同じように、DAGの可視化においては、ユーザーはステージをクリックすることで、ステージの詳細を展開することができます。以下の図ではALSにおける単一のステージのDAGを可視化しています。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-2.03.21-PM.png)

このステージビューにおいては、このステージに属するRDDの詳細が自動で展開されます。ユーザーはジョブのページの個々のドットの上にマウスを移動してチェックし、推測することなしに、迅速に特定のRDDの情報を見つけ出すことができます。

最後に、DAG可視化とSpark SQLのインテグレーションについてもハイライトしたいと思います。Spark SQLのユーザーは低レベルのSparkプリミティブよりも高レベルの物理的オペレーターに慣れ親しんでいるので、高レベルの物理的オペレーターが可視化されるべきです。この結果は、内部の実行DAGにマッピングされたSQLクエリー実行計画を想起させるものとなります。
![](https://databricks.com/wp-content/uploads/2015/06/Screen-Shot-2015-06-19-at-2.04.05-PM.png)

[Sparkストリーミング](https://databricks.com/blog/2015/07/30/diving-into-apache-spark-streamings-execution-model.html)とのインテグレーションもSpark 1.4で実装されており、別の記事でこちらはカバーします。

近い将来、Spark UIにはより適切な詳細を提供するために、高レベルのライブラリのセマンティクスを理解するようになります。Spark SQLには既存のSparkストリーミング同様に専用のタブが設けられます。Sparkのコアにおいては、RDDの上にマウスをホバーした際に、パーティションの数、コード上の呼び出し元、キャッシュの比率などが表示されるようになるでしょう。

# まとめ

最新のSpark1.4.0のリリースでは、Spark UIに様々な主要な可視化機能が追加されました。これは、アプリケーションの詳細を直感的な方法で表現することは、最初から情報を表示するのと同じくらい重要なことであるというプロジェクトの認識から生まれたものです。今後のリリースでは、Sparkのコアと、その上に構築される高レベルのライブラリのユーザーの両方がアクセスできるようにSpark UIを改善していきます。

SparkストリーミングにおけるUIの改善に関する[2つ目の記事](https://qiita.com/taka_yayoi/items/99bba16e95dacf0bc278)を楽しみにしていてください！

# 謝辞

この記事で説明した機能は、Sparkコミュニティのコントリビュータの労力による成果です。特に、NTTデータの*@sarutak*がタイムラインビューの機能の主要な作者です。

### Databricks 無料トライアル

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