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

はじめに

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に向けます。

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バケットに格納するスクリプトを作成してみます。

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を立ち上げます。

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