AWS Glueのテスト環境をローカルに構築

# Glueをローカル環境でテストしたい
AWSのLambdaがローカル環境で開発できるため、Glueもできないかなーと思い調べたところ、AWSからライブラリが提供されていました。
最初は個別でインストールしての利用を試みたのですが、うまくいかず…。
ググったところ、Dockerで一気に作成する手順を発見。ローカル開発環境の構築に成功しました。

#前提条件
 - CentOs 7.6
 - Docker 19.03.2

#大まかな手順
1. Dockerファイルを作製
2. 1.に基づき、コンテナをビルド
3. コンテナを起動
4. コンテナにログインしgluepyskarkを起動

#手順詳細
### 1. Dockerファイルを作製
適当なディレクトリに、Dockerfileという名前のテキストファイルを作製し、そこに設定内容を記述します。
※Emacs派なので、viでなくemacsで開いてます。

```terminal
$ mkdir aws-glue-local
$ cd aws-glue-local/
$ emacs Dockerfile
```
下記Dockerfileは、フューチャー開発者ブログさんの[こちらの記事](https://future-architect.github.io/articles/20191101/)を引用させていただきました。
※CA証明書の部分を追加しました。

```text:Dockerfile

FROM centos:7

######### CA証明書のインストール（セキュリティソフト等の関係でCAインストール必要な場合はコメントを外して実行）########
######### CA証明書（ここではmyca.crt）は、aws-glue-local内部に配置しておくこと。
#COPY myca.crt /usr/share/pki/ca-trust-source/anchors/ 
#RUN update-ca-trust extract
############################################################################################################

# https://omohikane.com/centos7_docker_python36/ を参考にpythonとjavaをインストール
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 https://repo.ius.io/ius-release-el7.rpm \
    && yum install -y python36u python36u-libs python36u-devel python36u-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

####################################################################################################
#### SSL証明書をインポートしてもmavenでSSL通信エラーが発生する場合は、コメントを外して実行。
#### MAVENオプションの記述内容の意味するところは
#### -Dmaven.wagon.http.ssl.insecure = true – ユーザー生成したCA証明書に対する安全でないSSLの使用を有効化
#### -Dmaven.wagon.http.ssl.allowall = true – サーバーのX.509証明書とホスト名の一致を有効化。
####                                          無効にすると検証済みのブラウザが使用される
#### -Dmaven.wagon.http.ssl.ignore.validity.dates = true – 証明書生成日付の問題を無視
#####################################################################################################
#RUN echo "MAVEN_OPTS=\"-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true\"" >> ~/.mavenrc 

# 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.6を利用する設定
RUN unlink /bin/python
RUN ln -s /bin/python3 /bin/python
RUN ln -s /bin/pip3.6 /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"]
```

### 2. Dockerfileに基づき、コンテナをビルド
dockerコマンドを用いて、コンテナを作製します。
Dockerfileがあることを確認し、ビルドコマンドを実行

```terminal
$ ls #Dockerfileがあるか確認
Dockerfile
$ docker build . -t aws-glue-local-image #イメージ名はaws-glue-local-imageで作製
```

成功の場合：完了まで20分くらい
失敗の場合：数秒で処理終了

####【ビルド成功】BUILD SUCCESS と表示される
この場合は、次のステップ（コンテナの起動）へ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389667/1ec344eb-5b70-b9d0-c31e-fcc0099b2b52.png)

####【ビルド失敗】BUILD FAILURE と表示される
この場合は、エラーメッセージを頼りに原因を特定しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389667/aa0ab0af-11f5-7dba-ec49-a4e885611925.png)
当方の環境では、yum等のSSL通信時にCA証明書が必要だったため、ビルドが失敗しました。
そのためDockerfileにCA証明書のインストールを記述、対応させました。

### 3. コンテナを起動
ビルド完了後、コンテナを起動させます。
docker run の時、&を付けるのは永久ループでのTerminal応答停止を防ぐためです。

```terminal
$ docker images  # dockerイメージの確認
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
aws-glue-local-image   latest              4daf5c8dd6ec        4 hours ago         2.07GB
centos                 7                   5e35e350aded        9 days ago          203MB

$ docker run --name aws-glue-local aws-glue-local-image & # 作成したイメージから、コンテナ名aws-glue-localで起動させる
```

### 4. コンテナにログインしgluepyskarkを起動
コンテナの起動を確認し、ログインします。
gluepyspark起動時にエラーが起きず、Glueのモジュールが利用できれば成功！

```terminal
$ docker ps  # 起動中のコンテナを確認
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS               NAMES
9b9886411b08        aws-glue-local-image   "/bin/sh -c 'while :…"   4 hours ago         Up 4 hours                              aws-glue-local

$ docker exec -it aws-glue-local bash # aws-glue-localコンテナに、bashでログイン
# ./aws-glue-libs/bin/gluepyspark # ログインするとrootになるので、gluepysparkを実行


~~~~中略~~~~ 
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.4.3
      /_/

Using Python version 3.6.8 (default, Aug  7 2019 17:28:10)
SparkSession available as 'spark'.
>>> from awsglue.context import GlueContext # GlueContextを試しにインポート
>>> GlueContext # 中身確認
<class 'awsglue.context.GlueContext'>
>>> quit() # gluepyspark終了
# exit #コンテナから出る
$ docker stop aws-glue-local #コンテナを停止
aws-glue-local
$ docker ps #コンテナを確認（何もない）
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
$ docker ps -a #停止コンテナ含めて確認（ちゃんといる）
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                       PORTS               NAMES
9b9886411b08        aws-glue-local-image   "/bin/sh -c 'while :…"   4 hours ago         Exited (137) 4 minutes ago                       aws-glue-local

```


### dockerコマンドメモ

```terminal
$ docker build Dockerfileの場所 -t イメージ名 #Dockerイメージの作成
$ docker images #Dockerimage一覧
$ docker run --name コンテナにつける名前 稼働元のイメージ名/ID #Dockerコンテナの起動
$ docker exec -it コンテナ名/ID コマンド #稼働中のコンテナにコマンドを送信※bashを指定するとTerminalログインできる
$ docker ps #コンテナ一覧（稼働中）
$ docker ps -a #コンテナ一覧（停止中含む）
$ docker stop コンテナ名 #コンテナの停止
$ docker rm コンテナ名 #コンテナ削除
$ docker rmi イメージID #イメージ削除
```

#参考
 - AWS Glue 開発者ドキュメント : [AWS Glue ETL ライブラリを使用した ETL スクリプトのローカルでの開発とテスト](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/aws-glue-programming-etl-libraries.html)
 - フューチャー開発者ブログ : [AWS Glueの開発エンドポイントがそこそこお高いのでローカル開発環境を用意しました](https://future-architect.github.io/articles/20191101/)
 - コードログ : [MavenにSSLエラーを無視して（そしてすべての証明書を信頼するように）指示する方法？](https://codeday.me/jp/qa/20181221/76184.html)
 - Stack Over Flow : [how-to-tell-maven-to-disregard-ssl-errors-and-trusting-all-certs](https://stackoverflow.com/questions/21252800/how-to-tell-maven-to-disregard-ssl-errors-and-trusting-all-certs)