AWS GlueのジョブでVPCエンドポイントを利用したS3接続を行う

## 先に結論
* GlueジョブでVPCエンドポイントを介してS3接続をおこなうにはJDBCタイプの接続設定が必要

## やりたいこと
AWS GlueのETLジョブではVPCエンドポイントを使用して、インターネットを経由せず
S3へのアクセスを行うことができます。
以下のドキュメントに記載されている通りです。

**Amazon VPC Endpoints for Amazon S3**
https://docs.aws.amazon.com/glue/latest/dg/vpc-endpoints-s3.html

イメージ図もそのまま引用させていただきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/d68b9e4b-76a5-de8b-d5d1-4505d35248c7.png)

Glueが指定したプライベートサブネット上でENIを作成し、そのENIに割り当てられたプライベートIPで
VPCエンドポイントを経由してS3にアクセスします。

やりたいことはこれだけなのですが、ジョブ自体にはS3のアクセスやVPCサブネットを指定する方法がなく
設定に戸惑ったため、手順を残しておきます。

ここでは以下の内容には触れません。

* VPCやVPCエンドポイントの作成、設定
* Glueジョブの作成

## 接続設定の追加
Glueのコンソールメニューから、データカタログのデータベース→接続と進み、接続の追加を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/dda1eb81-e923-d924-a111-9e9d5f042113.png)

接続のプロパティの設定で任意の接続名を入力し、接続タイプで **JDBC** を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/9ca3b0eb-38e7-c387-58b7-ecc99637a4cc.png)

現状、データソースとして**S3にプライベートアクセスを行いたいだけの場合であっても
このような接続設定を介す必要があります**。
これがわかりにくかった。入力したら次に進みます。

今回はデータベースへの接続は行わないのですが、JDBC URL/ユーザ名/パスワードが必須
になっているのでここではダミーの情報を入力しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/9155197f-c527-c0b0-1180-c1e00560b68f.png)

もちろんオンプレミスデータベースの接続情報を入力し、DirectConnectを介して
接続するなどの用途にも実際には使用することが可能です。

更にその下の設定でVPC/Subnet/セキュリティグループを指定することができます。
ここで指定するプライベートサブネットにS3のVPCエンドポイントが存在し、
適切なルート情報が設定されていれば、GlueジョブはS3へプライベート接続を行います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/ba7982ee-5c3f-17bd-7ee5-b19532028fab.png)

設定するセキュリティグループには全てのTCPポートに対する自己参照のインバウンドルール
が含まれている必要があります。自己参照ルールというのは以下のように
送信元に自身のセキュリティグループが設定されているルールのことを指します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/232eb433-24e6-76de-af34-6d4c54f3073a.png)

最後に設定の確認を行い、接続の追加を完了します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/68bd4bae-d2b9-b727-c583-ef4dd23c9bdd.png)
ちなみに接続のテストは指定したJDBC URLに対し接続確認を行います。
**エンドポイントを経由したS3アクセスを確認するものではありません。**
ダミーのJDBC情報を入力している場合は、当然のことながらテストは失敗します。

## ジョブ設定の編集
対象のジョブ設定で、**すべての接続**に先ほど追加した接続設定が追加されています。
選択をクリックすると**要求される接続**に設定が追加されますので、この状態でジョブ設定を保存します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/3cec0a7b-c436-ea02-254e-f4068f1269af.png)

## 動作確認
バケットポリシーで対象VPCエンドポイント経由のみを許可したバケットをデータソース/結果出力先として
ジョブを実行し、正常終了することを確認しました。
またCloudWatch Logsに保存されているジョブのログを参照し、VPCのプライベートIPが使用されていること
を確認することもできます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/2efed1bf-933c-33a1-bba3-2ed8ba950988.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/004ff951-9538-27eb-80cf-0b998ff996de.png)

## 注意点など
### S3バケットのリージョン
S3のVPCエンドポイントを使用する際の基本的な注意点ではありますが、
VPCエンドポイントが存在するリージョンとS3バケットが存在しているリージョンは
同一である必要があります。

### サブネットの利用可能なIP数を考慮する
前述の通りプライベート接続を使用したジョブを実行するとGlueにENIが割り当てられます。
特にサブネットを細かく分割している場合に、サブネットで利用可能なIPv4の残数に注意してください。

気がつくと結構な数のENIが割り当てられていたりします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/1e6c63d7-6dc0-d861-fb25-170bd00d0d19.png)

Resource unavailable（メッセージがわかりにくい…）でジョブがFailedする場合は
IPが枯渇している可能性が高いです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/8acb072a-095b-d944-c1bb-b646e4e96d15.png)

**Troubleshooting Errors in AWS Glue - Error: Resource Unavailable**
https://docs.aws.amazon.com/glue/latest/dg/glue-troubleshooting-errors.html#error-resource-unavailable
> For any connections and development endpoints that you use, check that your cluster has not run out of elastic network interfaces.

### ジョブ起動までの時間
些細な点ですが、プライベート接続を使用したジョブを起動すると通常よりも処理開始まで
時間がかかるようになりました。ENIの割当等で余計に時間を使っているのだと思われます。

### Python shell
最近追加されたPython shellで試してみましたが、ジョブに接続設定を追加しても
現状はVPCエンドポイント経由の接続とはならないようです。
以下のような超簡易ジョブを実行してみたところ、指定したVPCのIPアドレスにならなかったので。

```py
import os
os.system("env")
os.system("ip -f inet addr")
```
結果は以下のような感じ。

```
HOSTNAME=ip-172-31-41-88
USE_PROXY=true
PYTHON_PIP_VERSION=9.0.1
HOME=/ 
AWS_DEFAULT_REGION=ap-northeast-1 PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/amazon/bin:/opt/amazon/usr/bin
LANG=C.UTF-8
PYTHON_VERSION=2.7.14
GLUE_INSTALLATION=/glue/lib/installation 
PWD=/tmp PYTHONPATH=:/glue/lib/installation
ERROR_FILE_NAME_LOCATION=/reporting/error.txt

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
inet 169.254.76.2/32 scope global eth0 valid_lft forever preferred_lft forever
```



**Introducing Python Shell Jobs in AWS Glue**
https://aws.amazon.com/jp/about-aws/whats-new/2019/01/introducing-python-shell-jobs-in-aws-glue/

以上です。
参考になれば幸いです。