

# ジョブのモニタリングとデバッグ
<a name="monitor-profile-glue-job-cloudwatch-metrics"></a>

AWS Glue ジョブに関するメトリクスを収集して AWS Glue および Amazon CloudWatch コンソールで可視化し、問題を特定して修正できます。AWS Glue ジョブのプロファイリングには、以下のステップが必要です。

1.  メトリクスを有効にする: 

   1.  ジョブ定義で [**Job metrics (ジョブメトリクス)**] オプションを有効にします。AWS Glue コンソールでプロファイリングを有効にするか、ジョブに対するパラメータとして有効にできます。詳細については、「[Spark ジョブのジョブプロパティの定義](add-job.md#create-job)」または「[AWS Glue ジョブでジョブパラメータを使用する](aws-glue-programming-etl-glue-arguments.md)」を参照してください。

   1.  ジョブ定義で **[AWS Glue オブザーバビリティメトリクス]** オプションを有効にします。AWS Glue コンソールでオブザーバビリティを有効にするか、ジョブに対するパラメータとして有効にできます。詳細については、「[AWS Glue オブザーバビリティメトリクスを使用したモニタリング](monitor-observability.md)」を参照してください。

1. ジョブスクリプトが `GlueContext` を初期化することを確認します。たとえば、次のスクリプトスニペットは `GlueContext` を初期化し、プロファイリングされたコードが配置されるスクリプト内の場所を示しています。この一般的な形式は、以下のデバッグシナリオで使用されます。

   ```
   import sys
   from awsglue.transforms import *
   from awsglue.utils import getResolvedOptions
   from pyspark.context import SparkContext
   from awsglue.context import GlueContext
   from awsglue.job import Job
   import time
   
   ## @params: [JOB_NAME]
   args = getResolvedOptions(sys.argv, ['JOB_NAME'])
   
   sc = SparkContext()
   glueContext = GlueContext(sc)
   spark = glueContext.spark_session
   job = Job(glueContext)
   job.init(args['JOB_NAME'], args)
   
   ...
   ...
   code-to-profile
   ...
   ...
   
   
   job.commit()
   ```

1. ジョブを実行します。

1. メトリクスを可視化する:

   1. AWS Glue コンソールでジョブメトリクスを可視化し、ドライバまたはエグゼキュターの異常なメトリクスを識別します。

   1. オブザーバビリティメトリクスは、「ジョブ実行モニタリング」ページ、「ジョブ実行詳細」ページ、または Amazon CloudWatch で確認できます。詳細については、「[AWS Glue オブザーバビリティメトリクスを使用したモニタリング](monitor-observability.md)」を参照してください。

1. 特定されたメトリクスを使用して根本原因を絞り込みます。

1. 必要に応じて、識別されたドライバーまたはジョブエグゼキュターのログストリームを使用して根本原因を確認します。

 **AWS Glue オブザーバビリティメトリクスのユースケース** 
+  [OOM 例外とジョブの異常のデバッグ](monitor-profile-debug-oom-abnormalities.md) 
+  [要求の厳しいステージとストラグラータスクのデバッグ](monitor-profile-debug-straggler.md) 
+  [複数のジョブの進行状況のモニタリング](monitor-debug-multiple.md) 
+  [DPU の容量計画のモニタリング](monitor-debug-capacity.md) 
+  [AWS Glue オブザーバビリティを使用してリソースの使用状況を監視し、コストを削減します。](https://aws.amazon.com/blogs/big-data/enhance-monitoring-and-debugging-for-aws-glue-jobs-using-new-job-observability-metrics)

# OOM 例外とジョブの異常のデバッグ
<a name="monitor-profile-debug-oom-abnormalities"></a>

AWS Glue でメモリ不足 (OOM) 例外とジョブの異常をデバッグできます。以下のセクションでは、Apache Spark ドライバーや Spark エグゼキュターのメモリ不足例外をデバッグするためのシナリオについて説明します。
+ [ドライバー OOM 例外のデバッグ](#monitor-profile-debug-oom-driver)
+ [エグゼキュター OOM 例外のデバッグ](#monitor-profile-debug-oom-executor)

## ドライバー OOM 例外のデバッグ
<a name="monitor-profile-debug-oom-driver"></a>

このシナリオでは、Spark ジョブで多数の小さいファイルが Amazon Simple Storage Service (Amazon S3) から読み込まれます。ファイルが Apache Parquet 形式に変換された後、Amazon S3 に書き込まれます。Spark ドライバーのメモリが不足しています。入力 Amazon S3 データの複数の Amazon S3 パーティションに 100 万を超えるファイルがあります。

プロファイルされたコードは次のとおりです。

```
data = spark.read.format("json").option("inferSchema", False).load("s3://input_path")
data.write.format("parquet").save(output_path)
```

### AWS Glue コンソールでプロファイルされたメトリクスを可視化する
<a name="monitor-debug-oom-visualize"></a>

以下のグラフでは、ドライバーとエグゼキュターのメモリ使用率がパーセントで示されています。この使用率は、直近の 1 分間に報告された値を平均した 1 つのデータポイントとしてプロットされます。ジョブのメモリプロファイルでは、[ドライバーメモリ](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.jvm.heap.usage)が安全しきい値である使用率の 50% をすぐに超えることがわかります。一方、すべてのエグゼキュターにおける[平均メモリ使用率](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.jvm.heap.usage)は、まだ 4% 未満です。これは、この Spark ジョブにおけるドライバー実行に異常があることを明確に示しています。

![\[ドライバーおよびエグゼキュターのメモリ使用率の割合 (パーセント)。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-memoryprofile.png)


ジョブの実行はすぐに失敗し、AWS Glue コンソールの [**History**] (履歴) タブにエラー「Command Failed with Exit Code 1」が表示されます。このエラー文字列は、システムエラーのためにジョブが失敗したことを意味します。この場合はドライバーのメモリ不足です。

![\[AWS Glue コンソールに表示されたエラーメッセージ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-errorstring.png)


コンソールの [**History**] (履歴) タブにある [**Error logs**] (エラーログ) リンクを選択し、CloudWatch Logs からのドライバー OOM に関する詳細情報を確認します。ジョブのエラーログで「**Error**」を検索し、それが本当にジョブの失敗の原因となった OOM 例外であることを確認します。

```
# java.lang.OutOfMemoryError: Java heap space
# -XX:OnOutOfMemoryError="kill -9 %p"
# Executing /bin/sh -c "kill -9 12039"...
```

ジョブの [**履歴**] タブで、[**ログ**] を選択します。ジョブの開始時における CloudWatch Logs 内のドライバー実行の以下のトレースが表示されます。Spark ドライバーは、すべてのディレクトリのすべてのファイルのリストの取得を試み、`InMemoryFileIndex` を構築してファイルごとに 1 つのタスクを起動します。この結果、Spark ドライバーは、すべてのタスクを追跡するため、大量の状態をメモリに保持する必要が生じます。また、メモリ内インデックスの多数のファイルの完全なリストを取得するため、ドライバー OOM となります。

### グループ化を使用した複数のファイルの処理を修正する
<a name="monitor-debug-oom-fix"></a>

* で*グループ化AWS Glue機能を使用して、複数のファイルの処理を修正できます。グループ化は、動的なフレームを使用しているときと、入力データセットに多数のファイル (50,000 超) があるときに自動的に有効になります。グループかにより、複数のファイルを 1 つのグループにまとめることができ、タスクは単一のファイルではなくグループ全体を処理できるようになります。その結果、Spark ドライバーがメモリに保存する状態がかなり少なくなり、追跡するタスクが減少します。データセットのグループ化を手動で有効にする方法の詳細については、「[大きなグループの入力ファイルの読み取り](grouping-input-files.md)」を参照してください。

AWS Glue ジョブのメモリプロファイルを確認するには、グループ化を有効にして次のコードをプロファイルします。

```
df = glueContext.create_dynamic_frame_from_options("s3", {'paths': ["s3://input_path"], "recurse":True, 'groupFiles': 'inPartition'}, format="json")
datasink = glueContext.write_dynamic_frame.from_options(frame = df, connection_type = "s3", connection_options = {"path": output_path}, format = "parquet", transformation_ctx = "datasink")
```

AWS Glue ジョブプロファイルでは、メモリプロファイルと ETL データ移動を監視できます。

ドライバーは、AWS Glue ジョブの持続期間全体にわたって、メモリ使用率のしきい値である 50% 未満で実行されます。エグゼキュターは、Amazon S3 からデータをストリーミングして処理し、Amazon S3 に書き出します。その結果、消費されるメモリは常に 5% 未満となります。

![\[この問題が発生していたメモリプロファイルが修正されました。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-memoryprofile-fixed.png)


以下のデータ移動プロファイルは、ジョブが進行するにつれてすべてのエグゼキュターにより直近 1 分間に[読み取り](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.read_bytes)および[書き込み](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.write_bytes)された Amazon S3 バイトの合計数を示しています。データはすべてのエグゼキュターでストリーミングされるため、どちらも同様のパターンに従っています。ジョブは、100 万ファイルすべての処理を 3 時間未満で完了します。

![\[この問題が発生していたデータ移動プロファイルが修正されました。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-etlmovement.png)


## エグゼキュター OOM 例外のデバッグ
<a name="monitor-profile-debug-oom-executor"></a>

このシナリオでは、Apache Spark エグゼキュターで発生する可能性がある OOM 例外をデバッグする方法について説明します。次のコードでは、Spark MySQL リーダーを使用して、約 34 万行の大きなテーブルを Spark データフレームに読み取ります。次に、Parquet 形式で Amazon S3 に書き出します。接続プロパティを用意し、デフォルト Spark 設定を使用してテーブルを読み取ることができます。

```
val connectionProperties = new Properties()
connectionProperties.put("user", user)
connectionProperties.put("password", password)
connectionProperties.put("Driver", "com.mysql.jdbc.Driver")
val sparkSession = glueContext.sparkSession
val dfSpark = sparkSession.read.jdbc(url, tableName, connectionProperties)
dfSpark.write.format("parquet").save(output_path)
```

### AWS Glue コンソールでプロファイルされたメトリクスを可視化する
<a name="monitor-debug-oom-visualize-2"></a>

メモリ使用量グラフの傾きが正で 50% を超えた場合、および次のメトリクスが出力される前にジョブが失敗した場合は、メモリ枯渇が原因の候補となります。以下のグラフは、実行してから 1 分以内に、すべてのエグゼキュターにおける[平均メモリ使用率](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.jvm.heap.usage)がすぐに 50% を超えることを示しています。使用率は最大 92% に上昇し、エグゼキュターを実行するコンテナは Apache Hadoop YARN により停止されます。

![\[すべてのエグゼキュターにおける平均メモリ使用率。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-2-memoryprofile.png)


次のグラフが示すよう、ジョブが失敗するまで常に[単一のエグゼキュター](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.ExecutorAllocationManager.executors.numberAllExecutors)が実行されています。これは、新しいエグゼキュターが起動され、停止されたエグゼキュターが置き換えられるためです。JDBC データソースの読み取りは、列上のテーブルをパーティション化して複数の接続を開く必要があるため、デフォルトでは並列化されません。その結果、1 つのエグゼキュターだけがテーブル全体を順番に読み込みます。

![\[ジョブの実行は、ジョブが失敗するまで単一のエグゼキュターが実行されることを示しています。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-2-execution.png)


次のグラフが示すように、Spark はジョブが失敗するまでに 4 回新しいタスクを起動しようとします。3 つのエグゼキュターの[メモリプロファイル](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.jvm.heap.used)を確認できます。各エグゼキュターはすべてのメモリをすぐに使い尽くします。4 番目のエグゼキュターのメモリが不足し、ジョブは失敗します。その結果、そのメトリクスはすぐに報告されません。

![\[エグゼキュターのメモリプロファイル。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-2-exec-memprofile.png)


次の図に示すように、AWS Glue コンソールのエラー文字列からは、ジョブが OOM 例外のために失敗したことを確認できます。

![\[AWS Glue コンソールに表示されたエラーメッセージ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-2-errorstring.png)


**ジョブ出力ログ:** エグゼキュター OOM 例外の結果をさらに確認するには、CloudWatch Logs を参照します。「**Error**」を検索すると、メトリクスダッシュボードに示されているように、4 つのエグゼキュターがほぼ同じ時間枠で停止されています。メモリ制限を超えると、すべて YARN により終了されます。

エグゼキュター 1

```
18/06/13 16:54:29 WARN YarnAllocator: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:54:29 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:54:29 ERROR YarnClusterScheduler: Lost executor 1 on ip-10-1-2-175.ec2.internal: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:54:29 WARN TaskSetManager: Lost task 0.0 in stage 0.0 (TID 0, ip-10-1-2-175.ec2.internal, executor 1): ExecutorLostFailure (executor 1 exited caused by one of the running tasks) Reason: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
```

エグゼキュター 2

```
18/06/13 16:55:35 WARN YarnAllocator: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:55:35 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:55:35 ERROR YarnClusterScheduler: Lost executor 2 on ip-10-1-2-16.ec2.internal: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:55:35 WARN TaskSetManager: Lost task 0.1 in stage 0.0 (TID 1, ip-10-1-2-16.ec2.internal, executor 2): ExecutorLostFailure (executor 2 exited caused by one of the running tasks) Reason: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
```

エグゼキュター 3

```
18/06/13 16:56:37 WARN YarnAllocator: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:56:37 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:56:37 ERROR YarnClusterScheduler: Lost executor 3 on ip-10-1-2-189.ec2.internal: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:56:37 WARN TaskSetManager: Lost task 0.2 in stage 0.0 (TID 2, ip-10-1-2-189.ec2.internal, executor 3): ExecutorLostFailure (executor 3 exited caused by one of the running tasks) Reason: Container killed by YARN for exceeding memory limits. 5.8 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
```

エグゼキュター 4

```
18/06/13 16:57:18 WARN YarnAllocator: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:57:18 WARN YarnSchedulerBackend$YarnSchedulerEndpoint: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:57:18 ERROR YarnClusterScheduler: Lost executor 4 on ip-10-1-2-96.ec2.internal: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
18/06/13 16:57:18 WARN TaskSetManager: Lost task 0.3 in stage 0.0 (TID 3, ip-10-1-2-96.ec2.internal, executor 4): ExecutorLostFailure (executor 4 exited caused by one of the running tasks) Reason: Container killed by YARN for exceeding memory limits. 5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.
```

### AWS Glue 動的フレームを使用してフェッチサイズ設定を修正する
<a name="monitor-debug-oom-fix-2"></a>

Spark JDBC フェッチサイズのデフォルトの設定が 0 であるため、エグゼキュターは JDBC テーブルの読み取り中にメモリ不足になります。つまり、Spark は行を一度に 1 つずつストリーミングしますが、Spark エグゼキュター上の JDBC ドライバーがデータベースから 3400 万行をまとめてフェッチして、それらをキャッシュしようとします。Spark を使用すると、フェッチサイズパラメータを 0 以外のデフォルト値に設定することにより、このシナリオを回避できます。

代わりに AWS Glue 動的フレームを使用することで、この問題を解決することもできます。デフォルトでは、動的フレームは 1,000 行のフェッチサイズを使用します。これは通常十分な値です。このため、エグゼキュターが合計メモリの 7% 超を使用することはありません。AWS Glue ジョブは、エグゼキュターを 1 つだけ使用して 2 分未満で完了します。AWS Glue 動的フレームを使用することが推奨されるアプローチですが、Apache Spark の `fetchsize` プロパティを使用してフェッチサイズを設定することもできます。「[Spark SQL、DataFrames および Datasets ガイド](https://spark.apache.org/docs/2.2.0/sql-programming-guide.html#jdbc-to-other-databases)」を参照してください。

```
val (url, database, tableName) = {
 ("jdbc_url", "db_name", "table_name")
 } 
val source = glueContext.getSource(format, sourceJson)
val df = source.getDynamicFrame
glueContext.write_dynamic_frame.from_options(frame = df, connection_type = "s3", connection_options = {"path": output_path}, format = "parquet", transformation_ctx = "datasink")
```

**プロファイルされた通常のメトリクス:** 以下の図に示すように、AWS Glue 動的フレームを持つ[エグゼキュターメモリ](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.jvm.heap.usage)が安全しきい値を超えることはありません。データベースから行をストリーミングし、任意の時点で 1,000 行のみ JDBC ドライバーにキャッシュします。メモリ不足例外は発生しません。

![\[安全しきい値を下回ったエグゼキュターメモリを示している AWS Glue コンソール。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-oom-2-memoryprofile-fixed.png)


# 要求の厳しいステージとストラグラータスクのデバッグ
<a name="monitor-profile-debug-straggler"></a>

AWS Glue ジョブプロファイリングを使用して、抽出、変換、ロード (ETL) ジョブで要求の厳しいステージとストラグラータスクを特定できます。ストラグラータスクは、AWS Glue ジョブのステージに含まれる他のタスクよりかなり時間がかかります。その結果、ステージの完了まで時間がかかり、ジョブの合計実行時間も遅延します。

## 小さい入力ファイルを大きい出力ファイルにまとめる
<a name="monitor-profile-debug-straggler-scenario-1"></a>

ストラグラータスクは、さまざまなタスク間の処理が均等に分散していないときや、データスキューによって特定のタスクが処理するデータが多くなった場合に発生する可能性があります。

Apache Spark の一般的なパターンである次のコードをプロファイルし、多数の小さいファイルをいくつかの大きい出力ファイルにまとめることができます。この例では、入力データセットは 32 GB の JSON Gzip 圧縮ファイルです。出力データセットには、約 190 GB の圧縮されていない JSON ファイルが含まれています。

プロファイルされたコードは次のとおりです。

```
datasource0 = spark.read.format("json").load("s3://input_path")
df = datasource0.coalesce(1)
df.write.format("json").save(output_path)
```

### AWS Glue コンソールでプロファイルされたメトリクスを可視化する
<a name="monitor-debug-straggler-visualize"></a>

ジョブをプロファイルすると、4 つのメトリクスセットを調べることができます。
+ ETL データ移動
+ エグゼキュター間のデータシャッフル
+ ジョブの実行
+ メモリプロファイル

**ETL データ移動**: [**ETL Data Movement (ETL データ移動)**] プロファイルでは、バイトは、最初の 6 分間に完了する最初のステージのすべてのエグゼキュターにより比較的早く[読み取られます](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.read_bytes)。ただし、ジョブの合計実行時間が約 1 時間の場合、ほとんどがデータの[書き込み](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.write_bytes)で構成されます。

![\[ETL データ移動プロファイルを示すグラフ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-1.png)


**エグゼキュター間のデータシャッフル:** [ジョブ実行](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.aggregate.shuffleLocalBytesRead)メトリクスと[データシャッフル](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.aggregate.shuffleBytesWritten)メトリクスが示しているように、シャッフル中の**読み取り**および**書き込み**のバイト数も、ステージ 2 が終了する前に急増しています。すべてのエグゼキュターからのデータシャッフルの後、読み取りと書き込みはエグゼキュター 3 番のみから続行されます。

![\[エグゼキュター間でのデータシャッフルのメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-2.png)


**ジョブの実行:** 以下のグラフに示すように、他のすべてのエグゼキュターはアイドル状態であり、最終的に時間 10:09 までに破棄されます。この時点で、エグゼキュターの合計数はわずか 1 に減ります。これは、エグゼキュター 3 番が、実行時間が最も長いストラグラータスクで構成されているため、ジョブの実行時間の大部分を占めていることを明確に示しています。

![\[アクティブなエグゼキュターの実行メトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-3.png)


**メモリプロファイル:** 最初の 2 つのステージの後、[エグゼキュター 3 番](monitoring-awsglue-with-cloudwatch-metrics.md#glue.executorId.jvm.heap.used)のみがメモリをアクティブに消費してデータを処理します。残りのエグゼキュターはアイドル状態のままであるか、最初の 2 つのステージの完了後すぐに破棄されます。

![\[最初の 2 つのステージの後のメモリプロファイルのメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-4.png)


### グループ化を使用してストラグラーエグゼキュターを修正する
<a name="monitor-debug-straggler-fix"></a>

ストラグラーエグゼキュターは、* で*グループ化AWS Glueを使用して回避できます。グループ化を使用してデータをすべてのエグゼキュター間に均等に分散し、クラスター内で利用可能なすべてのエグゼキュターを使用して大きいファイルにまとめます。詳細については、 を参照してください。[大きなグループの入力ファイルの読み取り](grouping-input-files.md)

AWS Glue ジョブで ETL データ移動を確認するには、グループ化を有効にして次のコードをプロファイルします。

```
df = glueContext.create_dynamic_frame_from_options("s3", {'paths': ["s3://input_path"], "recurse":True, 'groupFiles': 'inPartition'}, format="json")
datasink = glueContext.write_dynamic_frame.from_options(frame = df, connection_type = "s3", connection_options = {"path": output_path}, format = "json", transformation_ctx = "datasink4")
```

**ETL データ移動:** データの書き込みは、ジョブの実行時間全体にわたって、データの読み取りと並行してストリーミングされるようになりました。その結果、ジョブは 8 分以内に終了します。以前よりもはるかに早いスピードです。

![\[この問題が発生していた ETL データ移動が修正されました。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-5.png)


**エグゼキュター間でのデータシャッフル:** グループ化機能を使用して読み取り中に入力ファイルがまとめられると、データの読み取り後、コストのかかるデータシャッフルが行われなくなります。

![\[この問題が発生していたデータシャッフルメトリクスが修正されました。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-6.png)


**ジョブの実行:** ジョブ実行メトリクスは、実行されてデータを処理しているアクティブなエグゼキュターの合計数がかなり安定していることを示しています。ジョブに単一のストラグラーはありません。すべてのエグゼキュターがアクティブで、ジョブが完了するまでは破棄されません。エグゼキュター間でデータの中間シャッフルは行われないため、ジョブにはステージが 1 つしかありません。

![\[ジョブにストラグラーがないことを示しているジョブの実行ウィジェットのメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-7.png)


**メモリプロファイル:** このメトリクスは、すべてのエグゼキュター間の[アクティブなメモリ消費](monitoring-awsglue-with-cloudwatch-metrics.md#glue.executorId.jvm.heap.used)を示しています。これは、すべてのエグゼキュター間にアクティビティがあることを再確認するものです。データは並行してストリーミングおよび書き出されるため、すべてのエグゼキュターの合計メモリ使用量はほぼ均等で、すべてのエグゼキュター安全しきい値をかなり下回っています。

![\[すべてのエグゼキュター間でアクティブなメモリ消費量を示すメモリプロファイルメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-straggler-8.png)


# 複数のジョブの進行状況のモニタリング
<a name="monitor-debug-multiple"></a>

複数の AWS Glue ジョブをまとめてプロファイルし、それらの間のデータフローを監視できます。これは、一般的なワークフローパターンであり、個々のジョブの進行状況、データ処理バックログ、データの再処理、ジョブのブックマークのモニタリングが必要です。

**Topics**
+ [プロファイルされたコード](#monitor-debug-multiple-profile)
+ [AWS Glue コンソールでプロファイルされたメトリクスを可視化する](#monitor-debug-multiple-visualize)
+ [ファイルの処理を修正する](#monitor-debug-multiple-fix)

## プロファイルされたコード
<a name="monitor-debug-multiple-profile"></a>

このワークフローには、入力ジョブと出力ジョブの 2 つがあります。入力ジョブは、定期的なトリガーを使用して 30 分ごとに実行するようにスケジュールされます。出力ジョブは、入力ジョブの実行が成功するたびに実行されるようにスケジュールされます。このようなスケジュールされたジョブは、ジョブトリガーを使用して制御されます。

![\[入力および出力ジョブのスケジュールを制御するジョブトリガーが表示されたコンソールスクリーンショット。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-1.png)


**入力ジョブ**: このジョブは Amazon Simple Storage Service (Amazon S3) の場所からデータを読み取り、`ApplyMapping` を使用して変換し、Amazon S3 のステージング場所に書き込みます。次のコードは、入力ジョブのプロファイルされたコードです。

```
datasource0 = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options = {"paths": ["s3://input_path"], "useS3ListImplementation":True,"recurse":True}, format="json")
applymapping1 = ApplyMapping.apply(frame = datasource0, mappings = [map_spec])
datasink2 = glueContext.write_dynamic_frame.from_options(frame = applymapping1, connection_type = "s3", connection_options = {"path": staging_path, "compression": "gzip"}, format = "json")
```

**出力ジョブ**: このジョブは、Amazon S3 内のステージング場所から入力ジョブの出力を読み取り、もう一度変換して、ターゲットの場所に書き込みます。

```
datasource0 = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options = {"paths": [staging_path], "useS3ListImplementation":True,"recurse":True}, format="json")
applymapping1 = ApplyMapping.apply(frame = datasource0, mappings = [map_spec])
datasink2 = glueContext.write_dynamic_frame.from_options(frame = applymapping1, connection_type = "s3", connection_options = {"path": output_path}, format = "json")
```

## AWS Glue コンソールでプロファイルされたメトリクスを可視化する
<a name="monitor-debug-multiple-visualize"></a>

次のダッシュボードでは、入力ジョブから取得された Amazon S3 バイトの書き込みメトリクスを、出力ジョブの同じタイムラインにある Amazon S3 バイトの読み取りメトリクスに重ね合わせています。タイムラインには、入力および出力ジョブのさまざまなジョブの実行が表示されます。入力ジョブ (赤で表示) は、30 分ごとに開始されます。出力ジョブ (茶色で表示) は、入力ジョブの完了時に開始し、最大同時実行数は 1 です。

![\[データの読み書きを示しているグラフ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-4.png)


この例では、[ジョブのブックマーク](https://docs.aws.amazon.com/glue/latest/dg/monitor-continuations.html)が有効になっていません。スクリプトコードでジョブのブックマークを有効にするために使用される変換コンテキストはありません。

**ジョブ履歴**: [**履歴**] タブに示されているように、入力および出力ジョブには、午後 12 時に始まる複数の実行があります。

AWS Glue コンソールで入力ジョブは次のようになります。

![\[入力ジョブの [履歴] タブが表示されているコンソールスクリーンショット。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-2.png)


以下のイメージに、出力ジョブを示します。

![\[出力ジョブの [履歴] タブが表示されているコンソールスクリーンショット。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-3.png)


**最初のジョブの実行**: 以下のデータバイトの読み書きグラフに示されているように、12:00 から 12:30 の間に行われた入力および出力ジョブの最初のジョブの実行は、曲線の下のほぼ同じ領域を示しています。これらの領域は、入力ジョブによって書き込まれた Amazon S3 バイトと、出力ジョブによって読み取られた Amazon S3 バイトを表しています。このデータは、書き込まれた Amazon S3 バイトの比率によっても確認されます (30 分間の累計 – 入力ジョブのジョブトリガー頻度)。午後 12 時に開始された入力ジョブの実行の比率のデータポイントも 1 です。

以下のグラフは、すべてのジョブの実行におけるデータフロー比率を示しています。

![\[データフロー比率を示しているグラフ: 書き込みバイトと読み取りバイト。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-5.png)


**2 回目のジョブの実行**: 2 回目のジョブの実行では、入力ジョブによって書き込まれるバイト数と比較して、出力ジョブにより読み取られるバイト数に明確な違いがあります。(出力ジョブの 2 つのジョブの実行にまたがる曲線の下の領域を比較するか、入力および出力ジョブの 2 回目の実行の領域を比較します)。読み取りバイトと書き込みバイトの比率は、12:30 から 13:00 までの 2 番目の 30 分スパンにおいて入力ジョブによって書き込まれるデータの約 2.5 倍のデータを出力ジョブが読み取ることを示しています。ジョブのブックマークが有効になっていなかったため、出力ジョブが入力上部による最初のジョブの実行の出力を再処理したことがその理由です。1 より大きい比率は、出力ジョブにより処理された追加のデータバックログがあることを示しています。

**3 回目のジョブの実行**: 入力ジョブは、書き込まれるバイト数の観点でほぼ一定です (赤色の曲線の下にある領域を参照)。ただし、入力ジョブの 3 回目の実行には、予想されるよりも時間がかかります (赤色の曲線が長く続いていることを確認)。その結果、出力ジョブの 3 回目のジョブの実行が遅れて開始します。3 回目のジョブの実行では、13:00 から 13:30 までの残りの 30 分においてステージング場所に蓄積されたデータのごく一部のみ処理されました。バイトフローの比率は、入力ジョブの 3 回目の実行により書き込まれたデータの 0.83 のみ処理されたことを示しています (13:00 の比率を参照)。

**入力ジョブと出力ジョブの重複**: 入力ジョブの 4 回目のジョブの実行は、スケジュールに従って 13:30 に開始されました。これは、出力ジョブの 3 回目のジョブの実行が終了する前です。これらの 2 つのジョブの実行は一部の重複しています。ただし、出力ジョブの 3 回目の実行では、13:17 前後に開始されたとき、Amazon S3 のステージング場所にリストされたファイルのみキャプチャされます。これは、入力ジョブの最初のジョブの実行から取得されたすべてのデータ出力で構成されています。13:30 時点での実際の比率は約 2.75 です。出力ジョブの 3 回目のジョブの実行では、13:30 から 14:00 に入力ジョブの 4 回目のジョブの実行により書き込まれたデータの約 2.75 倍のデータが処理されました。

これらのイメージが示すように、出力ジョブは、入力ジョブのそれ以前のすべてのジョブの実行から取得されたステージング場所からのデータを再処理します。その結果、出力ジョブの 4 番目のジョブの実行が最も長くなり、入力ジョブの 5 回目のジョブの実行全体と重複します。

## ファイルの処理を修正する
<a name="monitor-debug-multiple-fix"></a>

出力ジョブが、出力ジョブの以前のジョブの実行で処理されていないファイルのみ処理するようにする必要があります。これを行うには、次のように、ジョブのブックマークを有効にし、ジョブ出力に変換コンテキストを設定します。

```
datasource0 = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options = {"paths": [staging_path], "useS3ListImplementation":True,"recurse":True}, format="json", transformation_ctx = "bookmark_ctx")
```

ジョブのブックマークが有効な場合、出力ジョブは、入力ジョブのそれまでのすべてのジョブの実行から取得されたステージング場所のデータを再処理しません。読み書きされたデータを示す以下のイメージでは、茶色の曲線の下にある領域はほぼ一定であり、赤色の曲線と似ています。

![\[読み書きされたデータを赤色の線と茶色の線で示しているグラフ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-6.png)


追加のデータが処理されないため、バイトフローの比率もほぼ 1 に維持されます。

![\[データフロー比率を示しているグラフ: 書き込みバイトと読み取りバイト。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-7.png)


出力ジョブのジョブの実行が開始され、次の入力ジョブの実行がデータをさらにステージング場所に置き始める前にステージング場所のファイルをキャプチャします。これを続けている限り、それまでの入力ジョブの実行からキャプチャされたファイルのみ処理され、比率はほぼ 1 に維持されます。

![\[データフロー比率を示しているグラフ: 書き込みバイトと読み取りバイト。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-multiple-7.png)


入力ジョブに予想以上に時間がかかるため、2 回の入力ジョブの実行から取得されたステージング場所のファイルが出力ジョブによってキャプチャされるとします。その出力ジョブの実行では、比率が 1 よりも大きくなります。ただし、出力ジョブのそれ以降のジョブの実行では、出力ジョブのそれまでのジョブの実行により既に処理されているファイルは処理されません。

# DPU の容量計画のモニタリング
<a name="monitor-debug-capacity"></a>

AWS Glue でジョブメトリクスを使用すると、AWS Glue ジョブをスケールアウトするために使用できるデータ処理単位 (DPU) の数を予測できます。

**注記**  
このページは AWS Glue バージョン 0.9 および 1.0 にのみ適用できます。AWS Glue 以降のバージョンには、容量計画時に追加の考慮事項を導入するコスト削減機能が含まれています。

**Topics**
+ [プロファイルされたコード](#monitor-debug-capacity-profile)
+ [AWS Glue コンソールでプロファイルされたメトリクスを可視化する](#monitor-debug-capacity-visualize)
+ [最適な DPU 容量を決定する](#monitor-debug-capacity-fix)

## プロファイルされたコード
<a name="monitor-debug-capacity-profile"></a>

次のスクリプトは、428 個の gzip で圧縮された JSON ファイルを含む Amazon Simple Storage Service (Amazon S3) パーティションを読み取ります。このスクリプトは、マッピングを適用してフィールド名を変更し、Apache Parquet 形式に変換して Amazon S3 に書き込みます。デフォルトに従って 10 個の DPU をプロビジョニングし、このジョブを実行します。

```
datasource0 = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options = {"paths": [input_path], "useS3ListImplementation":True,"recurse":True}, format="json")
applymapping1 = ApplyMapping.apply(frame = datasource0, mappings = [(map_spec])
datasink2 = glueContext.write_dynamic_frame.from_options(frame = applymapping1, connection_type = "s3", connection_options = {"path": output_path}, format = "parquet")
```

## AWS Glue コンソールでプロファイルされたメトリクスを可視化する
<a name="monitor-debug-capacity-visualize"></a>

**ジョブの実行 1:** このジョブの実行では、プロビジョニングが不足している DPU がクラスターにあるかどうかを調べる方法を示します。AWS Glue でのジョブ実行機能は、[アクティブに実行されているエグゼキュターの合計数](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.ExecutorAllocationManager.executors.numberAllExecutors)、[完了したステージの数](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.aggregate.numCompletedStages)、[必要なエグゼキュターの最大数](monitoring-awsglue-with-cloudwatch-metrics.md#glue.driver.ExecutorAllocationManager.executors.numberMaxNeededExecutors)を表示します。

必要なエグゼキュターの最大数は、実行中のタスクと保留中のタスクの合計数を加算し、エグゼキュターごとのタスクで除算することによって算出されます。この結果は、現在の負荷に対応するために必要なエグゼキュターの合計数の尺度となります。

一方、アクティブに実行されているエグゼキュターの数は、アクティブな Apache Spark タスクで実行されているエグゼキュターの数を測定します。ジョブが進行するにつれて、必要なエグゼキュターの最大数は変化し、保留中のタスクキューが減るため通常はジョブの終わりに向かって減少します。

次のグラフの横方向の赤色の線は、割り当てられたエグゼキュターの最大数を示しています。これは、ジョブに割り当てる DPU の数によって異なります。この場合、ジョブの実行に対して 10 個の DPU を割り当てます。1 つの DPU が管理用に予約されています。9 個の DPU はそれぞれ 2 つのエグゼキュターを実行し、1 つのエグゼキュターは Spark ドライバー用に予約されています。Spark ドライバーはプライマリアプリケーション内で実行されます。そのため、割り当てられるエグゼキュターの最大数は、2\$19 - 1 = 17 です。

![\[アクティブなエグゼキュターと必要なエグゼキュターの最大数を示すジョブメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-capacity-1.png)


グラフが示すとおり、必要なエグゼキュターの最大数はジョブの開始時に 107 から始まりますが、アクティブなエグゼキュターの数は 17 のままです。これは、10 個の DPU を持つ割り当てられるエグゼキュターの最大数と同じです。必要なエグゼキュターの最大数と割り当てられるエグゼキュターの最大数の比率 (Spark ドライバーでは両方に 1 を加算) から、プロビジョニングが不足している係数が 108/18 = 6 倍であるとわかります。6 (プロビジョニング率未満) \$19 (現在の DPU 容量 -1) \$11 DPU = 55 個の DPU をプロビジョニングしてジョブをスケールアウトし、最大限の並列処理で実行することで処理時間を短縮することができます。

AWS Glue コンソールは、詳細なジョブメトリクスを、元の割り当てられるエグゼキュターの最大数を表す静的な線として表示します。コンソールは、メトリクスのジョブ定義からから割り当てられるエグゼキュターの最大数を計算します。対照的に、詳細なジョブ実行メトリクスについては、コンソールはジョブ実行設定から割り当てられるエグゼキュターの最大数、特にジョブ実行に割り当てられた DPU を計算します。個々のジョブ実行のメトリクスを表示するには、ジョブ実行を選択して、[**実行メトリクスの表示**] を選択します。

![\[ETL データ移動を示すジョブメトリクス。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-capacity-2.png)


[読み取り](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.read_bytes)および[書き込み](monitoring-awsglue-with-cloudwatch-metrics.md#glue.ALL.s3.filesystem.write_bytes)された Amazon S3 バイトを見ると、ジョブが Amazon S3 からのデータをストリーミングして並列に書き出すことに 6 分すべてを消費していることがわかります。割り当てられた DPU 上のすべてのコアは、Amazon S3 に対して読み取りと書き込みを行います。必要なエグゼキュターの最大数 107 は、入力 Amazon S3 パス内のファイル数 428 とも整合しています。各エグゼキュターは、4 つの Spark タスクを起動し、4 つの入力ファイルを処理します (gzip で圧縮された JSON)。

## 最適な DPU 容量を決定する
<a name="monitor-debug-capacity-fix"></a>

前のジョブの実行の結果に基づいて、割り当てられる DPU の合計数を 55 に増やし、ジョブがどのように実行されるかを確認できます。ジョブは 3 分未満で完了します。以前に必要な時間の半分です。この場合、実行時間の短いジョブであるため、ジョブのスケールアウトは直線的ではありません。存続期間の長いタスクまたは多数のタスクを持つジョブ (必要なエグゼキュターの最大数が大きい) は、直線的に近い DPU スケールアウトのパフォーマンス向上から恩恵を受けます。

![\[割り当てられる DPU の合計数の増加を示しているグラフ\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-capacity-3.png)


上の図に示すように、アクティブなエグゼキュターの合計数が最大割り当て数 107 個のエグゼキュターに到達します。同様に、必要なエグゼキュターの最大数が割り当てられるエグゼキュターの最大数を上回ることはありません。必要なエグゼキュターの最大数は、アクティブに実行されているタスクと保留中のタスクの数から計算されるため、アクティブなエグゼキュターの数よりも小さい可能性があります。これは、短期間部分的または完全にアイドル状態になり、まだ停止されていないエグゼキュターが存在する可能性があるためです。

![\[最大割り当て数に到達しているアクティブなエグゼキュターの合計数を示しているグラフ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-capacity-4.png)


このジョブの実行では、Amazon S3 から並列で読み取りと書き込みをするために 6 倍のエグゼキュターが使用されます。その結果、このジョブの実行では、読み取りと書き込みの両方に使用される Amazon S3 帯域幅が多くなり、早く完了します。

### 過度にプロビジョニングされた DPU を識別する
<a name="monitor-debug-capacity-over"></a>

次に、100 個の DPU (99 \$1 2 = 198 個のエグゼキュター) でジョブをスケールアウトするとこれ以上スケールアウトしなくてよくなるかどうかを調べることができます。次のグラフが示すように、ジョブが完了するまでまだ 3 分かかります。同様に、ジョブは 107 個を超えてスケールアウトしないため (55 DPU 構成)、残りの 91 個のエグゼキュターは過剰にプロビジョニングされてまったく使用されません。必要なエグゼキュターの最大数からわかるように、これは DPU の数を増やしても必ずパフォーマンスが向上するわけではないことを示しています。

![\[DPU の数を増やしてもジョブのパフォーマンスが必ずしも向上するわけではないことを示しているグラフ。\]](http://docs.aws.amazon.com/ja_jp/glue/latest/dg/images/monitor-debug-capacity-5.png)


### 時間の差を比較する
<a name="monitor-debug-capacity-time"></a>

次の表に示す 3 つのジョブが実行は、10 個の DPU、55 個の DPU、100 個の DPU のジョブの実行時間をまとめています。最初のジョブの実行をモニタリングすることで確立した推定値を使用して、ジョブの実行時間を向上させることができる DPU 容量を調べることができます。


| ジョブ ID | DPU の数 | 実行時間 | 
| --- | --- | --- | 
| jr\$1c894524c8ef5048a4d9..。 | 10 | 6 分。 | 
| jr\$11a466cf2575e7ffe6856..。 | 55 | 3 分。 | 
| jr\$134fa1ed4c6aa9ff0a814..。 | 100 | 3 分。 | 