ablog

不器用で落着きのない技術者のメモ

VPC 接続を使った Glue ジョブで VPC エンドポイントポリシーを設定すると "An error occurred (403) when calling the HeadObject operation: Forbidden"

Glue の「接続」を作ってジョブにアタッチすると、VPC エンドポイント経由で S3 にアクセスできる。VPC エンドポイントポリシーでアクセス可能な S3 バケットを絞りたい場合は、業務で使うバケットに加えて Glue ジョブが使うスクリプトディレクトリ(デフォルト: aws-glue-scripts-{AWS Account ID}-{region})、一時ディレクトリ(デフォルト: aws-glue-temporary-{AWS Account ID}-{region}) も許可する必要がある。

事象

  • エラーログ
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
Error downloading script ★: fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
  • ログ
--conf spark.hadoop.yarn.resourcemanager.connect.max-wait.ms=60000 
--conf spark.hadoop.fs.defaultFS=hdfs://172.17.2.84:8020 
--conf spark.hadoop.yarn.resourcemanager.address=172.17.2.84:8032 
--conf spark.dynamicAllocation.enabled=true 
--conf spark.shuffle.service.enabled=true 
--conf spark.dynamicAllocation.minExecutors=1
--conf spark.dynamicAllocation.maxExecutors=2 
--conf spark.executor.memory=5g 
--conf spark.executor.cores=4 
--conf spark.driver.memory=5g 
--JOB_ID j_d92fbdf94cb508d0ec9912ae6cf5d9aab616d829cc92da206f1a7f7849f3790e 
--JOB_RUN_ID jr_1d116a91943a03339a39f5e88a8ff16ac3a74a841f684f7547ab7999dcc44eb8 
--job-bookmark-option job-bookmark-disable 
--scriptLocation s3://aws-glue-scripts-123456789012-ap-northeast-1/admin/PrivateTest ★
--job-language python 
--TempDir s3://aws-glue-temporary-123456789012-ap-northeast-1/admin ★
--JOB_NAME PrivateTest
Detected region ap-northeast-1
Detected glue endpoint https://glue.ap-northeast-1.amazonaws.com
YARN_RM_DNS=172.17.2.84
JOB_NAME = PrivateTest
PYSPARK_VERSION 3 python3
Specifying ap-northeast-1 while copying script.
S3 copy with region specified failed. Falling back to not specifying region.

前提

  • 設定していた VPC エンドポイントポリシー
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::datalake-landing",
                "arn:aws:s3:::datalake-landing/*",
                "arn:aws:s3:::datalake-main",
                "arn:aws:s3:::datalake-main/*"
            ]
        }
    ]
}
  • 実行した Glue ジョブのコード(PySpark)
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init('test job')

df = spark.read.csv("s3://datalake-landing/test.csv")
df.coalesce(1).write.mode('overwrite').csv("s3://datalake-main/")

解決策

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "*",
            "Resource": [
                "arn:aws:s3:::glue-private-test",
                "arn:aws:s3:::glue-private-test/*",
                "arn:aws:s3:::log-raw-az",
                "arn:aws:s3:::log-raw-az/*"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-scripts-123456789012-ap-northeast-1/*"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1",
                "arn:aws:s3:::aws-glue-temporary-123456789012-ap-northeast-1/*"
            ]
        }
    ]
}
  • 上記は東京リージョンでデフォルトのケース。--scriptLocation や --TempDir を明示的に指定する場合はそのバケットの ARN を指定する。

参考

  • --scriptLocation - ETL スクリプトが s3://path/to/my/script.py のような形式で配置されている Amazon Simple Storage Service (Amazon S3) の場所。これは、JobCommand オブジェクトで設定されているスクリプトの場所を上書きします。
  • --TempDir - ジョブの一時ディレクトリとして使用できるバケットへの Amazon S3 パスを指定します。

たとえば、一時ディレクトリを設定するには、以下の引数を渡します。

'--TempDir': 's3-path-to-directory'
https://docs.aws.amazon.com/ja_jp/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html

現状、データソースとしてS3にプライベートアクセスを行いたいだけの場合であってもこのような接続設定を介す必要があります。
これがわかりにくかった。入力したら次に進みます。今回はデータベースへの接続は行わないのですが、JDBC URL/ユーザ名/パスワードが必須
になっているのでここではダミーの情報を入力しました。

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

VPC endpoints for Amazon S3 can alleviate these challenges. A VPC endpoint for Amazon S3 enables AWS Glue to use private IP addresses to access Amazon S3 with no exposure to the public internet. AWS Glue does not require public IP addresses, and you don't need an internet gateway, a NAT device, or a virtual private gateway in your VPC. You use endpoint policies to control access to Amazon S3. Traffic between your VPC and the AWS service does not leave the Amazon network.

(中略)

https://docs.aws.amazon.com/glue/latest/dg/images/PopulateCatalog-vpc-endpoint.png

https://docs.aws.amazon.com/glue/latest/dg/vpc-endpoints-s3.html