Glue StudioでAurora Serverless v1からデータを取得して結合するGlue Jobを作ってみた

2023.05.08

データアナリティクス事業本部の鈴木です。

Amazon AuroraのテーブルからAWS GlueのSparkジョブでデータを取得し結合する処理を作りたかったので、簡単な処理ですが試したのでご紹介します。

Auroraの検証環境を作ったことがなかったので、Aurora Serverless v1環境の準備からしてみました。

検証したいこと

Aurora Serverless v1のテーブルからGlue Jobでデータを取得して、S3バケットに保存するまでの環境構築とGlue Jobの作成がこの記事で検証したい内容です。

検証したい構成

Auroraからは2テーブルからデータを取得し、結合してS3バケットに保存してみました。

Auroraは、Amazon Aurora Serverlessをv1からv2にアップグレードしてみた | DevelopersIOどのAmazon Auroraを使う?に選び方の目安の記載があったので参考にしつつ、特にバージョン自体にはこだわりがなかったので、Aurora Serverless v1にしてみました。

環境構築

1. ネットワークの構築

以下のようなネットワーク構成を作成するはCloudFormationテンプレートを作成し、デプロイしました。

ネットワーク部分

エンドポイントは以下の2つを作成します。

# サービス 種類 用途
1 com.amazonaws.ap-northeast-1.s3 ゲートウェイ型 Glueジョブで処理した結果を保存する
2 com.amazonaws.ap-northeast-1.secretsmanager インターフェース型 Auroraのデータベースアクセス時にクレデンシャルを取得する

また、図中に表現していませんが、以下のセキュリティグループも作成します。

# 論理ID 説明 備考
1 GlueConnectionSecurityGroup Glue接続用のセキュリティグループ 今回はエンドポイントにもこのセキュリティグループを適用しました
2 AuroraSecurityGroup Aurora用のセキュリティグループ

テンプレートは以下です。

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  EnvironmentName:
    Type: String

  VPCCIDR:
    Type: String
    Default: 10.192.0.0/16

  PrivateSubnetCIDR1:
    Type: String
    Default: 10.192.0.0/24

  PrivateSubnetCIDR2:
    Type: String
    Default: 10.192.1.0/24

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-VPC

  # Private Subnetのネットワーク設定
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Ref PrivateSubnetCIDR1
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateSubnet1

  PrivateRouteTable1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateRouteTable1

  PrivateSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable1

  # Private Subnetのネットワーク設定
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Ref PrivateSubnetCIDR2
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateSubnet2

  PrivateRouteTable2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-PrivateRouteTable2

  PrivateSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable2

  # セキュリティグループ
  GlueConnectionSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: GlueConnectionSecurityGroup
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-GlueConnectionSecurityGroup

  SelfRefGlueConnectionSecurityGroupIgress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !GetAtt GlueConnectionSecurityGroup.GroupId
      IpProtocol: tcp
      FromPort: "0"
      ToPort: "65535"
      SourceSecurityGroupId: !GetAtt GlueConnectionSecurityGroup.GroupId

  AuroraSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: AuroraSecurityGroup
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-AuroraSecurityGroup
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId: !Ref GlueConnectionSecurityGroup


  # エンドポイントの設定
  EndpointS3:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      RouteTableIds:
        - !Ref PrivateRouteTable1
        - !Ref PrivateRouteTable2
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      VpcEndpointType: Gateway
      VpcId: !Ref VPC

  EndpointSecretManager:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub com.amazonaws.${AWS::Region}.secretsmanager
      VpcId: !Ref VPC
      PrivateDnsEnabled: True
      SubnetIds: 
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      SecurityGroupIds:
        - !Ref GlueConnectionSecurityGroup

Glue接続用のセキュリティグループには、自己参照ルールを作成しています。これはAWS Glue 用 JDBC データストアに接続するための VPC の設定 - AWS Glueに記載があります。CloudFormationでの自己参照ルールはAWS CloudformationでSecurity Groupの自己参照の設定を行う - YOMON8.NETを参考にしました。

テンプレートのVPC部分は[小ネタ]Session ManagerでアクセスできるEC2を1発で作成するCloudFormationテンプレート作ってみた | DevelopersIOを参考にしました。

2. Aurora Serverless v1 クラスターの準備

Amazon RDSのコンソールでデータベースからデータベースの作成を押しました。

データベース作成1

以下はデータベースの作成画面です。選択した箇所を説明します。

標準作成Aurora (PostgreSQL Compatible)を選び、利用可能なバージョンからAurora PostgreSQL (Compatible with PostgreSQL 11.16)としました。テンプレートは開発/テストとしました。

データベース作成2

インスタンスの設定でDBインスタンスクラスはサーバーレスを選びました。検証のためなので、容量の範囲は最小AUCは2、最大AUCは4としました。

データベース作成3

VPCおよびセキュリティグループは先に作成したものを選択しました。

データベース作成4

クエリエディタを使いたかったので、Data APIは有効化しました。

データベース作成5

ページ右下のデータベースの作成を押して利用可能となるのを待ちました。

3. データベースとテーブルの作成

Glueジョブからデータを取得できるよう、検証用のデータベースとユーザーおよびテーブルを作成しましたのでサンプルを紹介します。

アクションクエリからクエリエディタを起動しました。

クエリエディタの起動

データベースに接続しますポップアップが出るので、マスターユーザーでpostgresデータベースに接続しました。このときに画面の表示に従ってシークレットマネジャーにシークレットを作成しました。

マスターユーザーでの接続

クエリエディタで以下のSQL文を順に実行し、glue_userユーザーとglue_sample_databaseデータベースを作成し、ユーザーにデータベースへのアクセス権を付与しました。

-- パスワードの文字列は自分のものに変更してください。
CREATE USER glue_user WITH PASSWORD 'パスワードの文字列';
CREATE DATABASE glue_sample_database;
GRANT ALL PRIVILEGES ON DATABASE glue_sample_database TO glue_user;

ユーザーが作成できたら、glue_userユーザーでglue_sample_databaseデータベースに接続しました。このときに画面の表示に従ってシークレットマネジャーにシークレットを作成しました。

glue用ユーザーでの接続

テーブルを作成し、サンプルデータを入れておきました。

CREATE TABLE name (
  id INTEGER PRIMARY KEY,
  name VARCHAR(255) NOT NULL
);

CREATE TABLE price (
  id INTEGER PRIMARY KEY,
  price INTEGER NOT NULL
);

INSERT INTO name (id, name) VALUES
(1, 'Apple'),
(2, 'Orange'),
(3, 'Grape'),
(4, 'Pineapple'),
(5, 'Peach');

INSERT INTO price (id, price) VALUES
(1, 150),
(2, 120),
(3, 390),
(4, 400),
(5, 350);

以下のようにデータが格納されました。

nameテーブル

priceテーブル

4. Glue接続の作成

まずGlue接続の作成からです。AWS GlueのコンソールでData ConnectionsよりCreate connectionを選びました。

Glue接続の作成1

以下のように設定を入力しました。ポイントは以下の3点です。

  • JDBC URLは、AWS Glue 接続プロパティ - AWS GlueのPostgreSQLに沿うようにしました。
  • Credential typeSecretとして、先に作成したglue_userユーザーのシークレットを指定しました。
  • Network optionsは、先に作成したVPC・サブネット・セキュリティグループを指定しました。

Glue接続の作成2

5. Glue Job用サービスロールの作成

以下のテンプレートを使ってGlue Job用のサービスロールを作成しました。

※ もし使われる際は、ロール名はAWSGlueServiceRole-Studio-CM-nayutsにハードコードしてあるので適当に変更してください。

Auroraから取得して加工したデータをPUTするS3バケットおよびシークレットへのアクセス権を定義しました。Auroraのデータベース自体へのアクセスは、既に作成したGlue接続およびGlue用ユーザーにて制御しています。

AWSTemplateFormatVersion: "2010-09-09"
Description: Creating Glue Studio Execution Role for Aurora Data

Parameters:
  MartBucketName:
    Description: Backet Name for Data Mart.
    Type: String
  SecretARNforAuroraUser:
    Description: Secret ARN for Aurora User .
    Type: String

Resources:
  GlueStudioExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      # Role名はAWSGlueServiceRole-Studio-から始まる名前に変更してください。
      RoleName: AWSGlueServiceRole-Studio-CM-nayuts
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: glue.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: GlueStudioS3AccessPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: [
                  "s3:*"
                ]
                Resource: [
                  !Sub "arn:aws:s3:::${MartBucketName}",
                  !Sub "arn:aws:s3:::${MartBucketName}/*"
                ]
        - PolicyName: GlueStudioSecretManagerAccessPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: [
                  "secretsmanager:GetSecretValue"
                ]
                Resource: [
                  !Sub "${SecretARNforAuroraUser}"
                ]
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole

MartBucketNameに名前を入れるS3バケットは、今回はあらかじめ作ってあった検証用のS3バケットを使用しました。S3バケットに関しては作成自体は例えばマネジメントコンソールなどから簡単にできます。

Glueジョブの作成と実行

Glueジョブの作成

今回はGlue Studioでジョブを作成してみました。ノードをいくつか作っているので、ある程度の粒度に分けて記載します。

全体像

まず全体像ですが、以下のようなジョブを作成しました。

全体像

2テーブルのデータを取得して、片方のテーブルはJOINのため絡む名を変更しておきました。JOINした後、不要なカラムを落としてS3に保存しました。

テーブルからの読み出し

まずnameテーブルからの読み出し設定です。

Node propertiesタブではノード名を入力し、Node typePostgresとしました。

Data source properties - PostgreSQLタブにて、JDBC sourceJDBC connection detailsを選び、Connection nameで先ほど作成したGlue接続を指定しました。Table nameにてテーブルも指定しました。

nameテーブルからの読み出し

Output schemaタブではEditからSchemaを設定しました。

 Schema設定

priceテーブルからの読み出し設定も同様に行いました。

JOIN

nameテーブルはJOINのためRename fieldの変換でカラム名を変更しました。

カラム名の変更

Joinの変換で2テーブルを結合しました。

Join

結合キーは片方しか要らなかったので、不要なテーブル分は落としておきました。

不要なカラムの削除

不要なカラムの削除2

S3への保存

最後にS3への保存設定をしました。今回はCSV形式としました。

 S3への保存

Glueジョブの実行

エディタ右上の Saveボタンでジョブを保存し、同じ並びのRunボタンでジョブを実行しました。

以下のようにS3バケットの指定したパスにファイルが出力されました。

出力されたファイル

念の為Athenaからテーブルを作成して検索すると、期待通りデータが作成されていることが確認できました。

検索結果

最後に

今回はAmazon AuroraのテーブルからAWS GlueのSparkジョブでデータを取得し結合する処理を作成してみました。

Sparkジョブを作るだけだと比較的手軽な検証なのですが、VPCから全部作るとなるといろいろ知識が必要なので大変ですね。この記事では簡単にではありますが、VPCの作成からAurora Serverless v1クラスターの作成、Glue接続の設定なども盛り込んでみました。

参考になりましたら幸いです。