[参加レポート] 流しのエバンジェリストシリーズ AWSを中心として色んなサービスの基礎を学ぼう 「Amazon S3 Tables + Amazon Athena / Apache Iceberg」 #awsbasics

[参加レポート] 流しのエバンジェリストシリーズ AWSを中心として色んなサービスの基礎を学ぼう 「Amazon S3 Tables + Amazon Athena / Apache Iceberg」 #awsbasics

Clock Icon2025.04.13

コーヒーが好きな emi です。

最近データ分析分野を頑張っているのですが、実はいまだにしっかり S3 Tables を操作できていませんでした。
そんな折、2 年 4 ヶ月ぶりに以下のエバンジェリストシリーズで S3 Tables を扱っていただけるとのことで、参加しました。
https://awsbasics.connpass.com/event/347800/

座学を聞いてハンズオンを実施したのですが、ハンズオン部分でところどころつまづいたので復習も兼ねて実施したことを書いておきます。

座学の復習

座学パートの資料は以下で公開されています。

https://speakerdeck.com/simosako/apache-iceberg-to-amazon-s3-tables

後でコメントなど追記できたらと思いますが、特に印象的だったのは、テーブルの特定の行だけを簡単に操作できる仕組みです。

メンテナンスの重要性

大量のデータが含まれるテーブルから特定の 1 行を削除するのは通常困難です。特定の行を見つけるために大量の行をスキャンしなければならないからです。

しかし、Iceberg ではなぜこれが可能なのでしょうか?
実は、物理的にデータを削除しているわけではありません。代わりに 「この行を削除しました」 というメタデータを追加で書き込んでいるのです。

このように、Iceberg はデータの差分情報をメタデータとして管理しています。

ただし、このような更新操作を繰り返し行うと、メタデータのフラグメントが大量に発生します。そのため、コンパクションのようなメンテナンス作業が必要になります。S3 Tables では、このメンテナンス作業を自動的に裏側で処理してくれます。

また、すべての更新操作にはタイムスタンプが記録されるため、過去の任意の時点のデータスナップショットに簡単にアクセスできる 「タイムトラベルクエリ」 が実行可能になります。

ハンズオン

手順は以下となります。

https://zenn.dev/kameoncloud/articles/b7b58e0def2ec1

私が一部つまづいたり混乱した部分があるので、そこも含めて本ブログで改めて記載します。

構成図

図が無いと私がイメージしにくいので描いてみました。ほぼ このドキュメント の図を日本語で書き直したようなものなのですが、イメージいただけたら幸いです。

ポイントは、S3 Tables というサービスの中に以下 3 つのコンポーネントが存在する点です。

  • テーブルバケット
  • 名前空間(namespace)
  • テーブル

awsbasics-s3-tables-athena-apache-iceberg_31

テーブルバケットの中に名前空間、名前空間の中にテーブルがある、という階層構造になっています。
また、カタログは Glue Data Catalog として管理されていて、Glue とは上記図のように対応しています。

作成したテーブルは Athena から確認できます。

1. S3 Tables の作成

最初にテーブルバケットを作成します。S3 コンソールで 「テーブルバケット」 をクリックします。

以下のように 「AWS 分析サービスとの統合」 という表示があります。今回は S3 テーブルバケットに対して Athena からクエリするので、この統合が必要です。「統合を有効にする」 をクリックします。
awsbasics-s3-tables-athena-apache-iceberg_1

この統合を有効にすると、操作中のリージョンのテーブルバケットが s3tablescatalog というカタログの下にある Glue データカタログで自動的に使用できるようになると書かれています。統合の詳細は以下のドキュメントに記載があります。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/s3-tables-integrating-aws.html?icmpid=docs_amazons3_console

上記ドキュメントの図を以下に引用します。S3 Tables 内のデータが裏では Glue Data Catalog で自動管理されていて、そのおかげで Athena など他の AWS サービスからクエリできるということですね。
awsbasics-s3-tables-athena-apache-iceberg_27

私も理解が難しかったのですが、この後 Lake Formation コンソールで操作する手順がありまして、私たちは Lake Formation を使っているように感じますが、裏で作成されるリソースの実態は Glue なのだそうです。

「AWS Lake Formation テーブルバケットの登録」 の部分を読むと、Lake Formation 用の IAM ロールが作成されるようです。この IAM ロールによって、Lake Formation がアカウント内のすべてのテーブルバケットにアクセスし、このリージョン内のすべてのテーブルバケットを登録する権限を得ます。

awsbasics-s3-tables-athena-apache-iceberg_2

権限をクリックして見てみると以下のようになっていました。
awsbasics-s3-tables-athena-apache-iceberg_3

S3TablesRoleForLakeFormation
S3TablesRoleForLakeFormation
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LakeFormationPermissionsForS3ListTableBucket",
            "Effect": "Allow",
            "Action": [
                "s3tables:ListTableBuckets"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "LakeFormationDataAccessPermissionsForS3TableBucket",
            "Effect": "Allow",
            "Action": [
                "s3tables:CreateTableBucket",
                "s3tables:GetTableBucket",
                "s3tables:CreateNamespace",
                "s3tables:GetNamespace",
                "s3tables:ListNamespaces",
                "s3tables:DeleteNamespace",
                "s3tables:DeleteTableBucket",
                "s3tables:CreateTable",
                "s3tables:DeleteTable",
                "s3tables:GetTable",
                "s3tables:ListTables",
                "s3tables:RenameTable",
                "s3tables:UpdateTableMetadataLocation",
                "s3tables:GetTableMetadataLocation",
                "s3tables:GetTableData",
                "s3tables:PutTableData"
            ],
            "Resource": [
                "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/*"
            ]
        }
    ]
}

統合を有効にすると以下のように 「統合が正常に有効になりました。」 と表示されます。
では、テーブルバケットを作成していきます。
awsbasics-s3-tables-athena-apache-iceberg_4

ここではテーブルバケット名を指定するだけです。
awsbasics-s3-tables-athena-apache-iceberg_5

できました。テーブルバケット ARN をコピーしておきます。
awsbasics-s3-tables-athena-apache-iceberg_6

作成したテーブルバケットをクリックして見ると、まだ 「テーブル (0)」 となっており中身は空っぽです。

awsbasics-s3-tables-athena-apache-iceberg_28

2. テーブルと名前空間(namespace)の作成

このまま S3 Tables の画面から 「Athena でテーブルを作成」 をクリックし Athena でテーブルと名前空間(namespace)を作成することもできるのですが、ハンズオン手順に合わせて AWS CLI で作成します。CloudShell から CLI コマンドを実行します。

まずは create-namespace コマンドで名前空間(namespace)を作成します。

aws s3tables create-namespace --table-bucket-arn <s3 tables バケットarn> --namespace <名前空間の名前>

名前空間(namespace)の名前を 20250412-handson-ns としてコマンドを実行すると…

▼実行結果

[cloudshell-user@ip-10-132-81-204 ~]$ aws s3tables create-namespace --table-bucket-arn arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson --namespace 20250412-handson-ns

An error occurred (BadRequestException) when calling the CreateNamespace operation: The specified namespace name is not valid.
[cloudshell-user@ip-10-132-81-204 ~]$ 

おっと…エラーになってしまいました。The specified namespace name is not valid.(指定されたネームスペース名が有効ではありません。)ということで、名前空間(namespace)の命名規則に決まりがあるようです。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/s3-tables-buckets-naming.html

上記ドキュメントを見ると、ハイフンはダメでした。名前空間(namespace)の名前を handson_namespace として再度コマンドを実行します。

▼実行結果

[cloudshell-user@ip-10-132-81-204 ~]$ aws s3tables create-namespace --table-bucket-arn arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson --namespace "handson_namespace"
{
    "tableBucketARN": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson",
    "namespace": [
        "handson_namespace"
    ]
}
[cloudshell-user@ip-10-132-81-204 ~]$ 

今度は成功しました。

名前空間(namespace)ができたので、次は create-table コマンドでテーブルを作成します。
テーブルの作成と同時にスキーマも定義していきます。以下のブログも参照ください。

https://dev.classmethod.jp/articles/schema-definition-s3tables-createtable-with-awscli/

スキーマをコマンドの中で定義するのは大変なので、CLI スケルトンを使います。CLI スケルトンについては [AWS CLI] 長いパラメータの入力が不要になる CLI Skeleton が便利すぎる件 | DevelopersIO もご参照ください。

スキーマを定義するために、元になる JSON ファイルを作成していきます。vi コマンドで table.json というファイルを作成し開きます。

vi table.json

以下の JSON テキストで、<S3 tables バケットarn><先ほど作成した名前空間> を自分の環境に合わせて変更します。テーブル名は my_table としていますが、変えても OK です。

table.json
{
    "tableBucketARN": "<S3 tables バケットarn>",
    "namespace": "<先ほど作成した名前空間>",
    "name": "my_table",
    "format": "ICEBERG",
    "metadata": {
        "iceberg": {
            "schema": {
                "fields": [
                     {"name": "id", "type": "int","required": true},
                     {"name": "name", "type": "string"},
                     {"name": "value", "type": "int"}
                ]
            }
        }
    }
}

:set paste と入力して Enter キーを押すと、貼り付けモードになります。Enter キーを押すと、カーソルが上にきます。
i を押下して編集モードに移行します。i を押下すると画面下部に --INSERT (paste)-- と表示されます。
上記で編集した JSON を貼り付けると、インデント崩れせずに貼り付けられます。
esc キーを押下して編集モードを抜け、:wq と入力して Enter キーを押すと、保存して終了します。

こちら で似たような操作を行っているので、画面イメージも確認されたい方は参照ください。

cat で編集内容が保存できたか確認します。

[cloudshell-user@ip-10-132-81-204 ~]$ cat table.json 
{
    "tableBucketARN": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson",
    "namespace": "handson_namespace",
    "name": "my_table",
    "format": "ICEBERG",
    "metadata": {
        "iceberg": {
            "schema": {
                "fields": [
                     {"name": "id", "type": "int","required": true},
                     {"name": "name", "type": "string"},
                     {"name": "value", "type": "int"}
                ]
            }
        }
    }
}

[cloudshell-user@ip-10-132-81-204 ~]$ 

保存できていますね。
では、テーブルを作成します。

aws s3tables create-table --cli-input-json file://table.json

▼実行結果

[cloudshell-user@ip-10-132-81-204 ~]$ aws s3tables create-table --cli-input-json file://table.json
{
    "tableARN": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson/table/7e011d9d-94ef-49ca-a299-110ac1b365d6",
    "versionToken": "cc531d8d123530d30d09"
}
[cloudshell-user@ip-10-132-81-204 ~]$ 

S3 コンソールに戻って確認すると、テーブル my_table と名前空間 handson_namespace ができているのが分かります。

awsbasics-s3-tables-athena-apache-iceberg_29

3. Lake Formation の設定

さて、CLI で S3 テーブルの作成はできました。Athena コンソールからも S3 テーブルの作成ができるのでやってみます。

S3 コンソールで 「Athena でテーブルを作成」 をクリックしてみます。
awsbasics-s3-tables-athena-apache-iceberg_7

画面遷移すると、このように名前空間の指定を促されます。先ほど CLI で作成した名前空間を指定して 「Athena でテーブルを作成」 をクリックします。
awsbasics-s3-tables-athena-apache-iceberg_8

Athena の画面に遷移します。ここで、私は Athena を仕事でよく使うので、ワークグループやデータソースがデフォルトではないものになっていました。以下のように変更します。

  • ワークグループ:primary
  • データソース:AWSDataCatalog
  • カタログ:s3tables/<テーブルバケット名>
  • データベース:default

「S3 テーブルを作成」 というクエリが表示されているので、このタブを選択した状態で 「実行」 をクリックします。

すると、以下のようにエラーになりました。
awsbasics-s3-tables-athena-apache-iceberg_30

Iceberg cannot access the requested resource: Forbidden: Insufficient Lake Formation permission(s): Required Create Table on handson_namespace

Athena から S3 テーブルの作成をするには、Lake Formation 側から権限の設定が必要です。
ここから Lake Formation での操作は、Athena から S3 テーブルを作成するために必要な権限の設定です。先ほど CLI で作成済みのテーブルへの INSERT や SELECT は実行できたので、早くデータを入れたり SELECT したりしたい方は飛ばして 4. Athena から S3 テーブルへのクエリ発行 へ進んでください。

では、Lake Formation コンソールを開きます。Lake Formation の操作が初めての場合は以下のように権限を付与する旨のダイアログが出ます。

awsbasics-s3-tables-athena-apache-iceberg_11

ここでは Lake Formation コンソールへのフルアクセス権を持つ管理者の設定が必要です。

  • 「Add myself」(自分自身を追加):現在ログインしているユーザーを管理者として設定
  • 「Add other AWS users or roles」(他のAWSユーザーまたはロールを追加):別の IAM ユーザーやロールを管理者として設定

指定された管理者が Lake Formation を通じてデータレイクの管理を開始できるようになります。今回は今ログインしている IAM ロールにこのまま Lake Formation の操作権限を付与しますので、「Add myself」 にチェックして進めます。

以下のように画面が遷移しますので、左上のハンバーガーメニューをクリックしてナビゲーションペインを開きます。
awsbasics-s3-tables-athena-apache-iceberg_12

Data Permission を開きます。私は普段 Athena を仕事でよく使うので既に権限がたくさんありますね。
「Grant」 をクリックして権限を作成します。
awsbasics-s3-tables-athena-apache-iceberg_13

権限を設定していきます。現在ログインしている私の IAM ロールに権限を付与したいので、以下のように設定します。

  • Principals:IAM users and roles
  • IAM users and roles:<今ログインしている自分の IAM ロール>

awsbasics-s3-tables-athena-apache-iceberg_14

LF-Tags or catalog resources では 「Named Data Catalog Resources」 を選択し、Catalogs では先ほど作成した S3 テーブルバケット名が含まれたカタログを選択します。
awsbasics-s3-tables-athena-apache-iceberg_15

Databases では作成した名前空間(namespace)を選択します。
awsbasics-s3-tables-athena-apache-iceberg_16

Tables では CLI で作成したテーブルを選択します。
awsbasics-s3-tables-athena-apache-iceberg_17

Tables Permissions で Super を選択して 「Grant」 をクリックします。
awsbasics-s3-tables-athena-apache-iceberg_18

権限付与できました。これで、今ログインしている IAM ロールで Athena 経由のテーブル作成ができるようになりました。

awsbasics-s3-tables-athena-apache-iceberg_19

4. Athena から S3 テーブルへのクエリ発行

さて、Athena 画面に戻ります。Athena クエリエディタで以下を確認すると、JSON で設定したスキーマ通りにテーブル(my_table)が構成されているのが分かります。

  • ワークグループ:primary
  • データソース:AWSDataCatalog
  • カタログ:s3tables/<テーブルバケット名>
  • データベース:default

awsbasics-s3-tables-athena-apache-iceberg_20

以下のクエリでデータを INSERT します。

INSERT INTO handson_namespace.my_table
VALUES 
    (111, 'ABC', 100),
    (222, 'XYZ', 200);   

クエリエディタに上記クエリを入力して実行すると、成功するはずです。
awsbasics-s3-tables-athena-apache-iceberg_21

以下 SELECT クエリでテーブルの中身を確認します。

SELECT * FROM handson_namespace.my_table;

INSERT した 2 行が表示されました!
awsbasics-s3-tables-athena-apache-iceberg_22

更にもう 2 行 INSERT します。

INSERT INTO handson_namespace.my_table
VALUES 
    (333, 'DEF', 300),
    (444, 'UVW', 400);    

awsbasics-s3-tables-athena-apache-iceberg_23

以下 SELECT クエリでテーブルの中身を確認します。

SELECT * FROM handson_namespace.my_table;

INSERT した 4 行が表示されました!
awsbasics-s3-tables-athena-apache-iceberg_24

ここでクエリの履歴を確認します。「最近のクエリ」 タブで INSERT した時間を確認します。
ここで表示されているのは日本時間(JST)であることに注意してください。
awsbasics-s3-tables-athena-apache-iceberg_25

では、タイムトラベルクエリを試してみます。

    1. 最初の INSERT の時刻:2025-04-12T15:08:56.964+09:00 (UTC:2025-04-12 06:08:56.964)
    • INSERT INTO handson_namespace.my_table VALUES (111, 'ABC'...)
    1. 2 番目の INSERT の時刻:2025-04-12T15:46:18.713+09:00 (UTC:2025-04-12 06:46:18.713)
    • INSERT INTO handson_namespace.my_table VALUES (333, 'DEF'...)

最初の INSERT の後、2番目の INSERT の前のデータを取得するために、以下のタイムトラベルクエリを実行します。1 つめのクエリ(UTC:2025-04-12 06:08)と 2 つめのクエリ(UTC:2025-04-12 06:08)の間の時刻(2025-04-12 06:30:00 UTC)を指定しました。

SELECT * FROM handson_namespace.my_table FOR TIMESTAMP AS OF TIMESTAMP '2025-04-12 06:30:00 UTC'

以下のように、最初に INSERT した 2 行のみの結果が取得できました!

awsbasics-s3-tables-athena-apache-iceberg_26

5. 削除

Lake Formation で Grant を削除します。作成した Grant をチェックして 「Revoke」 をクリックしてください。

awsbasics-s3-tables-athena-apache-iceberg_32

AWS CLI で、テーブル、名前空間、テーブルバケットの順番に削除します。コンソールからは削除できません。
本ブログの冒頭で記載したように S3 Tables は階層構造になっているため、階層の一番下のテーブルから順番に削除していく必要があります。

delete-table コマンドでテーブルを削除します。

aws s3tables delete-table --table-bucket-arn <bucket arn> --namespace my_namespace --name my_table

▼実行結果

[cloudshell-user@ip-10-134-16-93 ~]$ aws s3tables delete-table --table-bucket-arn arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson --namespace handson_namespace --name my_table
[cloudshell-user@ip-10-134-16-93 ~]$ 

delete-namespace コマンドで名前空間を削除します。

aws s3tables delete-namespace --table-bucket-arn <bucket arn> --namespace my_namespace

▼実行結果

[cloudshell-user@ip-10-134-16-93 ~]$ aws s3tables delete-namespace --table-bucket-arn arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson --namespace handson_namespace
[cloudshell-user@ip-10-134-16-93 ~]$ 

delete-table-bucket コマンドでテーブルバケットを削除します。

aws s3tables delete-table-bucket --region ap-northeast-1 --table-bucket-arn <bucket arn>

▼実行結果

[cloudshell-user@ip-10-134-16-93 ~]$ aws s3tables delete-table-bucket --region ap-northeast-1 --table-bucket-arn arn:aws:s3tables:ap-northeast-1:123456789012:bucket/20250412-handson
[cloudshell-user@ip-10-134-16-93 ~]$ 

おわりに

S3 Tables の概要はドキュメントで確認していたのですが、ようやく自分で操作することで腹落ちしました。座学パートも分かりやすくて大変勉強になりました。

本記事への質問やご要望については画面下部のお問い合わせ「DevelopersIO について」からご連絡ください。記事に関してお問い合わせいただけます。

参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/s3-tables-getting-started.html

https://speakerdeck.com/shuyasawa/apache-icebergnojie-shuo-toawsdenoyun-yong

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.