DockerでlocalGlue開発環境構築

published
2021-03-31

最近、お仕事で Glue を使った開発を行っていたため、備忘録として

Glue の開発エンドポイントを使えない場合(料金的にも)local で開発することができます
また、localstack と組み合わせることで、local 開発のみでいろいろと試せるため、何かと便利です。
今回は localstack の S3 を用いて、localGlue で入出力を行いたいと思います

環境

docker 内で作業するため、あまり関係ないですが

バージョン
windows10
docker20.10.5
docker-compose1.28.5

Glue の Dockerfile

AWS Glue の開発エンドポイントがそこそこお高いのでローカル開発環境を用意しました
↑ まんま用意されているので、こちらを使おうと思ったら、以下のようなエラーが出た
local-glue-docker-error1
ため、上記を少し修正しました

FROM centos:7 RUN yum install -y bzip2 bzip2-devel gcc gcc-c++ make openssl-devel readline-devel zlib-devel wget curl unzip vim epel-release git \ && yum install -y tig jq vim-enhanced bash-completion net-tools bind-utils \ && yum install -y python3 python3-libs python3-devel python3-pip \ && yum install -y java java-1.8.0-openjdk-devel \ && rm -rf /var/cache/yum/* RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 ENV LANG ja_JP.UTF-8 ENV LC_CTYPE "ja_JP.UTF-8" ENV LC_NUMERIC "ja_JP.UTF-8" ENV LC_TIME "ja_JP.UTF-8" ENV LC_COLLATE "ja_JP.UTF-8" ENV LC_MONETARY "ja_JP.UTF-8" ENV LC_MESSAGES "ja_JP.UTF-8" ENV LC_PAPER "ja_JP.UTF-8" ENV LC_NAME "ja_JP.UTF-8" ENV LC_ADDRESS "ja_JP.UTF-8" ENV LC_TELEPHONE "ja_JP.UTF-8" ENV LC_MEASUREMENT "ja_JP.UTF-8" ENV LC_IDENTIFICATION "ja_JP.UTF-8" ENV LC_ALL ja_JP.UTF-8 # Glueライブラリ取得 RUN git clone -b glue-1.0 --depth 1 https://github.com/awslabs/aws-glue-libs # Maven取得 RUN curl -OL https://archive.apache.org/dist/maven/maven-3/3.6.2/binaries/apache-maven-3.6.2-bin.tar.gz RUN tar -xzvf apache-maven-3.6.2-bin.tar.gz RUN mv apache-maven-3.6.2 /opt/ RUN ln -s /opt/apache-maven-3.6.2 /opt/apache-maven ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/jre/ ENV PATH $PATH:/opt/apache-maven/bin RUN mvn -version # Glueアーティファクト取得 RUN curl -OL https://aws-glue-etl-artifacts.s3.amazonaws.com/glue-1.0/spark-2.4.3-bin-hadoop2.8.tgz RUN tar -xzvf spark-2.4.3-bin-hadoop2.8.tgz RUN mv spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8 /opt/ RUN ln -s /opt/spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8 /opt/spark ENV SPARK_HOME /opt/spark # Python3を利用する設定 RUN unlink /bin/python RUN ln -s /bin/python3 /bin/python RUN ln -s /bin/pip3 /bin/pip # 異なるバージョンのjarがsparkとglueに混在するので適切なバージョンのみを見るよう設定 RUN ln -s ${SPARK_HOME}/jars /aws-glue-libs/jarsv1 RUN ./aws-glue-libs/bin/gluepyspark ENTRYPOINT ["/bin/sh", "-c", "while :; do sleep 10; done"]

docker-compose の設定

上記 Dockerfile と localstack を使う compose を定義します

version: '3' services: localstack: container_name: localstack image: localstack/localstack:0.10.9 ports: - 8080:8080 - 4566-4597:4566-4597 environment: - DEFAULT_REGION=us-east-1 - SERVICES=s3 - AWS_ACCESS_KEY_ID=hoge - AWS_SECRET_ACCESS_KEY=hoge glue: container_name: glue build: . volumes: # スクリプトを共有するフォルダ - ./share:/share environment: - AWS_DEFAULT_REGION=us-east-1 # GlueからLocalstackを用いる際に、access_keyとsecret_keyが同じになっている必要がある - AWS_ACCESS_KEY_ID=hoge - AWS_SECRET_ACCESS_KEY=hoge

localstack は、あるバージョンから各サービスのポートが一つにまとまってしまったためガラッと使用感が変わっているので注意してください

詳しくはlocalstack の Githubに乗っています(確か)

↑ 乗ってました

2019-10-09: LocalStack Pro is out! We're incredibly excited to announce the launch of LocalStack Pro - the enterprise version of LocalStack with additional APIs and advanced features. Check out the free trial at https://localstack.cloud

Glue を使ってみる

localGlue には3 つのスクリプトが用意されています

  • gluepyspark
    • 対話モードで gluepyspark を動かしたいならこれ
  • gluesparksubmit
    • スクリプト実行したいならこれ
  • gluepytest
    • テストしたい場合これ

今回は対話型を起動するまで、で

1: glue イメージにアタッチする

compose で立ち上げて、対象の image にアタッチします

$ docker-compose up -d $ docker-compose exec glue ./bin/bash [root@edfaf015156a /]#

2: 対話型を立ち上げる

アタッチしたパスから aws-glue-libs/bin/直下に上記 3 つのスクリプトがあります

[root@edfaf015156a /]# aws-glue-libs/bin/gluepyspark ...省略 Welcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /__ / .__/\_,_/_/ /_/\_\ version 2.4.3 /_/ Using Python version 3.6.8 (default, Nov 16 2020 16:55:22) SparkSession available as 'spark'. >>>

上記のように出れば成功ですー

Glue で localstackS3 を用いて入出力

テスト用の csv か何か用意してください
おすすめは kaggle にあるポケモンデータです

バケット・データを localstackS3 に用意する

読み込むデータと、バケットを localstack の S3 に用意します

方法は、aws cli を使う感覚と全く同じですが、後ろに--endpoint-urlパラメータを渡してあげないといけないです

# 4572ポートがs3 $ aws s3 mb s3://test-csv --endpoint-url="http://localhost:4572" make_bucket: test-csv $ aws s3 cp Pokemon.csv s3://test-csv/ --endpoint-url="http://localhost:4572" upload: Pokemon.csv to s3://test-csv/Pokemon.csv

これで localstack を用いた s3 側の準備は OK です

次にスクリプトです。
注意点はlocalGlue は from_catalog が使用できないということです

import sys import json from pyspark.context import SparkContext from awsglue.context import GlueContext from awsglue.job import Job from awsglue.utils import getResolvedOptions job_params = [ 'JOB_NAME', ] args = getResolvedOptions(sys.argv, job_params) # ローカルs3を対象にするため、hadoopにendpointの設定を行う sc = SparkContext() sc._jsc.hadoopConfiguration().set("fs.s3a.endpoint", "http://localstack:4572") sc._jsc.hadoopConfiguration().set("fs.s3a.path.style.access", "true") sc._jsc.hadoopConfiguration().set("fs.s3a.signing-algorithm", "S3SignerType") glue_context = GlueContext(sc) spark = glue_context.spark_session job = Job(glue_context) job.init(args['JOB_NAME'], args) dyf = glue_context.create_dynamic_frame.from_options( connection_type="s3", connection_options={ "paths": ["s3a://test-csv/"] }, format="csv", format_options={ "withHeader": True }, ) # ここでdyfに対して何かしら処理をする glue_context.write_dynamic_frame.from_options( frame=dyf, connection_type="s3", connection_options={ "path": "s3a://test-csv/write/" }, format='csv' ) job.commit()

中身は公式サンプルを参考に、localstack 用に弄りました
s3a とか s3n とかの違いはこちらを参照ください

次に、スクリプトとして実行します

[root@edfaf015156a /]# aws-glue-libs/bin/gluesparksubmit /share/glue.py --JOB_NAME 'test' ...# 省略 21/04/05 14:41:16 INFO SparkContext: Successfully stopped SparkContext 21/04/05 14:41:16 INFO ShutdownHookManager: Shutdown hook called 21/04/05 14:41:16 INFO ShutdownHookManager: Deleting directory /tmp/spark-d2b1e11e-f253-4949-a5eb-b627cd9d7610/pyspark-7ae5c6ac-d888-499a-9e33-112f255c5bef 21/04/05 14:41:16 INFO ShutdownHookManager: Deleting directory /tmp/spark-d2b1e11e-f253-4949-a5eb-b627cd9d7610 21/04/05 14:41:16 INFO ShutdownHookManager: Deleting directory /tmp/spark-78c7a151-ac61-412e-bc92-eb1351e04f3a [root@edfaf015156a /]#

Successfully となっていて、作業ディレクトリも削除されていることが log で出力されます

最後に中身を確認します

[root@7e42382cfe7a /]# aws s3 ls s3://test-csv/write/ --endpoint-url="http://localstack:4572"
2021-04-05 14:41:16      44230 run-1617633674695-part-r-00000

中身確認しませんが、まぁ大丈夫でしょう!

参考資料

AWS Glue ETL ライブラリを使用した ETL スクリプトのローカルでの開発とテスト

AWS Glue の開発エンドポイントがそこそこお高いのでローカル開発環境を用意しました