【AWS Glue】JOBを実行した際によくあるエラーについて図解でまとめてみた

<h1>１．はじめに</h1>
こんにちは、株式会社ジールの田崎です。<br>
AWS GlueのJOBを利用する際によくあるエラーとその対処方法についてまとめました。

Glue JOBでよく発生するエラーをインフラの観点から図で解説していきたいと思います。
今後、エラーが起きた際のトラブルシューティングに役立てて頂ければ幸いです。

<h1>２．Glueの基本的な構成</h1>
AWS GlueはAWSが提供しているETL基盤です。<br>
サーバレスで実行できるため、インフラ面を意識せずともすぐにETLパイプラインを構築することが出来ます。

データ分析基盤を構築する際の基本的な構成図及び通信の方法は下記でございます。

<b>①スケジュール/手動実行等で、Glue JOBを実行する。
②Glueコンソールにて作成した接続定義に記載のサブネット内にて、Glue JOBが起動する。
（実態はENIという、サブネット内の空いているIPアドレスが割り振られた部品が起動する）
③サブネット内から各データソース/ターゲットに対して通信が行われる。</b>

<img width="900
" alt="Glue_全体.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/9c8de013-c7ef-b3c5-f38d-d48ffbb25220.png">

インフラの観点から見た場合、<b>③にてエラーが発生する場合が多い</b>ので、２章ではそちらについて解説させて頂きます。

<h1>３．よくあるエラーについて</h1>

本章では、インフラの観点からGlueで発生しがちなエラーについて説明致します。

<h3>１．接続時にエラーが出てしまう、起動するがS3にデータが作成されない等</h3>

接続時のエラーについては、主に３つの原因が考えられます。<br>

<b>①Glue JOBから対象リソースへの経路(ルート)が無い
②Glue JOBに設定しているIAMロールに、リソースへのアクセス許可が含まれていない
③セキュリティグループにて、対象リソースへのアクセスが許可されていない</b>

詳しく見ていきましょう。

<b>①Glue JOBから対象リソースへの経路が無い</b>
S3への接続時に起こりがちなエラーです。

S3はサブネットの外にあるリソースのため、<br>サブネット内で起動したGlue JOBからS3への経路が無い場合はエラーが返されます。

<img width="900
" alt="S3_接続.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/50039f62-0c42-bc45-7975-cbcdb74a77f1.png">
S3と通信を行うためには、<b>ゲートウェイ型VPCエンドポイント</b>を経由させる必要があります。
※NATゲートウェイ＆インターネットゲートウェイを経由してのアクセスも可能ですが、<br>ここでは割愛させて頂きます。

<img width="900
" alt="S3_接続_2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/0485dc98-6463-19c4-c1a3-0e790a70d3d5.png">
その上で、Glue JOBが起動しているサブネットのルーティングテーブルに
ゲートウェイ型VPCエンドポイントへの経路があるかを確認しましょう。<br>（通常は、ゲートウェイ型VPCエンドポイントを作成した時点で自動で経路が追加されます。）

図の通り、ゲートウェイ型VPCエンドポイントはVPCと紐づくものであるため、
まずは使用しているVPCにS3のゲートウェイ型VPCエンドポイントがアタッチされているかを確認しましょう。


また、RedshiftやRDS等のインスタンスへの接続を行う場合も、
ルートテーブルの確認を行い経路があるかを確認しましょう。

<b>②Glue JOBに設定しているIAMロールにリソースへのアクセス許可が含まれていない</b>

リソースへの接続は出来ても、Glue JOBに設定するIAMロールにS3バケットへのアクセスが
許可されていなければエラーが出てしまったり、
実行が出来てもデータが作成されない等の事象が起きてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/cf94e9a7-6935-1f04-2eb1-41cb26b806b8.png)

では、どのようなIAMポリシーが必要なのでしょうか？

基本的には、AWSマネージドポリシーである「AWSGlueServiceRole」を利用することで
Glue JOBを実行することが出来ます。

AWSGlueServiceRoleでは、Glue JOBを稼働させる際に必要とされる権限がデフォルトで入っています。

![glueservice.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/9b3bf6ee-1ca0-5349-1dfc-bc0a7f11092d.png)
中身はこんな感じです。
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "glue:*",
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:ListAllMyBuckets",
                "s3:GetBucketAcl",
                "ec2:DescribeVpcEndpoints",
                "ec2:DescribeRouteTables",
                "ec2:CreateNetworkInterface",
                "ec2:DeleteNetworkInterface",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcAttribute",
                "iam:ListRolePolicies",
                "iam:GetRole",
                "iam:GetRolePolicy",
                "cloudwatch:PutMetricData"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:CreateBucket"
            ],
            "Resource": [
                "arn:aws:s3:::aws-glue-*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::aws-glue-*/*",
                "arn:aws:s3:::*/*aws-glue-*/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::crawler-public*",
                "arn:aws:s3:::aws-glue-*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:*:*:/aws-glue/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateTags",
                "ec2:DeleteTags"
            ],
            "Condition": {
                "ForAllValues:StringEquals": {
                    "aws:TagKeys": [
                        "aws-glue-service-resource"
                    ]
                }
            },
            "Resource": [
                "arn:aws:ec2:*:*:network-interface/*",
                "arn:aws:ec2:*:*:security-group/*",
                "arn:aws:ec2:*:*:instance/*"
            ]
        }
    ]
}
```

しかし、このデフォルト権限では、S3に対してのアクションを"Resource"句にて
S3のバケットを<b>"aws-glue-*"等で絞り込んでしまっている</b>ため、
独自でバケットを作成している場合はそのバケットのオブジェクトに対しての
読み込み・書き込みを行うことは出来ません。<br>（JOBが実行出来てもデータが作られない等の問題が発生する。）

この場合、「AWSGlueServiceRole」のJSONの内容をコピーし、
S3バケットの部分のみを自分が作成したバケット名に置換したIAMポリシーを新規作成し、
Glue JOB実行用のIAMロールに含めることで解決することが出来ます。

<b>変更前</b>
```json
            "Resource": [
                "arn:aws:s3:::aws-glue-*/*",
                "arn:aws:s3:::*/*aws-glue-*/*"
            ]
```

<b>変更後</b>

```json
            "Resource": [
                "arn:aws:s3:::<自分のバケット名>/*",
                "arn:aws:s3:::*/*aws-glue-*/*"
            ]
```

S3へのアクセスが拒否される、S3のファイルが読み込めない、作成されない場合は、
こちらの設定を見直してみましょう。


<b>③セキュリティグループにて、対象リソースへのアクセスが許可されていない</b>
RedshiftやRDS等サブネット内で起動するインスタンスにはセキュリティグループというものが設定されています。
（インスタンス単位で付与するファイアウォールのようなもの。S3には付かない。）

そのセキュリティグループに、Glueからのアクセス許可が付与されていない場合は通信を行うことが出来ません。

<img width="900
" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/64bbc456-7641-36a4-0628-9bd0c094fd67.png">

Glueと接続先のDB(Redshift,RDS等)との通信を行う際、
Glue JOBと接続先DBに対して以下のようにセキュリティグループを設定する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/6b642888-9fd8-8bff-21f5-2c5834ae468d.png)

インバウンドルールでは、GlueからRedshiftへの任意のTCPポート0-65535の範囲内で
通信を受け付けることを許可します。
ただし、ソースを自身のセキュリティグループ"SG_Example"に絞り込むことで、
あくまで"SG_Example"と紐づいているものとしか通信させないようにしています。

この設定を行うことで、Redshiftからしてみれば、Glue JOBは、TCPポート0-65535を使用し、
かつ"SG_Example"が付与されているため、インバウンド通信は問題無く許可されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078298/010a6207-3c0d-88ac-8dc0-a72bd5cf47f5.png)

こちらの設定は、RedshiftであればRedshiftコンソール、GlueならばGlueコンソールのConnectionにて
確認することが出来ます。

AWS公式ドキュメントでは、この設定方式は「自己参照ルール」と呼ばれております。

引用元：
https://docs.aws.amazon.com/ja_jp/glue/latest/dg/setup-vpc-for-glue-access.html


<h3>２．DBユーザーの権限の問題</h3>
上記１の内容を全てクリアした上でJOBを実行してもなおエラーが出る場合があります。<br>
この場合、考えられる原因は、JOBで指定しているDBユーザーの対象テーブルへの権限が無い事によることがほとんどです。

JOBを実行した際、DBユーザーによる権限が起因として返されるエラーメッセージは以下のようなものです。<br>
```json
"An error occurred while calling o97.getDynamicFrame. Exception thrown in awaitResult:"
```

・・・ぱっと見、何が原因なのか分からないですよね(笑)
しかし、CloudWatchなどで詳しくエラーログの内容を見てみると、

```json
"permission denied for schema XXX"
```

 のような、いかにもDB起因のエラーが記録されていることがあります。

この場合、実行しているJOBで<br><br>

<b>
・どのDBユーザーで<br>
・どのような操作を<br>
・どのスキーマの<br>
・どのテーブルで<br>
</b><br>

を行っているのかを確認することが必要です。

どんな操作を行っているのかを確認できたところで、
クライアントツール等からそのユーザーでDBに実際にログインし、
実際にそのクエリを実行してみてエラーが返ってくるかを確認してみましょう。

ここでエラーが返ってくるようであれば、単純にDBユーザーのクエリの実行権限が<br>足りないということになるので、GRANT文などでそのDBユーザーに対して権限を付与しましょう。

<h1>３．まとめ</h1>
サーバレスということもあり、Glueではインフラ面を意識することはあまりないかもしれません。

しかし、JOBが動いている裏側で、どのような通信や部品が行われているのかを把握することにより、
よりエラーの解決スピードを上げることが出来るのではないでしょうか。