ローカルJupyterLab環境でGlueジョブを開発・実行する
はじめに
記事概要
- Glue ジョブをローカルの Jupyter Lab で開発・テスト実行できる環境を整備します。
- ローカル開発環境 (Docker コンテナ) から、S3上のサンプルデータを Spark DataFrame として読み込みます。
a. 想定読者
- Glue ジョブをもっと便利に開発したい方
- Docker について一定の知識・スキルがある方
b. 前提条件
- Docker がインストールされており、起動していること
- AWS CLI がインストールされており、設定が完了していること
c. 動作確認環境
筆者が動作確認を行った環境は以下の通りです。
# OS
% sw_vers
ProductName: macOS
ProductVersion: 11.6
BuildVersion: 20G165
# Docker
% docker --version
Docker version 20.10.8, build 3967b7d
# Python
% python --version
Python 3.7.6
d. リソース
本記事で使用するリソースは、GitHub に公開しています。
目次
- 経緯
- 実施手順
- 機能概要
- 未採択要件
- 落穂拾い
1. 経緯
【既に Glue バリバリ使ってるよ!という方は読み飛ばしてください】
Amazon Web Service が提供するマネージドなETLサービスとして AWS Glue があります。Glue は「データカタログ」や「クローラ」など様々な機能を備えていますが、「Glueジョブ」もまた Glue の中核をなすサービスの一つとなっています。開発者は実行基盤を意識することなく、サーバーレスに ETL ワークロードを実装・実行することができます。
Glue ジョブを開発する手段としては、以下が一般的だと思います。
(1)Glue ジョブのスクリプトエディタで、直接スクリプトを編集する。
(2)Glue Notebook インスタンスのノートブック上で、処理ロジックを記述する。
ぶっつけ本番で開発に着手できる人は(1)を選ぶでしょうし、一旦ノートブックでロジックの妥当性を確認したい人は(2)を選ぶでしょう。
しかし、いずれにしても AWS マネジメントコンソールにログインする必要があり、(特に MFA をきちんと設定している場合などには)開発着手までが若干手間です。また、開発時にインターネット接続が必須になってしまうのもあまり好ましくありません。ローカル環境で Glue ジョブをテスト実行する術はないでしょうか。
結論から言ってしまうと、一応あります。AWS が公式に Glue ジョブ実行ランタイム用の Docker イメージを公開しており、これを Pull & Run することで、awsglue
ライブラリが事前インストールされた開発用の Glue ジョブ実行環境が手に入ります。詳細な手順は、以下の公式ブログに記載されています。
ブログの手順通りに進めれば、とりあえず Jupyter Notebook 上で Glue ジョブを開発できるようになります。ただ、本記事執筆時点(2021.9.23時点)で公開されているイメージには Jupyter Lab が事前インストールされておらず、また、開発対象の ETL ワークロード毎に必要なライブラリも色々と入っていない可能性があります。Jupyter Notebook を起動し、コマンドプロンプトでインストールでも良いでしょうが、今後も使い回すことを考えれば、必要なライブラリを一式揃えた Docker イメージを新たに Build する運用を整えた方が楽でしょう。
ということで前置きが長くなりましたが、以降で AWS 提供のコンテナイメージをベースに Jupyter Lab 等のライブラリをインストールした新たなイメージをビルドしていきます。また、Amazon S3 に格納されたサンプルデータを読み込み、Spark DataFrame として表示できるかどうかの確認までを行います。
2. 実施手順
2-1. 事前準備
適当なディレクトリ(以下、$WORK_DIR
)配下で Git リポジトリをクローンし、移動します。
% cd $WORKDIR
% git clone git@github.com:roki18d/glue-job-local-execution.git
% cd glue-job-local-execution
必要に応じて、pyenv や conda 等の Python 仮想環境にスイッチします。本記事では glueenv
という pyenv 仮想環境を使用します。
% pyenv local glueenv
% python --version
Python 3.7.6
必要な Python ライブラリを pip インストールします。
% pip install -r requirements.txt
2-2. 設定
config_sample.json
をもとに config.json
をコピー作成します。環境に合わせて設定値を編集します。(ビルドした新規イメージを Docker Hub に Push する必要がない場合は、DOCKER_HUB_SECRET
の編集は不要です)
% cp config_sample.json config.json
% vi config.json
{
"GENERAL": {
"BASE_IMAGE_NAME": "amazon/aws-glue-libs:glue_libs_1.0.0_image_01",
"DOCKERFILE_LOCATION": ".",
"MY_IMAGE_NAME": "my-aws-glue-libs"
},
"LOGGING": {
"CONFIG_FILE_LOCATION": "./logging.conf",
"SEPARATOR_CHAR": "-",
"SEPARATOR_NUM_REPEAT": 60
},
"DOCKER_HUB_SECRET": {
"USERNAME": "<your username>",
"PASSWORD": "<your password>",
"EMAIL": "<your email>",
"REGISTRY": "https://index.docker.io/v1/"
},
"DOCKER_CONTAINER_CONFIG": {
"NAME": "glue_jupyter",
"PORT_ON_HOST_JUPYTER_NOTEBOOK": 18888,
"PORT_ON_HOST_SPARK_UI": 14040
}
}
Key | Default Value | Description |
---|---|---|
GENERAL.BASE_IMAGE_NAME | "amazon/aws-glue-libs:glue_libs_1.0.0_image_01" | Base image name with tag provided by AWS. |
GENERAL.DOCKERFILE_LOCATION | "." | Dockerfile location |
GENERAL.MY_IMAGE_NAME | "my-aws-glue-libs" | Custom image name |
LOGGING.CONFIG_FILE_LOCATION | "./logging.conf" | Logging configuration file locaion. |
LOGGING.SEPARATOR_CHAR | "-" | The character to be used for logging separator. |
LOGGING.SEPARATOR_NUM_REPEAT | 60 | The length of logging separator. |
DOCKER_HUB_SECRET.USERNAME | "your username" | Docker Hub username |
DOCKER_HUB_SECRET.PASSWORD | "your password" | Docker Hub password |
DOCKER_HUB_SECRET.EMAIL | "your email" | Docker Hub E-mail Address |
DOCKER_HUB_SECRET.REGISTRY | "https://index.docker.io/v1/" | Docker Hub Registry |
DOCKER_CONTAINER_CONFIG.NAME | "glue_jupyter" | Container name to run locally. |
DOCKER_CONTAINER_CONFIG.PORT_ON_HOST_JUPYTER_NOTEBOOK | 18888 | Host-side port number for Jupyter Notebook/Lab. |
DOCKER_CONTAINER_CONFIG.PORT_ON_HOST_SPARK_UI | 14040 | Host-side port number for Spark UI. |
2-3. Build & Run
Dockerfile を編集します。本リポジトリでは例として、ベースイメージに対して pip の更新、および Jupyter Lab のインストールのみ行っています。
% vi Dockerfile
FROM amazon/aws-glue-libs:glue_libs_1.0.0_image_01
RUN pip install -U pip
RUN pip install jupyterlab
scripts/main.py
を実行し、カスタムイメージを Build & Run します。-t
オプションには、カスタムイメージに対するタグを指定します。タグフォーマットは vX.Y
(X: Major Version, Y: Minor Version) です。
# Tag Format: vX.Y
# - X: Major Version
# - Y: Minor Version
% python scripts/main.py -t vX.Y
2-4. 動作確認
まず、Docker コンテナが起動されているか確認します。docker ps
コマンドを実行すると、確かに glue_jupyter
という名前のコンテナが起動していることが分かります。
% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d77a2b0aadb my-aws-glue-libs:v0.98 "/home/jupyter/jupyt…" 53 seconds ago Up 52 seconds 8080/tcp, 8998/tcp, 0.0.0.0:14040->4040/tcp, :::14040->4040/tcp, 0.0.0.0:18888->8888/tcp, :::18888->8888/tcp glue_jupyter
ホスト側ポート番号 18888
は Docker 側ポート番号 8888 (Jupyter Notebook 用) にバインディングされています。ブラウザから http://localhost:18888/lab
にアクセスすると、Jupyter Lab 環境にアクセスできることを確認できます。
Kernel として "PySpark" を選択し、ノートブックを新規作成します。ファイル名は適当に test_notebook.ipynb
としておきます。
AWS がサンプルとして提供している S3 上の JSON ファイルを Spark DataFrame として読み込んでみます。作成したノートブックのセルに以下をコピー&ペーストし、実行します。
from pyspark import SparkContext
from awsglue.context import GlueContext
glueContext = GlueContext(SparkContext.getOrCreate())
inputDF = glueContext.create_dynamic_frame_from_options(
connection_type = "s3",
connection_options = {"paths": ["s3://awsglue-datasets/examples/us-legislators/all/memberships.json"]},
format = "json")
inputDF.toDF().show()
JSON ファイルの中身を DataFrame としてきちんと表示できているようです。セル実行の際、裏側では Spark Job が起動しており、これに関する情報を Spark UI 上で確認できます。ブラウザから http://localhost:14040/jobs/
にアクセスすることで、Spark UI を確認できます。(設定でバインディングポート番号を変更した場合は、適宜読み替えてください)
以上で Glue ジョブのローカル実行の動作確認は完了です。
3. 機能概要
本章では、機能面の概要説明を付しておきたいと思います。(詳細はソースコードをご覧ください)
3-0. Main 関数の動作
以下は、scripts/main.py
の抜粋です。次の流れで動作します。
- ベースイメージを Pull する。
- カスタムイメージを Build する。
- カスタムイメージを Push する。
- コンテナを Run する。
def main():
# parse agguments
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--tag", help="")
args = parser.parse_args()
new_image_version = args.tag
# pull base image, if not exists
base_image = pull_image(image_name=base_image_name)
# build my image
built_image = build_image(
my_image_name=my_image_name,
new_image_version=new_image_version,
dockerfile_location=dockerfile_location)
# get new image tag
new_image_tag = ":".join([my_image_name, new_image_version])
# push my image
push_image(my_image_name, new_image_version)
# run container from built image
containers = run_container(new_image_tag, restart=True)
# end program with exit code 0
logging.info(logging_separator)
logging.info('This program completed successfully.')
logging.info(logging_separator)
sys.exit(0)
3-1. Pull
ローカルにベースイメージが存在しない場合、これを Pull します。既に存在する場合はスキップされます。
3-2. Build
設定で指定されてカスタムイメージ名、および -t
オプションで指定されたタグを付し、Dockerfile の内容をもとにビルドします。
タグはローカルに既存のバージョンよりも大きなものを指定する縛りを設けています。(特定バージョンが上書かれたり、 "untagged" が大量発生するのを防ぐため)
- 🙆♂️ :
v0.2
-->v1.0
- 🙆♂️ :
v0.2
-->v0.4
- 🙅♂️ :
v0.2
-->v0.2
- 🙅♂️ :
v0.2
-->v0.1
3-3. Push
設定で指定された認証情報を用いて Docker Hub にログインし、Build 済みカスタムイメージを Push します。ログインや Push の実行中に何かしらの問題が発生した場合、Push を自動的にスキップします。
3-4. Run
カスタムイメージからコンテナを起動します。既に指定されたコンテナ名がローカルに存在する場合、エラーを出して処理を中断するか、実行中コンテナ停止・削除した上で再起動するかを選択できます。再起動する場合は、run_container(tag, restart)
関数の引数に restart=True
を与えます。
4. 未採択要件
執筆時点 (2021.9.23時点) で、以下の要件は未採択です。(そのうち取り込むかもしれません)
- 引数の追加、説明付与
- 自動バージョニング ... タグを指定しなかった場合の自動的にタグを付与する
- ビルドスキップ ... 最新カスタムイメージから変更がない場合にビルドをスキップする
- カスタムイメージクリーンアップ ... 指定したバージョン以下のカスタムイメージを削除する
5. 落穂拾い
5-1. AWS 上の Glue ジョブでライブラリを追加する
本記事では、Glue ジョブ実行ランタイムに含まれない Python ライブラリは Dockerfile で追加しました。ローカル環境で開発した Glue ジョブを実際に AWS の環境で実行する際には、これらを外部ライブラリとして Glue ジョブが読み込めるように設定する必要があります。手順の詳細は以下に記載されています。
How do I use external Python libraries in my AWS Glue 1.0 or 0.9 ETL job? - AWS
5-2. Zeppelin Notebook を使用する
本記事では、Jupyter Notebook, Jupyter Lab を使用しましたが、Zeppeline Notebook を使用することもできるようです。その場合、以下を参考にポート番号や実行コマンドを Zeppelin 用のものに読み替えてください。
docker run -itd -p 8080:8080 -p 4040:4040 -v ~/.aws:/root/.aws:ro --name glue_zeppelin amazon/aws-glue-libs:glue_libs_1.0.0_image_01 /home/zeppelin/bin/zeppelin.sh
さいごに
本記事では、Glue ジョブのテスト実行環境をローカルに構築しました。Glue ジョブそのものというよりは、Docker イメージ・コンテナ管理に主眼を置いた記事でした。以前はシェルスクリプトで管理したりもしていましたが、"Docker SDK for Python" の存在を知ってからは専ら Python 管理になりました。Python, Docker、便利ですね。
最後までご覧頂き、ありがとうございました。
参考
- Developing AWS Glue ETL jobs locally using a container | AWS Big Data Blog
- Docker SDK for Python
- How do I use external Python libraries in my AWS Glue 1.0 or 0.9 ETL job? - AWS
EOF
Discussion