SQS を利用して Glue Crawler をイベントに基づいてクロールさせる方法

SQS を利用して Glue Crawler をイベントに基づいてクロールさせる方法

Takahiro Iwasa
Takahiro Iwasa
6 min read
ETL Glue

Glue クローラーは、 SQS を利用したイベント通知で S3 バケットをクロールできます。 これにより、フルスキャンを回避し、クロールのパフォーマンスおよびコストの両方でメリットがあります。

概要

AWS リソース作成

以下の内容で CloudFormation テンプレートを作成してください。

AWSTemplateFormatVersion: '2010-09-09'
Description: Glue crawler test

Resources:
  SqsQueue:
    Type: AWS::SQS::Queue
    Properties:
      SqsManagedSseEnabled: true
      QueueName: glue-crawler-test-queue

  SqsQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref SqsQueue
      PolicyDocument:
        Version: '2008-10-17'
        Id: __default_policy_ID
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                - !Sub arn:aws:iam::${AWS::AccountId}:root
                - !GetAtt IAMRoleGlueCrawler.Arn
            Action: sqs:*
            Resource: !GetAtt SqsQueue.Arn
          - Effect: Allow
            Principal:
              Service: s3.amazonaws.com
            Action: sqs:*
            Resource: !GetAtt SqsQueue.Arn

  S3Bucket:
    Type: AWS::S3::Bucket
    DependsOn: SqsQueuePolicy
    Properties:
      BucketName: !Sub glue-crawler-test-${AWS::AccountId}-${AWS::Region}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      NotificationConfiguration:
        QueueConfigurations:
          - Event: 's3:ObjectCreated:*'
            Queue: !GetAtt SqsQueue.Arn
      PublicAccessBlockConfiguration:
        BlockPublicAcls: TRUE
        BlockPublicPolicy: TRUE
        IgnorePublicAcls: TRUE
        RestrictPublicBuckets: TRUE

  IAMRoleGlueCrawler:
    Type: AWS::IAM::Role
    Properties:
      Path: /service-role/
      RoleName: !Sub glue-crawler-test-service-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: glue.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: cw-logs
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: "*"
        - PolicyName: glue
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - glue:CreateTable
                  - glue:GetDatabase
                  - glue:GetTable
                  - glue:UpdateTable
                Resource: "*"
        - PolicyName: s3
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:ListBucket
                  - s3:PutObject
                Resource: '*'

  GlueDatabase:
    Type: AWS::Glue::Database
    Properties:
      CatalogId: !Ref AWS::AccountId
      DatabaseInput:
        Name: glue-crawler-test-db

  GlueTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId: !Ref AWS::AccountId
      DatabaseName: !Ref GlueDatabase
      TableInput:
        Name: glue-crawler-test-table
        TableType: EXTERNAL_TABLE
        Parameters:
          classification: json
        StorageDescriptor:
          Location: !Sub 's3://${S3Bucket}/'
          Compressed: false
          InputFormat: org.apache.hadoop.mapred.TextInputFormat
          OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
          SerdeInfo:
            SerializationLibrary: org.openx.data.jsonserde.JsonSerDe

  GlueCrawler:
    Type: AWS::Glue::Crawler
    Properties:
      Name: glue-crawler-test
      Role: !Sub service-role/${IAMRoleGlueCrawler}
      Targets:
        CatalogTargets:
          - DatabaseName: !Ref GlueDatabase
            Tables:
              - !Ref GlueTable
      SchemaChangePolicy:
        UpdateBehavior: UPDATE_IN_DATABASE
        DeleteBehavior: LOG

<YOUR_CFN_BUCKET> を実際の値で置き換え、次のコマンドで CloudFormation スタックをデプロイしてください。

$ STACK_NAME=glue-crawler-test
$ aws cloudformation package \
  --template-file template.yaml \
  --s3-bucket <YOUR_CFN_BUCKET> \
  --s3-prefix "$STACK_NAME/$(date +%Y)/$(date +%m)/$(date +%d)/$(date +%H)/$(date +%M)" \
  --output-template-file package.template
$ aws cloudformation deploy \
  --stack-name $STACK_NAME \
  --template-file package.template \
  --capabilities CAPABILITY_NAMED_IAM

Glue Crawler 対象テーブル設定更新

現時点では CloudFormation は Glue Crawler の S3 イベント通知をサポートしていないため、対象のテーブル設定を手動で更新してください。

CloudFormation Support For S3 Event isn’t currently available. S3 Event Crawler’s integration with CloudFormation is in scope and in the works. We plan on releasing this coverage to Cloudformation some later this year. Thank you for patience.

テーブルを選択し Edit をクリックしてください。

Subsequent crawler runsCrawl based on events を選択してください。

テスト

JSON Version 1 で Glue テーブルスキーマを更新

以下のコマンドでサンプルの JSON をアップロードして、 S3 イベント通知をトリガーしてください。

$ echo '{"message": "Hello World"}' > sample1.json
$ aws s3 cp sample1.json s3://glue-crawler-test-<ACCOUNT_ID>-<REGION>/

Glue クローラーを起動します。

$ aws glue start-crawler --name glue-crawler-test

Glue クローラーのステータスが STOPPING になるまで確認してください。

$ aws glue get-crawler --name glue-crawler-test | jq -r '.Crawler.State'
STOPPING

Glue テーブルが更新され、新しいスキーマが表示されるはずです。

$ aws glue get-table \
  --database-name glue-crawler-test-db \
  --name glue-crawler-test-table \
| jq '.Table.StorageDescriptor.Columns'
[
  {
    "Name": "message",
    "Type": "string"
  }
]

JSON Version 2 で Glue テーブルスキーマを更新

以下のコマンドでサンプルの JSON をアップロードして、 S3 イベント通知をトリガーしてください。

$ echo '{"message": "Hello World", "statusCode": 200}' > sample2.json
$ aws s3 cp sample2.json s3://glue-crawler-test-<ACCOUNT_ID>-<REGION>/

Glue クローラーを起動します。

$ aws glue start-crawler --name glue-crawler-test

Glue クローラーのステータスが STOPPING になるまで確認してください。

$ aws glue get-crawler --name glue-crawler-test | jq -r '.Crawler.State'
STOPPING

Glue テーブルが更新され、新しい v2 スキーマが表示されるはずです。

$ aws glue get-table \
  --database-name glue-crawler-test-db \
  --name glue-crawler-test-table \
| jq '.Table.StorageDescriptor.Columns'
[
  {
    "Name": "message",
    "Type": "string"
  },
  {
    "Name": "statuscode",
    "Type": "int"
  }
]

SQS メッセージ数確認

キュー内のメッセージ数を確認してください。 ApproximateNumberOfMessages: 0 が表示されるはずです。

$ queue_url=$(aws sqs get-queue-url --queue-name glue-crawler-test-queue | jq -r '.QueueUrl')
$ aws sqs get-queue-attributes \
  --queue-url $queue_url \
  --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "0"
    }
}

クリーンアップ

以下のコマンドを使用して、プロビジョニングされた AWS リソースを削除してください。

$ aws s3 rm s3://glue-crawler-test-<ACCOUNT_ID>-<REGION>/ --recursive
$ aws cloudformation delete-stack --stack-name $STACK_NAME

まとめ

イベント通知で S3 バケットをクロールすることで、フルスキャンを回避でき、クロールのコスト削減とパフォーマンス向上につながります。

この投稿が、お役に立てば幸いです。

Takahiro Iwasa

Takahiro Iwasa

Software Developer at KAKEHASHI Inc.
Involved in the requirements definition, design, and development of cloud-native applications using AWS. Now, building a new prescription data collection platform at KAKEHASHI Inc. Japan AWS Top Engineers 2020-2023.