Glue ETLジョブの監視どうする？

# はじめに
Glue ETLジョブの監視を行う際にどのようにすれば良いのかをまとめていこうと思います。

# 監視方法
## 結論
監視といえど、様々な観点での監視があると思います。
今回は、ジョブ自体の動作として正常終了したのか、異常終了したのかを監視する時の方法を述べます。

**結論は、CloudWatch Eventや、EventBridgeを使用した監視で実現できます。**
Glue ETLジョブのエラーは、CloudWatchイベントとして送信され、これを検知して通知を行うことができます。
[参考: ドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/EventTypes.html#glue-event-types)

なので、CloudWatchイベントのルールを作成し、イベントパターンで絞り、awsでマネージドされているGlue ETLジョブの結果を指定し、CloudWatchイベントのtargetでイベントを検知した際に検知を通知するようなやり方をすれば良いということになります。


実際にどのようなGlue ETLジョブの結果が返ってくるのかというと下記のようなものを得られます。
この結果は、ジョブが実行に失敗した時のものです。
なので、イベントパターンを指定する際には、「"detail-type":"Glue Job State Change"」, 「"severity":"ERROR"」もしくは、「"state":"FAILED"」で絞ることで、ジョブの実行に失敗した時に検知した結果を通知する、といったことができます。

```
{
    "version":"0",
    "id":"abcdef01-1234-5678-9abc-def012345678",
    "detail-type":"Glue Job State Change",
    "source":"aws.glue",
    "account":"123456789012",
    "time":"2017-09-07T06:02:03Z",
    "region":"us-west-2",
    "resources":[],
    "detail":{
        "jobName":"MyJob",
        "severity":"ERROR",
        "state":"FAILED",
        "jobRunId":"jr_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
        "message":"JobName:MyJob and JobRunId:jr_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef failed to execute with exception Role arn:aws:iam::123456789012:role/Glue_Role should be given assume role permissions for Glue Service."
    }
}
```

# 他の観点での監視
* チューニングの目安になるような、例えば、CPUの負荷率をみたり、メモリの使用量などを監視したい時については、CloudWatchメトリクスで取れる項目を基に監視を行うことがいいと思います。
* 下記に、チューニングの目安として選択するのに良さそうなメトリクスの項目と説明を記載しておきます。

### 注意点
ただ、ここで気を付けなければならないことがあります。
**現在(2021/02/04 時点)において、Glue ETLジョブのtypeである、「python shell」typeのジョブに関しては、「Spark」typeのジョブのようなメトリクスを出力する機能がありません。**

## glueのメトリクス説明
<table>
<tr>
  <th>メトリクス名</th>
  <th>説明</th>
</tr>
<tr>
  <td>glue.driver.aggregate.bytesRead</td>
  <td>実行されている完了したSparkタスクによってすべてのデータソースから読み取られたバイト数。<br/>有効な統計はsum。<br/>sparkタスクの最後に更新される。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.elapsedTime</td>
  <td>ミリ秒単位のETL経過時間。<br/>有効な統計はsum。<br/>ジョブの実行に平均してかかる時間を決定するために使用できる。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.numCompletedStages</td>
  <td>ジョブで完了したステージ数。<br/>有効な統計はsum。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.numCompletedTasks</td>
  <td>ジョブで完了したタスク数。<br/>有効な統計はsum。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.numFailedTasks</td>
  <td>失敗したタスク数。<br/>有効な統計はsum。<br/><br/>監視により分かるもの<br/>・ジョブタスクの失敗の原因となるデータの異常。<br/>・ジョブタスクの失敗の原因となるクラスターの異常。<br/>・ジョブタスクの失敗を引き起こすスクリプトの異常。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.numKilledTasks</td>
  <td>強制終了したタスク数。<br/>有効な統計はsum。<br/><br/>監視により分かるもの<br/>・タスクを強制終了する例外, メモリ不足状態(OOM)が発生するデータスキューの異常。<br/>・タスクを強制終了する例外, メモリ不足状態(OOM)を発生させるスクリプトの異常。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.recordsRead</td>
  <td>全てのexecutorsで実行されている完了した全てのSparkタスクの全てのデータリソースから読みこまれたレコード数。<br/>有効な統計はsum。<br/>sparkタスクの最後に更新される。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.shuffleBytesWritten</td>
  <td>前回のレポート以降で、データをシャッフル(大規模な結合、groupBy、再パーティション化、合体)するためにずべてのexecutorsによって書き込まれたバイト数。<br/>有効な統計はsum。</td>
</tr>
<tr>
  <td>glue.driver.aggregate.shuffleLocalBytesRead</td>
  <td>前回のレポート以降で、データをシャッフル(大規模な結合、groupBy、再パーティション化、合体)するためにずべてのexecutorsによって読み込まれたバイト数。<br/>有効な統計はsum。</td>
</tr>
<tr>
  <td>glue.driver.BlockManager.disk.diskSpaceUsed_MB</td>
  <td>全てのexecutorsで、使用されるディスク領域の平均メガバイト数。<br/>有効な統計はAverage。<br/><br/>監視により分かるもの<br/>・キャッシュされたRDDパーティションを表すブロックに使用されるディスク容量。<br/>・中間シャッフル出力を表すブロックに使用されるディスク容量。<br/>・ブロードキャストを表すブロックに使用されるディスク容量。</td>
</tr>
<tr>
  <td>glue.driver.ExecutorAllocationManager.executors.numberAllExecutors</td>
  <td>アクティブに実行されているジョブexecutorsの数。<br/>有効な統計はAverage。</td>
</tr>
<tr>
  <td>glue.driver.ExecutorAllocationManager.executors.numberMaxNeededExecutors</td>
  <td>現在の負荷を満たすために必要な最大（アクティブに実行中および保留中）のジョブエグゼキュータの数<br/>有効な統計はsum。</td>
</tr>
<tr>
  <td>glue.driver.jvm.heap.usage</td>
  <td>ドライバー（スケール：0-1）のJVMヒープがドライバーに使用するメモリーの平均割合。（スケール：0-1）のパーセンテージ表示。<br/>有効な統計はAverage。<br/>ドライバのメモリ不足状態（OOM）を特定できる。</td>
</tr>
<tr>
  <td>glue.ALL.jvm.heap.usage</td>
  <td>すべてのエグゼキュータについて、このドライバ（スケール：0-1）のJVMヒープによって使用されるメモリの平均割合。（スケール：0-1）のパーセンテージ表示。<br/>有効な統計はAverage。<br/>エグゼキュータのメモリ不足状態（OOM）を特定できる。</td>
</tr>
<tr>
  <td>glue.driver.jvm.heap.used</td>
  <td>ドライバーのJVMヒープによって使用されるメモリバイト数。<br/>有効な統計はAverage。<br/>ドライバーのメモリ不足状態（OOM）を特定できる。</td>
</tr>
<tr>
  <td>glue.ALL.jvm.heap.used</td>
  <td>すべてのエグゼキュータのJVMヒープによって使用されるメモリバイト数。<br/>有効な統計はAverage。<br/>エグゼキュータのメモリ不足状態（OOM）を特定できる。</td>
</tr>
<tr>
  <td>glue.driver.s3.filesystem.write_bytes</td>
  <td>前回のレポートから、ドライバーによってAmazonS3に書き込まれた平均バイト数。<br/>有効な統計はsum。<br/><br/>監視により分かるもの<br/>・ジョブの問題（データの処理、再処理、およびスキップ）。<br/>・外部データソースからの読み取りと取り込み率の比較。<br/>・ジョブ実行間の差異。</td>
</tr>
<tr>
  <td>glue.ALL.s3.filesystem.write_bytes</td>
  <td>前回のレポートから、すべてのエグゼキュータによってAmazonS3に書き込まれた平均バイト数。<br/>有効な統計はsum。<br/><br/>監視により分かるもの<br/>・ジョブの問題（データの処理、再処理、およびスキップ）。<br/>・外部データソースからの読み取りと取り込み率の比較。<br/>・ジョブ実行間の差異。</td>
</tr>
<tr>
  <td>glue.driver.system.cpuSystemLoad</td>
  <td>ドライバーによって使用されるCPUシステム負荷の平均割合。（スケール：0-1）のパーセンテージ表示<br/>有効な統計はAverage。<br/>ドライバーのCPU負荷。</td>
</tr>
<tr>
  <td>glue.ALL.system.cpuSystemLoad</td>
  <td>すべてのエグゼキュータによって使用されるCPUシステム負荷の平均割合。（スケール：0-1）のパーセンテージ表示。<br/>有効な統計はAverage。<br/>エグゼキュータのCPU負荷。</td>
</tr>
</table>

## glueのメトリクスに出てくる用語の説明
### ステージ
* Sparkのアプリケーションは全体的なデータ処理をステージと呼ばれるいくつかのフェーズに分割して実行する。
* 1つのジョブは、複数のステージから構成される可能性がある。
* ステージの生成は、スクリプトの内容に応じて自動的に行われる。

### タスク
* Sparkのデータ処理においては、ステージがさらに複数のタスクに分割される。
* なので、1つのステージには1つ以上のタスクが存在することになる。
* １つ１つのタスクは、分散処理における最小の処理単位となり、例えば複数のファイルを処理する場合は、個々のファイルの処理が1つのタスクになる場合がある。

### executors
* Executorは、分散処理を実際に実行するプロセス。
* Sparkのジョブでは、上述の通り1つのジョブは複数のステージから構成され、個々のステージには複数のタスクが存在しますが、この最小の処理単位であるタスクを実際に実行するプロセスがExecutorとなる。
* Executorの数は、データを並列処理できるプロセスの数に該当し、Glueジョブの最大キャパシティやワーカー数を大きく設定するほど、Executorの数が増加する関係となる。