AWS Glue Studioにてお気軽にGUIでETL

記事タイトルとURLをコピーする

例のAWSデータレイクの本でお勉強がてら AWS Glueを開いていたら何やら「new!」としてAWS Glue Studioなる機能が追加されていたので実際に触ってみました。

aws.amazon.com

一言でいうと「AWS Glueの新しいビジュアルインタフェースで、利用者がAWS Glue ETL ジョブを簡単に作成、実行、および監視できるようになるもの」だそうです。

実際に簡単なCSVのサンプルデータを作ってどんな感じに利用出来るのか試してみたので自分へのメモを兼ねて残します。 今回は実際の現場でよくあるであろう複数テーブルのジョインや、不要カラムの削除、カラム名変更、フィルタリングをやってみました。

サンプル環境整備

今回用意したサンプルデータは以下2点のごく簡単なものです。 最終的にこのデータ2つに上で書いたようなETLをかけてデータ分析用途のファイルとして出力してみます。

サンプルデータ

% cat /tmp/one.csv <(echo -----) /tmp/two.csv
No,aaa,bbb,ccc,ddd
1,a1,b1,c1,d1
2,a2,b2,c2,d2
3,a3,b3,c3,d3
4,a4,b4,c4,d4
5,a5,b5,c5,d5
-----
No,eee,fff,ggg,hhh
1,e1,f1,g1,h1
2,e2,f2,g2,h2
3,e3,f3,g3,h3
4,e4,f4,g4,h4
5,e5,f5,g5,h5
%

まずは、こちらの2つのサンプルデータを 検証用S3bucket任意の名前(私の場合は ytamu-aws-glue-xxx)でを作成してぶち込みます。

% aws s3 mb s3://ytamu-aws-glue-src #インプット用(データレイクでいうRawデータ格納用)
% aws s3 mb s3://ytamu-aws-glue-dst #アウトプット用(データレイクでいうETL後のデータ格納用)
% aws s3 cp /tmp/one.csv s3://ytamu-aws-glue-src/
% aws s3 cp /tmp/two.csv s3://ytamu-aws-glue-src/

IAM-Roleとして、GlueServiceRole(名称はお好みで) を作成します。 信頼されたエンティティの種類は Glue(glue.amazonaws.com)とし、IAMポリシーとして以下2点をアタッチしておきます。(検証環境でなければ必要に応じて絞るべきですが)
・AmazonS3FullAccess
・AWSGlueServiceRole

AWS Glueでデータカタログ用のデータベースを作成します。

% aws glue create-database --database-input Name=ytamu-test-db,Description=hoge

続けてCrawlerを作成します。(nameとdatabaseの名称は適当に指定で)

% aws glue create-crawler --name ytamu-test-crawler --database ytamu-test-db --role GlueServiceRole --targets '{"S3Targets":[{"Path":"s3://ytamu-aws-glue-src/"}]}'

上で作成したCrawlerを実行しデータカタログを作ります。虫が苦手な方もAWS CLIでやれば大丈夫です。

% aws glue start-crawler --name ytamu-test-crawler

処理が終わる(RUNNING->Readyになる)まで待ちます。(45sec程度でした)

% aws glue get-crawler --name ytamu-test-crawler |grep State

あっという間に出来ましたね。Apache Hive Metastore互換のデータカタログが。<-すごい

中を覗いてみると、きちんとサンプルデータのone.csvの内容通りレコードの行数やらデリミタやらカラムやらデータ型やら平均レコードサイズなど様々な情報が取得されデータカタログが出来上がっているのがわかります。

(一部手でフィルタリングしてます)

% aws glue get-table --database-name ytamu-test-db --name one_csv
{
    "Table": {
        "Name": "one_csv",
        "DatabaseName": "ytamu-test-db",
        "Owner": "owner",
        "CreateTime": 1601994226.0,
        "UpdateTime": 1601994226.0,
        "LastAccessTime": 1601994226.0,
        "Retention": 0,
        "StorageDescriptor": {
            "Columns": [
                {
                    "Name": "no",
                    "Type": "bigint"
                },
                {
                    "Name": "aaa",
                    "Type": "string"
                },
                {
                    "Name": "bbb",
                    "Type": "string"
                },
                {
                    "Name": "ccc",
                    "Type": "string"
                },
                {
                    "Name": "ddd",
                    "Type": "string"
                }
            ],
            "Location": "s3://ytamu-aws-glue-src/one.csv",
            "InputFormat": "org.apache.hadoop.mapred.TextInputFormat",
            "OutputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat",
            "Compressed": false,
            "NumberOfBuckets": -1,
            "SerdeInfo": {
                "SerializationLibrary": "org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe",
                "Parameters": {
                    "field.delim": ","
                }
            },
            "BucketColumns": [],
            "SortColumns": [],
            "Parameters": {
                "CrawlerSchemaDeserializerVersion": "1.0",
                "CrawlerSchemaSerializerVersion": "1.0",
                "UPDATED_BY_CRAWLER": "ytamu-test-crawler",
                "areColumnsQuoted": "false",
                "averageRecordSize": "14",
                "classification": "csv",
                "columnsOrdered": "true",
                "compressionType": "none",
                "delimiter": ",",
                "recordCount": "6",
                "sizeKey": "89",
                "skip.header.line.count": "1",
                "typeOfData": "file"
            },
            "StoredAsSubDirectories": false
        },
        "PartitionKeys": [],
        "TableType": "EXTERNAL_TABLE",
        "Parameters": {
            "CrawlerSchemaDeserializerVersion": "1.0",
            "CrawlerSchemaSerializerVersion": "1.0",
            "UPDATED_BY_CRAWLER": "ytamu-test-crawler",
            "areColumnsQuoted": "false",
            "averageRecordSize": "14",
            "classification": "csv",
            "columnsOrdered": "true",
            "compressionType": "none",
            "delimiter": ",",
            "recordCount": "6",
            "sizeKey": "89",
            "skip.header.line.count": "1",
            "typeOfData": "file"
        },
        "CreatedBy": "arn:aws:sts::XXXXXXXXXXX:assumed-role/GlueServiceRole/AWS-Crawler",
        "IsRegisteredWithLakeFormation": false
    }
}
%

AWS Glue Studio

ここから本題の AWS Glue Studioのお話ですが、GUIがウリの機能なので画面キャプチャーを取るのが面倒ですがAWSマネジメントコンソールに画面を移して作業します。 AWS -> AWS Glue -> 左ペインの AWS Glue Studioを開きます。

f:id:swx-tamura:20201008103709p:plain

ジョブ作成画面に遷移したら、Create jobのラジオボタンをお好みで選択し [Create]を押下します。 今回は S3 -ETL-> S3がしたいので画面のように S3 -> S3を選択しました。

f:id:swx-tamura:20201008111056p:plain

上の画面内にも書いてある通り、ここはVisual画面を空の状態で作るかそれともソースとターゲットのノードを選択追加した状態で作るかを選ぶだけです。ソースは、S3、RDS、Kinesis、Kafka(のGlueのデータカタログ)が選べ、ターゲットの方は S3バケットかGlueのデータカタログが選べます。

仮に Blank graphを選択しても、空のVisual画面で始まるだけでその後数クリックでソースとターゲットを指定した内容と同じ状況は作れますし、ソースとターゲットの指定を間違えてても後に数クリックで変更可能です。 (という訳でここのラジオボタンの選択はあまり気にせずいきましょう)

S3 -> S3 を選択して[Create]した場合の初期のVisual画面はこんな感じです。 指定どおりSourceとTargetのノードが S3 bucketになっている事がわかります。 真ん中にはTransform のノードも空の状態で1つ作成してくれます。

f:id:swx-tamura:20201008112832p:plain

ここからGUIでぽちぽちして処理を作っていきます。 私の場合、マニュアルを読まずに触っちゃうタイプの人間なので操作感を掴むまで多少戸惑いましたが これから触る方は以下のポイントだけ事前に頭に入れておくとスムーズに操作が出来ると思います。

  1. ノードの追加
     水色の+マークでノードを追加できる
    ・何も選択していない状態で+を押下したらスタート地点のノードが追加される
    ・何かノードを選択した状態で+を押下したらそのノードの下に次の処理の空ノードが追加される
    ・既に子のノードが存在している親ノードを選択した状態で+を押下するとそこを起点とした2分岐にできる

  2. ノードの削除
    ノードを選択した状態で 赤いxマークを押下すると選択しているノードを削除できる

  3. ノードの移動
    ノードを選択した状態で右ペインの Node Properties にある Node parents を選択し切り替える事でそのノードがどのノードと線で繋がるか(連携するか)を変更できる

1つめの起点としてData Source(S3バケット) に one.csv のデータカタログを指定します。 画面の通り、Data Sourceのノードを選択し、右ペインの Data source properties - S3 からone_csvを選択。

f:id:swx-tamura:20201008114142p:plain

2つめの起点として、何もノードを選択していない状態で +マークを押下すると空ノードが作成されます。

f:id:swx-tamura:20201008114744p:plain

その空ノードが選択された状態で、Node Type のプルダウンから Data Sourceとして S3 を選択します。
(GUI力の高い方はこちらの画面を見た瞬間わかったかもしれませんが、仮に初期のラジオボタンの種類選択を誤ってもここの選択を切り替えるだけで解決します)

f:id:swx-tamura:20201008115926p:plain

同様にS3 にある two.csv のデータカタログを指定していきます。

f:id:swx-tamura:20201008120434p:plain

変換処理(Transform)

今回はサンプルデータ2つを起点とし、ETLのTの字にあたるTransformを4点やってみました。

  1. 複数テーブルのジョイン
  2. 不要なカラムの削除
  3. カラム名変更
  4. フィルタリング(カラム内の条件を満たすデータのみ出力)

これ以外の組み込み処理に興味がある場合は、以下オフィシャルサイトの情報を参照ください。 組み込み変換 - AWS Glue

やること1.複数テーブルのジョイン

分析のために、異なるデータを繋ぎ合わせるようなケースがあると思うので実施してみます。 今回S3バケット内に格納されている、2つのサンプルデータを1つに纏める為には、TransformのJoinを使います。

f:id:swx-tamura:20201008121236p:plain

Joinのように複数のデータソースが必要な場合は、選択をした時点で「ここは複数指定が必須ですよ」と警告の赤いエクスクラメーションマークが出ますので、追加したtwo.csvの方の S3のデータカタログにもチェックを入れて指定します。

すると、フローが以下のように複数のデータソース(S3バケット)から TransformのJoinのノードに線が繋がります。 わかりやすいですね。

f:id:swx-tamura:20201008121702p:plain

ここまでは、ただデータソースを指定しただけなので、これからJoinの処理の詳細を指定していきます。 Transformタブを押下し、Join Typeや Join Conditions を指定します。 今回は適当に Inner joinでお互いのnoカラムを指定しました。

f:id:swx-tamura:20201008192042p:plain

この時、Transformタブの隣にある Output schema タブを選択することで このTransform処理を加えることでどうなるのか?を確認することができます。

この先Transform処理を複数つなげていきますが、各処理のノード毎にこのような確認が可能なのでとても便利ですね。

f:id:swx-tamura:20201008205838p:plain

今回は図のように2テーブルをジョインした想定のカラムが表示されている事や、重複している no カラムの頭にドットを自動付与してくれている事がわかります。

続けて次のTransformの処理を作っていきます。 という事で、先程作ったJoinのノードを選択し、+ボタンでポチっとノードを追加すると以下画面のように分岐してしまいます。(下にノードが既にある場合にこの挙動は仕様です)

f:id:swx-tamura:20201008194530p:plain

今回は、間にTransformを順に追加していきたいイメージだったので、ちょっと違う・・・って気分になりますが こういう状態になった場合は、一番下にしたいノード(今回はデータターゲットのS3)を選択し、Node Propertiesタブ -> Node parents で今回の場合はぶら下げたい(親にしたい)ノードにチェックを入れます。

f:id:swx-tamura:20201008195328p:plain

そうすると今回思い描いていたフロー図になってホッとします。後は空ノードを好きな処理に変えていくといった具合です。

(GUI力の高い方はこちらの流れを見た瞬間、沢山の処理を作る際はターゲットを最後に作ったほうが良くない?というかBlank graphスタートでよくない?と思ったかもしれませんが、私もその通りだと思います)

f:id:swx-tamura:20201008200026p:plain

やること2.不要なカラムの削除

データをマージした後、例えば内容が重複していたり、値が入っていない or 入っててもゴミのような不要カラムがあった場合を想定し特定カラムを除外してみます。

今回は、Transformの DropFields を利用することで、「bbb」,「fff」,「.no」カラムをチェックして除外してみました。

f:id:swx-tamura:20201008200938p:plain

カラム名変更しつつdropチェックも出来るような ApplyMapping や 逆に出力したいカラムを選択する SelectFields というTransformを選択しても同様の処理は実現出来ます。

やること3.カラム名変更

分析用途によりカラム名を変更する場合があると思うのでソースのカラム名「hhh」を「iii」に変更してみます。

RenameField を使って Source path に「hhh」カラムを選択、Traget path はフリーで自分の好きな文字列を入力(今回はiii)としました。

f:id:swx-tamura:20201008201814p:plain

やること4.フィルタリング

対象データを一部をフィルタリングします。例えば日付がYYYYMMDD以降のみのデータとかに絞る場合等はよくあると思うので今回はNo.カラムの値が3以上のものに絞って出力するようにしました。

f:id:swx-tamura:20201008202515p:plain

出力先指定と実行

最後にアウトプット先の S3バケットの指定とフォーマットの指定をします。 今回はデータ解析系の検証っぽく Apache Parquet形式を指定してみました。

(ずっと画面でS3のノードに警告が出ていて気になっていた方=多分エンジニアがいたかもしれませんが、単純にS3バケットのURIを指定していなかったからというだけで、指定したら消えます)

f:id:swx-tamura:20201008203004p:plain

ここまでが今回の処理の全体の図となり、GUIのポチポチだけで作ってきましたが、 Scriptカラムを選択することで実際に走行するコード(今回は PySpark)を確認する事ができます。

f:id:swx-tamura:20201008203447p:plain

処理を実行する前に、ジョブの名前(アカウント内で一意のもの)や実行する際のIAMロールを 指定する必要があるので、Job details カラムを選択して指定します。

f:id:swx-tamura:20201008204224p:plain

処理を実行前に一度保存する必要があるので[Save]をしてから[Run]を押下し、実行します。

f:id:swx-tamura:20201008205131p:plain

もし、実行するにあたり問題(設定の不備/不足)がある場合は、 赤いエクスクラメーションマークで当該ノードの箇所に警告を出してくれるので適宜見直します。

AWS Glue Studioではモニタリングとして以下のような画面も追加されており、グラフィカルに処理の状況を把握可能です。

f:id:swx-tamura:20201008154026p:plain

また、Run detailsタブを押下することで過去のジョブの実行履歴やログも確認できます。 (各ログのリンクを押下すると CloudWatch Logs の当該ログ画面に遷移します)

f:id:swx-tamura:20201008213911p:plain

アウトプットの確認

処理が終わったら出力先に指定した S3バケットにデータが吐かれている事を確認します。(今回は1min程度でした)
出力形式でApache Parquetを指定すると自動的にsnappyで圧縮もしてくれるようです。

% aws s3 ls s3://ytamu-aws-glue-dst/
2020-10-07 18:58:15        719 part-00000-e506ff91-52b6-4eca-958c-e84c956bea23-c000.snappy.parquet
2020-10-07 18:58:17       1544 part-00051-e506ff91-52b6-4eca-958c-e84c956bea23-c000.snappy.parquet
2020-10-07 18:58:17       1544 part-00052-e506ff91-52b6-4eca-958c-e84c956bea23-c000.snappy.parquet
2020-10-07 18:58:15       1544 part-00053-e506ff91-52b6-4eca-958c-e84c956bea23-c000.snappy.parquet
%

対象ファイルをダウンロードして parquet-tools を利用し参照したところ 想定どおりのETLを経たアウトプットが得られている事が確認出来ました :tada:

一部データを参照すると以下の様な感じです。 (Amazon S3標準の S3 Selectを利用しても同様に Parquet形式を指定する事で参照可能です)

% parquet-tools cat part-00051-e506ff91-52b6-4eca-958c-e84c956bea23-c000.snappy.parquet
ccc = c3
aaa = a3
no = 3
ggg = g3
iii = h3
eee = e3
ddd = d3
%

本番データであれば、この後にAmazon Redshiftでロードしてクエリ投げて分析したり、Amazon QuickSightと連携しグラフで可視化するとかの用途で活用出来ると思います。

という訳で今回、AWS Glue Studioを少し触ってみました。 GUIでフロー図や各Transform処理単位でOutput Schema機能で各処理でどのような項目が出力されるか等を確認しながら処理を作り込めるのは便利で、利用にあたり敷居が下がった印象です。

まとめ

ちょっとだけAWS Glueを身近に感じました。