概要 フューチャーアドベントカレンダー の6日目のエントリーです。 昨日はyut0n さんによる「GoogleカレンダーのイベントをHangouts Chatに通知するbotを作った話 」でした。
当記事では、AWS Glue をローカル環境で単体テストするための環境構築方法についてまとめました。
手順
環境構築
pytest の環境構築
conftest.py の設定
テスト対象の作成
テスト実行
実行環境
Amazon Linux 2 AMI 2.0.20190618 x86_64 HVM gp2
Docker 18.06.1-ce
docker-compose version 1.24.0
1. 環境構築 docker compose を利用します。 GlueのDockerfileは、 こちらの記事(AWS Glueの開発エンドポイントがそこそこお高いのでローカル開発環境を用意しました | Future Tech Blog - フューチャーアーキテクト ) にて、紹介されているDockerfileをベースに利用します。 少々イメージサイズが大きかったので、小さくする対応をしていますが基本は同じです。
ディレクトリ構成
├── Dockerfile ├── docker-compose.yml ├── src └── tests
Dockerfile
S3の環境が必要だったため、 LocalStack を利用しています。
docker-compose.yml version: "3" services: glue.local: build: context: ./ container_name: gluelocal volumes: - ./src:/opt/src/src - ./tests:/opt/src/tests environment: - AWS_DEFAULT_REGION=ap-northeast-1 - AWS_DEFAULT_OUTPUT=json - AWS_ACCESS_KEY_ID=xxx - AWS_SECRET_ACCESS_KEY=xxx aws.local: image: localstack/localstack environment: - SERVICES=s3 - DEFAULT_REGION=ap-northeast-1 - AWS_DEFAULT_REGION=ap-northeast-1 - AWS_DEFAULT_OUTPUT=json - AWS_ACCESS_KEY_ID=xxx - AWS_SECRET_ACCESS_KEY=xxx
コンテナ起動
docker-compose up -d --build
2. pytestの環境構築 必要なパッケージのインストールをします。Glueバージョン 1.0 を想定して、pysparkは2.4.3を明示的にインストールします。
3. conftest.py の設定 pytestではテストの前後処理を tests/conftest.py 内に実装する慣習があるためそれにならいます。 Test実行時に1回だけ実行したい処理をまとめています。
4. テスト対象の作成 サンプル程度に、S3上のcsvファイルからDynamicFrameを生成する関数をテストします。
Glueスクリプト: src/etl.py
from awsglue.dynamicframe import DynamicFramefrom awsglue.context import GlueContextimport sysdef load_dynamic_frame_from_csv (glueContext: GlueContext, spark, bucket: str , path: str ) -> DynamicFrame: p = "s3://{}/{}" .format (bucket, path) return glueContext.create_dynamic_frame_from_options( connection_type="s3" , connection_options={"paths" : [p]}, format ="csv" , format_options={"withHeader" : True , "separator" : "," }, )
テストコード: tests/test_etl.py
import pytestimport boto3import jsonimport osimport sysimport ioimport csvfrom src.etl import load_dynamic_frame_from_csvfrom botocore.client import Configdef test_load_dynamic_frame_from_csv (): inputs = [ { "id" : "1" , "name" : "xxx" , "address" : "xxx@example.co.jp" }, { "id" : "2" , "name" : "yyy" , "address" : "yyy@example.co.jp" } ] input_str = io.StringIO() w = csv.DictWriter(input_str, fieldnames=inputs[0 ].keys()) w.writeheader() for input in inputs: w.writerow(input ) s3 = boto3.resource( "s3" , endpoint_url=os.environ["TEST_S3_ENDPOINT_URL" ], region_name="ap-northeast-1" , use_ssl=False , config=Config(s3={"addressing_style" : "path" }), ) bucket_name = "test-csv-bucket" bucket = s3.Bucket(bucket_name) bucket.create(ACL="public-read-write" ) body = input_str.getvalue() key = "user/2019/12/06/users.csv" bucket.put_object(Key=key, Body=body, ACL="public-read-write" ) res_df = load_dynamic_frame_from_csv(pytest.glueContext, pytest.spark, bucket_name, key) assert res_df.count() == len (inputs) res_df_json = res_df.toDF().toJSON().take(len (inputs)) for res in res_df_json: r = json.loads(res) assert r in inputs
5. テスト実行 Glue環境を構築して pytest を実行する gluepytest コマンドが用意されているため、そちらを利用します。 PATHを通してあるので、下記で実行できます。
docker exec -it gluelocal gluepytest
結果
所感 Glueをローカル環境にて、単体テストを実施する環境を整備してみました。 Glueの動作確認は、開発エンドポイントを利用して確認することが多く、 少々面倒かつ再現性がなかったため、テスト環境を構築してテストを実行することで安定して開発を進めていきたいです。 Dockerで作成しているため、CI/CD等にも組み込んでいけたらと考えています。 (Dockerfileが1GB程度とまだまだ大きいため、もう少しスリムにしたいなとは思っています。)
参考