AWS Glueをローカル環境で実行してみた

2019年8月28日にGlue ETLライブラリのバイナリがリリースされました。これにより、ローカル環境でGlueのETLスクリプトを実行出来るようになります。今回はローカル環境でGlue Python ETLライブラリを使用して、ETLスクリプトを実行してみます。
2019.09.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部の高橋雄大です。

2019年8月28日にGlue ETLライブラリのバイナリがリリースされました。このリリースにより、ローカルの開発環境でGlueのETLスクリプトを実行出来るようになりました。

AWS Glue、Glue ジョブ用 Glue ETL ライブラリのバイナリをリリース

目次

開発環境

項目 バージョン 備考
macOS Mojave 10.14.6
AWS CLI 1.16.233
Apach Maven 3.6.2 Mavenをダウンロード
Java Development Kit 8 (JDK) 1.8 JDKをダウンロード
Apache Spark  2.2.1 Sparkをダウンロード
Python 2.7.1
LocalStack 0.10.2 LocalStack

環境変数を設定します。MavenとSparkのパスは個人の環境に合わせて変更してください。

echo 'export PATH=$HOME/.apache-maven-3.6.2/bin:$PATH' >> ~/.bash_profile
echo 'export SPARK_HOME=$HOME/.spark-2.2.1-bin-hadoop2.7' >> ~/.bash_profile
echo 'export JAVA_HOME=`/usr/libexec/java_home -v 1.8`' >> ~/.bash_profile

Pythonでローカル開発

AWS Glue Pythonライブラリを取得

GitHubからAWS Glue Pythonライブラリをダウンロードします。以降の作業はAWS Glue Pythonライブラリのルートディレクトリで行います。

https://github.com/awslabs/aws-glue-libs

PySparkをインストール

pipコマンドでpysparkパッケージをインストールします。

$ pip install pyspark

Apache Sparkの環境設定

Sparkの設定ファイルを作成します。DynamoDBとS3のエンドポイントをLocalStackに向けます。

conf/spark-defaults.conf

spark.hadoop.dynamodb.endpoint http://localhost:4569
spark.hadoop.fs.s3a.endpoint http://localhost:4572
spark.hadoop.fs.s3a.path.style.access true
spark.hadoop.fs.s3a.signing-algorithm S3SignerType

PythonでETLスクリプトを作成

DynamoDBから全ての項目を取得して、S3バケットに格納するスクリプトを作成してみます。

src/sample.py

import sys
from awsglue.transforms import ApplyMapping
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job
from awsglue.dynamicframe import DynamicFrame

DYNAMODB_INPUT_TABLE_NAME = 'aws-glue-local-test-table'
S3_OUTPUT_BUCKET_NAME = 'aws-glue-local-test-bucket'

args = getResolvedOptions(sys.argv, [
    'JOB_NAME'
])

JOB_NAME = args['JOB_NAME']

sc = SparkContext()
glueContext = GlueContext(sc)
job = Job(glueContext)
job.init(JOB_NAME, args)

datasource = glueContext.create_dynamic_frame.from_options(
    connection_type='dynamodb',
    connection_options={
        'dynamodb.input.tableName': DYNAMODB_INPUT_TABLE_NAME
    }
)

applymapping = ApplyMapping.apply(
    frame=datasource,
    mappings=[
        ('Id', 'string', 'Id', 'string'),
        ('Column1', 'string', 'Column1', 'string'),
        ('Column2', 'string', 'Column2', 'string'),
        ('Column3', 'string', 'Column3', 'string')
    ]
)

glueContext.write_dynamic_frame.from_options(
    frame=datasource,
    connection_type='s3',
    connection_options={
        'path': 's3://' + S3_OUTPUT_BUCKET_NAME
    },
    format='csv'
)

job.commit()

ETLスクリプトの実行

LocalStackの準備

docker-composeでLocalStackを立ち上げます。

docker-compose.yml

version: "3.3"

services:
  localstack:
    container_name: localstack
    image: localstack/localstack
    ports:
      - "8080:8080"
      - "4569:4569"
      - "4572:4572"
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock
      - DEFAULT_REGION=ap-northeast-1
      - SERVICES=dynamodb,s3
$ docker-compose up -d

DynamoDBテーブルの準備

DynamoDBのテーブルを作成します。

$ aws dynamodb create-table \
    --table-name aws-glue-local-test-table \
    --attribute-definitions \
        AttributeName=Id,AttributeType=S \
    --key-schema AttributeName=Id,KeyType=HASH \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --endpoint-url http://localhost:4569

テスト用のデータを登録します。

$ aws dynamodb put-item \
    --table-name aws-glue-local-test-table  \
    --item \
        '{"Id": {"S": "test"}, "Column1": {"S": "test1"}, "Column2": {"S": "test2"}, "Column3": {"S": "test3"}}' \
    --endpoint-url http://localhost:4569

S3バケットの準備

S3のバケットを作成します。

$ aws s3api create-bucket \
    --bucket aws-glue-local-test-bucket \
    --endpoint-url http://localhost:4572

実行コマンド

AWS Glue Pythonパッケージのルートディレクトリで以下のコマンドを実行できます。

コマンド 説明
./bin/gluepyspark ライブラリと統合するシェルにPythonスクリプトを入力して実行します。
./bin/gluesparksubmit Pythonスクリプトを指定して実行します。
./bin/gluepytest Pythonの単体テストを実行します。pytestモジュールが必要です。

動作確認

認証情報が設定されていないとエラーになるので、環境変数をセットしておきます。

$ export AWS_ACCESS_KEY_ID='dummy'
$ export AWS_SECRET_ACCESS_KEY='dummy'
$ export AWS_REGION='ap-northeast-1'

Pythonスクリプトを指定して実行します。--JOB_NAMEは必須なのでdummyをセットしておきます。初回の実行時はGlue ETLライブラリのバイナリをダウンロードしてくるので、多少時間がかかります。

$ ./bin/gluesparksubmit ./src/sample.py --JOB_NAME='dummy'

S3バケットにCSVファイルが作成されていることを確認します。

$ aws s3api list-objects \
    --bucket aws-glue-local-test-bucket \
    --endpoint-url http://localhost:4572

S3からローカルにダウンロードして内容を確認します。

$ aws s3 cp s3://aws-glue-local-test-bucket ~/Downloads/aws-glue-local-test-bucket \
    --recursive \
    --endpoint-url http://localhost:4572

最後に

AWS Glueの開発において、今まで時間がかかっていたデバッグ作業が改善できました。pytestで単体テストを実装することもできるので、AWS環境へデプロイ前に簡単な動作確認ができそうです。Glue Python ETLライブラリは発展途上で、まだ不足している機能がたくさんあります。今後のアップデートに期待しています。

参考

Developing and Testing ETL Scripts Locally Using the AWS Glue ETL Library