この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部の若槻です。
AWS Glueは、データ変換処理(ETL)をサーバーレスで実装できるAWSサービスです。
今回は、AWS Glueのジョブ実行がjava.io.FileNotFoundException: No such file or directory
というエラーになる際の対処方法を確認しました。
事象
Glueデータカタログから取得したデータの変換を行う下記のようなスクリプトのGlueジョブを作成しました。
devices-etl.py
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
args = getResolvedOptions(sys.argv, ['JOB_NAME', 'GLUE_DATABASE_NAME', 'SRC_GLUE_TABLE_NAME', 'DEST_GLUE_TABLE_NAME'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)
datasource0 = glueContext.create_dynamic_frame.from_catalog(database = args['GLUE_DATABASE_NAME'], table_name = args['SRC_GLUE_TABLE_NAME'], transformation_ctx = "datasource0")
applymapping1 = ApplyMapping.apply(frame = datasource0, mappings = [("端末id", "string", "device_id", "string"), ("イベント日時", "bigint", "timestamp", "bigint"), ("状態", "string", "status", "string")], transformation_ctx = "applymapping1")
selectfields2 = SelectFields.apply(frame = applymapping1, paths = ["device_id", "timestamp", "status"], transformation_ctx = "selectfields2")
resolvechoice3 = ResolveChoice.apply(frame = selectfields2, choice = "MATCH_CATALOG", database = args['GLUE_DATABASE_NAME'], table_name = args['DEST_GLUE_TABLE_NAME'], transformation_ctx = "resolvechoice3")
datasink4 = glueContext.write_dynamic_frame.from_catalog(frame = resolvechoice3, database = args['GLUE_DATABASE_NAME'], table_name = args['DEST_GLUE_TABLE_NAME'], transformation_ctx = "datasink4")
job.commit()
しかし、上記ジョブを実行したところ次のようなエラーとなりました。
java.io.FileNotFoundException: No such file or directory 's3://devices-raw-data-XXXXXXXXXXXX-ap-northeast-1/utf8-data/f28f7767-0088-4bb7-b015-ce548fb350c8'
調査、解決
エラーはcreate_dynamic_frame.from_catalog()
によるデータソースのS3バケットからのデータ取得時に発生しているようです。しかし、取得したいデータのファイル名(f28f7767-0088-4bb7-b015-ce548fb350c8)までは取れているのにそんなファイルが無いってどういうこと?となり最初は原因がさっぱり分かりませんでした。
そして同様の事象を検索してみると以下の質問投稿がありました。
質問に対する回答として、権限の問題でGetObjectが足りていないのではないか?とコメントが付いていますね。
it was indeed a permission's issue. As it turns out, I forgot to add specific S3 permissions for the objects (GetObject) inside the bucket and not only to the bucket itself.
ジョブ実行ロールはCloudFormationで作成したので、リソースの定義を再確認してみるとビンゴでした。下記の通りデータソースのS3バケットのキーに対してs3:PutObject
権限しか割り当たっていませんでした。
template.yaml
//<Other Resources>
ExecuteDevicesGlueJobRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Principal:
Service:
- glue.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: execute-devices-glue-job-policy
PolicyDocument:
Version: 2012-10-17
Statement:
//<Other Statements>
-
Effect: Allow
Action:
- s3:PutObject
Resource:
- !Sub arn:aws:s3:::${DevicesRawDataBucket}/utf8-data/*
そこでs3:GetObject
の権限を付与する変更を行いました。
template.yaml
//<Other Resources>
ExecuteDevicesGlueJobRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Principal:
Service:
- glue.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: execute-devices-glue-job-policy
PolicyDocument:
Version: 2012-10-17
Statement:
//<Other Statements>
-
Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
Resource:
- !Sub arn:aws:s3:::${DevicesRawDataBucket}/utf8-data/*
するとジョブが正常に実行できるようになり解決できました。
おわりに
AWS Glueのジョブ実行が"java.io.FileNotFoundException: No such file or directory"というエラーになる際の対処方法を確認しました。
エラー内容から権限を確認しようという発想にすぐに至らなかったため調査が長期化しそうでしたが、先人の投稿をもとに解決できて良かったです。
以上