AWS Glueを使うときに気をつけること

使ってると色々ハマることがあったのでメモ。

ETLジョブ

DataFrameとDynamicFrame

GlueのジョブをコーディングするのにPython ShellやPython Sparkが選べますが、Python Shellの方は汎用処理に使って、Python Sparkは分散データ処理に使います。

で、Python Sparkの構文を調べ始めるとDataFrameとDynamicFrameというのが出てきてまあまあ混乱します。
DataFrameはSparkで用意されている分散処理用のデータセットでDynamicFrameはAWSの用意しているラッパーです。

マネジメントコンソールで自動生成されるジョブはDynamicFrameで作られていますが、ちょっと凝ったことをしようと思ったら、DataFrameに変換してコードを書いた方が色々と小回りが利いて便利です。

情報もSparkで使われてる分DataFrameの方がよく見つかります。

ワーカー数初期設定

初期値が10と妙に大きいので必要なければ最小の2にしておきましょう。

継続的なログ記録の有効化

ジョブの進捗状況が確認できるようになるので、是非有効化しましょう。


プログレスバーの表現が物凄くわかりにくいですが、

Stage Number (Stage Name): > (numCompletedTasks + numActiveTasks) / totalNumOfTasksInThisStage]
Stage Number (Stage Name): > (処理済みタスク + 処理中タスク) / タスク総数]
だと思っておけばいいっぽい。

パフォーマンスチューニング

RDSから大きなテーブルのデータを抽出するときは、フルスキャンにならないよう並列取り込みを利用するようにしましょう。
テーブルに数値型、または日付型のキーまたはインデックス項目があることが前提です。


こういうコードだと
df = spark \
    .read \
    .format("jdbc") \
    .option("url", JDBC_URL) \
    .option("user", "USER") \
    .option("password", "PASSWORD") \
    .option("dbtable", "MY_TABLE") \
    .option("driver", "oracle.jdbc.driver.OracleDriver") \
    .option("numPartitions", 10) \
    .option("partitionColumn", "id") \
    .option("lowerBound", 0) \
    .option("upperBound", 10000) \
    .load()
こういうクエリが並列で走ります。
SELECT * FROM MY_TABLE WHERE id < 1000;
SELECT * FROM MY_TABLE WHERE 1000 <= id AND id < 2000;
~~~
SELECT * FROM MY_TABLE WHERE 8000 <= id AND id < 9000;
SELECT * FROM MY_TABLE WHERE 9000 <= id;
ワーカーが増えればこの分かれたクエリを同時に実行できる数が増えます。

ネットワーク

接続とジョブのサブネット

接続定義を作るときにVPCとサブネットを割り当てますが、ジョブが作られる場所もその割り当てた接続によって決まります。
ジョブのコードでJDBCを使って直接アクセスする場合も、割り当てた接続定義によってアクセスできる場所にいるか決まるので注意。

サブネットにS3 VPCエンドポイトが必要

S3を参照しないジョブでもサブネットが属するVPCにS3 VPCエンドポイントが必要です。
ソースをダウンロードするから必要ってことなんでしょうね。

ジョブを実行するサブネットのIP範囲

ワーカー数1に対してプライベートIPを一つ消費するので、サブネットに割り当てられたIP範囲は十分な数である必要があります。

サブネットをまたぐ連携

RDSからRDSとかにデータを転送する場合、サブネットをまたいでいる場合は、たとえ2つの接続を割当てても1つのジョブでエクスポート、インポートできません。
一旦S3に出力するなどして、中継する必要があります。