AWS Glue ETLライブラリにて、ローカルPCにGlue開発環境をつくる

aws-glue-libs を利用するば、ローカルPC上でGlue開発環境を用意できるとのこと、試してみます。

aws-glue-libs

This repository contains libraries used in the AWS Glue service. These libraries extend Apache Spark with additional data types and operations for ETL workflows. They are used in code generated by the AWS Glue service and can be used in scripts submitted with Glue jobs.

下記のマニュアルを参考にしています。

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

利用可能Glueバージョン

aws-glue-libsでは、以下バージョンのGlue環境を作成できます。

Glue Spark Python
0.9 2.2.1 2.7
1.0 2.4.3 2.7
3.6

Glue バージョン

今回はGlue1.0の環境を、Python3.6を使って用意してみます。

環境作成

Dockerfileを作成しているので、実際に必要となる処理については、Dockerfile内の記載を確認してください。

FROM centos:7

# python 3.6.9
WORKDIR /usr/local
RUN yum -y update && yum -y groupinstall --skip-broken "Development tools" \
    && yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel bsdtar git \
    && rm -rf /var/cache/yum/* \
    && yum clean all \
    && curl -fsSL https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz -o /usr/local/Python-3.6.9.tar.xz \
    && tar -Jxf /usr/local/Python-3.6.9.tar.xz \
    && rm -rf /usr/local/Python-3.6.9.tar.xz \
    && cd /usr/local/Python-3.6.9/ \
    && ./configure --prefix=/usr/local/python369 --with-ensurepip=install --enable-optimizations \
    && make && make install && make clean

# java 1.8.0
RUN yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel \
    && rm -rf /var/cache/yum/* \
    && yum clean all
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64/jre

# glue-common/apache-maven-3.6.0
WORKDIR /opt
RUN curl -fsSL https://aws-glue-etl-artifacts.s3.amazonaws.com/glue-common/apache-maven-3.6.0-bin.tar.gz -o /opt/apache-maven-3.6.0-bin.tar.gz \
    && tar -xzvf /opt/apache-maven-3.6.0-bin.tar.gz \
    && rm -f /opt/apache-maven-3.6.0-bin.tar.gz
ENV PATH $PATH:/opt/apache-maven-3.6.0/bin

# glue-1.0/spark-2.4.3
RUN curl -fsSL https://aws-glue-etl-artifacts.s3.amazonaws.com/glue-1.0/spark-2.4.3-bin-hadoop2.8.tgz -o /opt/spark-2.4.3-bin-hadoop2.8.tgz \
  && bsdtar -xzvf /opt/spark-2.4.3-bin-hadoop2.8.tgz \
  && rm -f /opt/spark-2.4.3-bin-hadoop2.8.tgz
ENV SPARK_HOME /opt/spark-2.4.3-bin-spark-2.4.3-bin-hadoop2.8

# aws-glue-libs
RUN git clone -b glue-1.0 https://github.com/awslabs/aws-glue-libs
ENV PATH $PATH:/opt/aws-glue-libs/bin

RUN unlink /bin/python \
  && ln -s /usr/local/python369/bin/python3 /bin/python \
  && ln -s /usr/local/python369/bin/pip3 /bin/pip

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"

# aws cli
RUN pip install awscli
ENV PATH $PATH:/usr/local/python369/bin

# https://github.com/awslabs/aws-glue-libs/issues/25
RUN ln -s ${SPARK_HOME}/jars /opt/aws-glue-libs/jarsv1

RUN /opt/aws-glue-libs/bin/gluepyspark

pythonをmake installしてるのと、mavenの処理で、buildにすっごい時間がかかります。

トラブルシューティング

gluepyspark コマンド実行時に、以下のエラーが出てきて正常にpysparkが起動してくれず、困りました。

Py4JJavaError: An error occurred while calling None.org.apache.spark.api.java.JavaSparkContext.
: java.lang.NoSuchMethodError: io.netty.buffer.PooledByteBufAllocator.defaultNumHeapArena()I
        at org.apache.spark.network.util.NettyUtils.createPooledByteBufAllocator(NettyUtils.java:113)
        at org.apache.spark.network.client.TransportClientFactory.<init>(TransportClientFactory.java:106)
        at org.apache.spark.network.TransportContext.createClientFactory(TransportContext.java:99)
        at org.apache.spark.rpc.netty.NettyRpcEnv.<init>(NettyRpcEnv.scala:71)
        at org.apache.spark.rpc.netty.NettyRpcEnvFactory.create(NettyRpcEnv.scala:461)
        at org.apache.spark.rpc.RpcEnv$.create(RpcEnv.scala:57)
        at org.apache.spark.SparkEnv$.create(SparkEnv.scala:249)
        at org.apache.spark.SparkEnv$.createDriverEnv(SparkEnv.scala:175)
        at org.apache.spark.SparkContext.createSparkEnv(SparkContext.scala:257)
        at org.apache.spark.SparkContext.<init>(SparkContext.scala:424)
        at org.apache.spark.api.java.JavaSparkContext.<init>(JavaSparkContext.scala:58)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:247)
        at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
        at py4j.Gateway.invoke(Gateway.java:238)
        at py4j.commands.ConstructorCommand.invokeConstructor(ConstructorCommand.java:80)
        at py4j.commands.ConstructorCommand.execute(ConstructorCommand.java:69)
        at py4j.GatewayConnection.run(GatewayConnection.java:238)
        at java.lang.Thread.run(Thread.java:748)

そのため、Dockerfile内で以下の記載を追加しています。

RUN ln -s ${SPARK_HOME}/jars /opt/aws-glue-libs/jarsv1

以下を参考にしています。

gluepyspark errors on local development

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

コンテナ起動

作成したイメージを起動します。ローカルPCにあるDocker-machine上で起動することを想定しています。

コンテナ起動時に、AWSの認証情報(IAMユーザのアクセスキー)を渡してあげることになります。なので、そのためのENVファイルを用意しておきます。下記みたくですね。Glue開発環境で利用される認証情報のため、Glue Data CatalogやS3への権限をもたせてあげてください。

AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXX
AWS_REGION=ap-northeast-1

コンテナ起動します。

> docker run -itd --name glue --env-file (envfile) -v /c/Users/zunda/Desktop/volume:/mnt/volume (image) /bin/bash

コンテナに入ると、Glue環境のSparkを利用可能となります。

> docker exec -it (container_id) /bin/bash

$ gluepyspark
...
...
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.4.3
      /_/

Using Python version 3.6.9 (default, Nov 29 2019 11:08:08)
SparkSession available as 'spark'.
>>> import sys
>>> from awsglue.transforms import *
>>> from awsglue.utils import getResolvedOptions
>>> from pyspark.context import SparkContext
>>> from awsglue.context import GlueContext
>>>
>>> glueContext = GlueContext(SparkContext.getOrCreate())
>>> voiceloid = glueContext.create_dynamic_frame.from_catalog( database="voiceloid", table_name="classifier_voiceloid")
>>> voiceloid.printSchema()
root
|-- voiceroid_id: long
|-- name: string
|-- japanese_name: string
>>>