[アップデート] Amazon S3 Tables の名前空間とテーブルも AWS CDK L2 Construct で構築可能になりました

[アップデート] Amazon S3 Tables の名前空間とテーブルも AWS CDK L2 Construct で構築可能になりました

2025.08.07

こんにちは、製造ビジネステクノロジー部の若槻です。

Amazon S3 Tables は、S3 上で Apache Iceberg テーブルを簡単に管理できるフルマネージドサービスですが、最近の AWS CDK のアップデートにより、S3 Tables の テーブル(Table) および 名前空間(Namespace) リソースが L2 Construct(アルファ版)で構築できるようになりました。

https://github.com/aws/aws-cdk/releases/tag/v2.209.0

今まで L2 Construct をサポートしていたのはテーブルバケット(Table Bucket) のみでしたが、これで S3 Tables のすべてのリソースを L2 Construct で実装可能になりました。

試してみた

CDK パッケージのアップデート

AWS CDK モジュールとアルファモジュール(@aws-cdk/aws-s3tables-alpha)を v2.209.0 以上にアップデートします。

npm i -D @aws-cdk/aws-s3tables-alpha@latest aws-cdk-lib@latest aws-cdk@latest

CDK で S3 Tables を構築する

以下のコードは、S3 Tables のテーブルバケット、名前空間、およびテーブルを CDK で構築する例です。

lib/main-stack.ts
import * as cdk from "aws-cdk-lib";
import * as s3tables from "@aws-cdk/aws-s3tables-alpha";
import { Construct } from "constructs";

export class MainStack extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    /**
     * テーブルバケット
     */
    const tableBucket = new s3tables.TableBucket(this, "TableBucket", {
      tableBucketName: "table-bucket-1", // 指定必須
      removalPolicy: cdk.RemovalPolicy.DESTROY, // デフォルトは Retain
    });

    /**
     * テーブル名前空間
     */
    const namespace = new s3tables.Namespace(this, "Namespace", {
      namespaceName: "my_namespace_1", // 指定必須
      tableBucket,
    });

    /**
     * テーブル
     */
    new s3tables.Table(this, "Table", {
      tableName: "my_table_1", // 指定必須
      removalPolicy: cdk.RemovalPolicy.DESTROY, // デフォルトは Retain
      namespace,
      openTableFormat: s3tables.OpenTableFormat.ICEBERG, // 指定必須。現在選択可能なのは ICEBERG のみ。
      icebergMetadata: {
        icebergSchema: {
          schemaFieldList: [
            {
              name: "id",
              type: "int",
              required: true,
            },
            {
              name: "name",
              type: "string",
            },
          ],
        },
      },
      /**
       * 圧縮戦略の設定。既定では、テーブルのソート順に基づいて最適なコンパクション戦略が選択される。
       * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-maintenance.html#s3-tables-maintenance-compaction-strategies
       */
      compaction: {
        status: s3tables.Status.ENABLED,
        targetFileSizeMb: 128, // 指定必須。
      },
      /**
       * スナップショット管理の設定。デフォルトで有効。
       * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-maintenance.html#s3-tables-maintenance-snapshot-management
       */
      snapshotManagement: {
        status: s3tables.Status.ENABLED,
        maxSnapshotAgeHours: 48, // スナップショットの最大保持期間。 デフォルトは 120 時間(5 日)
        minSnapshotsToKeep: 5, // 保持する最小スナップショット数。デフォルトは 1
      },
    });
  }
}

上記をデプロイすると、CloudFormation で以下のようなリソースが作成されます。

作成されたテーブルバケット table-bucket-1 です。

作成されたテーブル my_table_1 です。名前空間 my_namespace_1 に属しています。

マネジメントコンソールから確認できる情報はここまでです。詳細な情報の確認や操作は相変わらず AWS CLI で行う必要があるんですね。

という訳で CLI でテーブルの情報を取得してみます。

$ aws s3tables get-table \
    --table-bucket-arn "arn:aws:s3tables:ap-northeast-1:${ACCOUNT_ID}:bucket/table-bucket-1" \
    --namespace "my_namespace_1" \
    --name "my_table_1"
{
    "name": "my_table_1",
    "type": "customer",
    "tableARN": "arn:aws:s3tables:ap-northeast-1:XXXXXXXXXXXX:bucket/table-bucket-1/table/171a89c4-777a-4dcb-b925-aef117030124",
    "namespace": [
        "my_namespace_1"
    ],
    "versionToken": "218f1265852efc6123ae",
    "metadataLocation": "s3://171a89c4-777a-4dcb-n6i8qfi7eepus8auwjqgnk97cxyqaapn1b--table-s3/metadata/00000-8c03be4d-345d-4589-bc34-b68135990c28.metadata.json",
    "warehouseLocation": "s3://171a89c4-777a-4dcb-n6i8qfi7eepus8auwjqgnk97cxyqaapn1b--table-s3",
    "createdAt": "2025-08-07T13:27:08.804433+00:00",
    "createdBy": "XXXXXXXXXXXX",
    "modifiedAt": "2025-08-07T13:29:36.541333+00:00",
    "ownerAccountId": "XXXXXXXXXXXX",
    "format": "ICEBERG"
}

メタデータは 171a89c4-777a-4dcb-n6i8qfi7eepus8auwjqgnk97cxyqaapn1b--table-s3 というシステムバケットに保存されています。ここからメタデータを取得してみます。

aws s3 cp "s3://171a89c4-777a-4dcb-n6i8qfi7eepus8auwjqgnk97cxyqaapn1b--table-s3/metadata/00000-8c03be4d-345d-4589-bc34-b68135990c28.metadata.json" ./metadata.json

メタデータの内容を確認してみます。CDK で指定したスキーマ設定が反映されていることがわかります。

$ cat metadata.json | jq .
{
  "format-version": 2,
  "table-uuid": "171a89c4-777a-4dcb-b925-aef117030124",
  "location": "s3://171a89c4-777a-4dcb-n6i8qfi7eepus8auwjqgnk97cxyqaapn1b--table-s3",
  "last-sequence-number": 0,
  "last-updated-ms": 1754576866280,
  "last-column-id": 2,
  "current-schema-id": 0,
  "schemas": [
    {
      "type": "struct",
      "schema-id": 0,
      "fields": [
        {
          "id": 1,
          "name": "id",
          "required": true,
          "type": "int"
        },
        {
          "id": 2,
          "name": "name",
          "required": false,
          "type": "string"
        }
      ]
    }
  ],
  "default-spec-id": 0,
  "partition-specs": [
    {
      "spec-id": 0,
      "fields": []
    }
  ],
  "last-partition-id": 999,
  "default-sort-order-id": 0,
  "sort-orders": [
    {
      "order-id": 0,
      "fields": []
    }
  ],
  "properties": {
    "write.parquet.compression-codec": "zstd"
  },
  "current-snapshot-id": -1,
  "refs": {},
  "snapshots": [],
  "statistics": [],
  "partition-statistics": [],
  "snapshot-log": [],
  "metadata-log": []
}

テーブルのメンテナンス設定を確認してみます。圧縮とスナップショットの管理が CDK で指定した通りに設定できています。

$ aws s3tables get-table-maintenance-configuration \
    --table-bucket-arn "arn:aws:s3tables:ap-northeast-1:${ACCOUNT_ID}:bucket/table-bucket-1" \
    --namespace "my_namespace_1" \
    --name "my_table_1"
{
    "tableARN": "arn:aws:s3tables:ap-northeast-1:XXXXXXXXXXXX:bucket/table-bucket-1/table/171a89c4-777a-4dcb-b925-aef117030124",
    "configuration": {
        "icebergCompaction": {
            "status": "enabled",
            "settings": {
                "icebergCompaction": {
                    "targetFileSizeMB": 128
                }
            }
        },
        "icebergSnapshotManagement": {
            "status": "enabled",
            "settings": {
                "icebergSnapshotManagement": {
                    "minSnapshotsToKeep": 1,
                    "maxSnapshotAgeHours": 120
                }
            }
        }
    }
}

CDK デプロイ時に遭遇したエラー

UnscopedValidationError エラー

名前空間を my-namespace-1 という名前で作成しようとしました。

new s3tables.Namespace(this, "Namespace", {
  namespaceName: "my-namespace-1",
  tableBucket,
});

すると、以下のようなエラーが発生しました。英数字以外はアンダースコア(_) のみ使用可能なようです。

/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/@aws-cdk/aws-s3tables-alpha/lib/namespace.ts:134
      throw new UnscopedValidationError(
            ^
UnscopedValidationError: Invalid S3 Tables namespace name (value: my-namespace-1)
Namespace name must only contain lowercase characters, numbers, and underscores (_) (offset: 2)
    at path [undefined]

テーブルを作成する際にも同様のエラーが発生しました。my-table-1 という名前で作成しようとしました。

new s3tables.Table(this, "Table", {
  tableName: "my-table-1",
  namespace,
  openTableFormat: s3tables.OpenTableFormat.ICEBERG,
});

こちらも同様に、英数字以外はアンダースコア(_) のみ使用可能なようです。

/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/node_modules/@aws-cdk/aws-s3tables-alpha/lib/table.ts:326
      throw new UnscopedValidationError(
            ^
UnscopedValidationError: Invalid S3 table name (value: my-table-1)
Table name must only contain lowercase characters, numbers, and underscores (_) (offset: 2)
    at path [undefined]

InvalidRequest エラー

テーブルを作成する際に、IcebergMetadata を指定しないでデプロイしようとしました。

new s3tables.Table(this, "Table", {
  tableName: "my_table_1", // テーブル名の指定必須
  namespace,
  openTableFormat: s3tables.OpenTableFormat.ICEBERG, // OpenTableFormat の指定必須。現在選択可能なのは ICEBERG のみ。
});

すると、以下のようなエラーが発生しました。IcebergMetadata または WithoutMetadata のいずれかを指定する必要があるようです。

Resource handler returned message: "Invalid request provided: Either IcebergMetadata or WithoutMetadata must be specified" (RequestToken: 3b8a431b-d9ef-3076-0194-9751a75
8fb13, HandlerErrorCode: InvalidRequest)

AlreadyExists エラー

テーブルバケットをデプロイしようとしました。

const tableBucket = new s3tables.TableBucket(this, "TableBucket", {
  tableBucketName: "my-table-bucket-1",
});

同名のテーブルバケットが存在しているという理由で、デプロイ時に AlreadyExists エラーが発生しました。

11:48:51 PM | CREATE_FAILED        | AWS::S3Tables::TableBucket | TableBucket/TableBucket
Resource handler returned message: "The bucket that you tried to create already exists, and you own it. (Service: S3Tables, Status Code: 409, Request ID: 4614ca28-a2b8-4
31a-90ba-92faa163efaf) (SDK Attempt Count: 1)" (RequestToken: 00d6f3c1-48ed-6a7f-a7cb-8ad966f5e28b, HandlerErrorCode: AlreadyExists)

AWS CLI で該当のテーブルバケットを削除します。

aws s3tables delete-table-bucket \
    --table-bucket-arn "arn:aws:s3tables:ap-northeast-1:${accountId}:bucket/my-table-bucket-1" \
    --region ap-northeast-1

これで OK かと思いきや、テーブルバケット削除直後に再度デプロイをしようとすると、また AlreadyExists エラーが発生しました。

11:55:43 PM | CREATE_FAILED        | AWS::S3Tables::TableBucket | TableBucket/TableBucket
Resource handler returned message: "The bucket is in a transitional state because of a previous deletion attempt. Try again later. (Service: S3Tables, Status Code: 409,
Request ID: bbad8cd1-d28c-4bbf-aebb-f822737506aa) (SDK Attempt Count: 1)" (RequestToken: 35fe9290-4458-8019-27b4-2f95a3921494, HandlerErrorCode: AlreadyExists)

今度は The bucket is in a transitional state because of a previous deletion attempt. とあり、削除完了の状態になるるまで時間がかかるようでした。マネジメントコンソールからだと消えているように見えるんですけどね。仕方がないので別の名前のテーブルバケットを作成しました。

おわりに

Amazon S3 Tables の名前空間とテーブルも AWS CDK L2 Construct で構築可能になったので試してみました。

これで S3 Tables の実装のインフラのコード化がより捗りますね!S3 Tables の採用を検討している方は、ぜひ試してみてください。

以上

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.