[AWS Glue]ジョブの実行が”java.io.FileNotFoundException: No such file or directory”というエラーになる時は実行ロールの権限を確認しよう

2021.01.03

この記事は公開されてから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"というエラーになる際の対処方法を確認しました。

エラー内容から権限を確認しようという発想にすぐに至らなかったため調査が長期化しそうでしたが、先人の投稿をもとに解決できて良かったです。

以上