Apache Icebergのクイックスタートやってみた

Apache Icebergの公式ドキュメントにて、クイックスタートを発見！早速やってみました。

クイックスタートはSparkを使うものとHiveを使うものが用意されています。

https://iceberg.apache.org/spark-quickstart/

https://iceberg.apache.org/hive-quickstart/

今回は、Spark版を試しました。

## 環境構築

環境は、Docker Composeで構築します。

ドキュメントに記載のある`docker-compose.yml`を作成します

<details><summary>docker-compose.yml</summary>

```yaml:docker-compose.yml
version: "3"

services:
  spark-iceberg:
    image: tabulario/spark-iceberg
    container_name: spark-iceberg
    build: spark/
    networks:
      iceberg_net:
    depends_on:
      - rest
      - minio
    volumes:
      - ./warehouse:/home/iceberg/warehouse
      - ./notebooks:/home/iceberg/notebooks/notebooks
    environment:
      - AWS_ACCESS_KEY_ID=admin
      - AWS_SECRET_ACCESS_KEY=password
      - AWS_REGION=us-east-1
    ports:
      - 8888:8888
      - 8080:8080
      - 10000:10000
      - 10001:10001
  rest:
    image: apache/iceberg-rest-fixture
    container_name: iceberg-rest
    networks:
      iceberg_net:
    ports:
      - 8181:8181
    environment:
      - AWS_ACCESS_KEY_ID=admin
      - AWS_SECRET_ACCESS_KEY=password
      - AWS_REGION=us-east-1
      - CATALOG_WAREHOUSE=s3://warehouse/
      - CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO
      - CATALOG_S3_ENDPOINT=http://minio:9000
  minio:
    image: minio/minio
    container_name: minio
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=password
      - MINIO_DOMAIN=minio
    networks:
      iceberg_net:
        aliases:
          - warehouse.minio
    ports:
      - 9001:9001
      - 9000:9000
    command: ["server", "/data", "--console-address", ":9001"]
  mc:
    depends_on:
      - minio
    image: minio/mc
    container_name: mc
    networks:
      iceberg_net:
    environment:
      - AWS_ACCESS_KEY_ID=admin
      - AWS_SECRET_ACCESS_KEY=password
      - AWS_REGION=us-east-1
    entrypoint: >
      /bin/sh -c "
      until (/usr/bin/mc config host add minio http://minio:9000 admin password) do echo '...waiting...' && sleep 1; done;
      /usr/bin/mc rm -r --force minio/warehouse;
      /usr/bin/mc mb minio/warehouse;
      /usr/bin/mc policy set public minio/warehouse;
      tail -f /dev/null
      "
networks:
  iceberg_net:
```

</details>

:::note
コンテナが３つ定義されています。

- tabulario/spark-iceberg : Sparkがこれ？
- apache/iceberg-rest-fixture : Icebergがこれ？
- minio/minio : S3互換オブジェクトストレージのはず
:::

起動します。

```shell
docker compose up
```

これで完了です。

## アクセスしてみる

`docker-compose.yml`に、PORTの設定があったのでまずは片っ端からアクセスしてみます。

- spark-icebergコンテナ
    - ノートブック環境（8888）
        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/5a8178bd-6753-97ac-890a-e89e13c5aaeb.png)
    - Spark管理画面（8080）
        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d4fc1588-862b-6555-8857-3357d01ea1e4.png)
- minioコンテナ
    - Minio環境（9001）
        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/afcbfefa-3518-aaaa-ec8b-e3fbf052de96.png)
        `warehouse`という空のバケットが作成されています。（mcコンテナが起動時に作成しています）
        ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d6641fa7-989d-14c0-94f7-6e23c2b6f3c2.png)

## クイックスタートの手順を実行

ノートブックでクイックスタートの手順をやってみましょう。

:::note
SparkSQL、Spark-Shell、PySparkの3通り方法が記載されていますが、今回はSparkSQLを使用しました。
:::


1. Creating a table

    ```sql
    CREATE TABLE demo.nyc.taxis
    (
      vendor_id bigint,
      trip_id bigint,
      trip_distance float,
      fare_amount double,
      store_and_fwd_flag string
    )
    PARTITIONED BY (vendor_id);
    ```

    セルの先頭に`%%sql`と書くとSQLと認識するようです。

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8d1f828d-f41f-be00-964e-8e32792582a9.png)

    テーブル作成後、Minioの「warehouse/nyc/taxis/metadata」にファイルが作成されました。

    ![localhost_9001_ (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/aaf07606-cb7d-1721-4b82-244e058a6846.png)

    <details><summary>metadataファイル</summary>
    
    ```json:00000-7b48b19d-7693-43ed-8c0a-ed453826ce32.metadata.json
    {
      "format-version" : 2,
      "table-uuid" : "b077f2e1-7ba7-4c84-99d9-d91cac34fa72",
      "location" : "s3://warehouse/nyc/taxis",
      "last-sequence-number" : 0,
      "last-updated-ms" : 1736166858036,
      "last-column-id" : 5,
      "current-schema-id" : 0,
      "schemas" : [ {
        "type" : "struct",
        "schema-id" : 0,
        "fields" : [ {
          "id" : 1,
          "name" : "vendor_id",
          "required" : false,
          "type" : "long"
        }, {
          "id" : 2,
          "name" : "trip_id",
          "required" : false,
          "type" : "long"
        }, {
          "id" : 3,
          "name" : "trip_distance",
          "required" : false,
          "type" : "float"
        }, {
          "id" : 4,
          "name" : "fare_amount",
          "required" : false,
          "type" : "double"
        }, {
          "id" : 5,
          "name" : "store_and_fwd_flag",
          "required" : false,
          "type" : "string"
        } ]
      } ],
      "default-spec-id" : 0,
      "partition-specs" : [ {
        "spec-id" : 0,
        "fields" : [ {
          "name" : "vendor_id",
          "transform" : "identity",
          "source-id" : 1,
          "field-id" : 1000
        } ]
      } ],
      "last-partition-id" : 1000,
      "default-sort-order-id" : 0,
      "sort-orders" : [ {
        "order-id" : 0,
        "fields" : [ ]
      } ],
      "properties" : {
        "owner" : "root",
        "write.parquet.compression-codec" : "zstd"
      },
      "current-snapshot-id" : null,
      "refs" : { },
      "snapshots" : [ ],
      "statistics" : [ ],
      "partition-statistics" : [ ],
      "snapshot-log" : [ ],
      "metadata-log" : [ ]
    }
    ```
    
    </details>

1. Writing Data to a Table

    ```sql
    INSERT INTO demo.nyc.taxis
    VALUES (1, 1000371, 1.8, 15.32, 'N'), (2, 1000372, 2.5, 22.15, 'N'), (2, 1000373, 0.9, 9.01, 'N'), (1, 1000374, 8.4, 42.13, 'Y');
    ```

    Minio上にファイルが作成されます。

    メタデータ
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/bf53d07b-5bf7-f874-af93-083462168e7c.png)

    データ

    自動的にパーティションが作成される

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b9ddb722-81a9-b602-1e8d-a24f05570111.png)

    データはParquet形式で作成される

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/a2f2d63a-9504-71ff-d8f9-fb008574d3ec.png)

1. Reading Data from a Table

    ```sql
    SELECT * FROM demo.nyc.taxis;
    ```

    登録したデータが取得できました
    
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b1d2a9c7-d811-48af-04df-340adde978f3.png)

以上、触りだけですがクイックスタートをやってみました。

## サンプルノートブックがたくさんあります

- Iceberg - An Introduction to the Iceberg Java API
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8b68929b-b057-0a81-b29a-06d581457830.png)
- Iceberg - Berlin Buzzwords 2023
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/2b68a69f-0d0b-4643-c7c9-8aff6d23eb16.png)
- Iceberg - Getting Started
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/955c0211-c290-3ad0-242d-b35687f501cf.png)
- Iceberg - Integrated Audits Demo
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/99b4c09d-5b7b-8ac0-de82-d5fc9ebd831b.png)
- Iceberg - Table Maintenance Spark Procedures
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/c4c2588d-d9fb-fdf6-b38a-9ba72f0d2cb5.png)
- Iceberg - View Support
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/80201e56-1dd6-220b-ac96-e59f1e2008bf.png)
- Iceberg - Write-Audit-Publish (WAP) with Branches
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/84a07e92-4a6b-763f-ff1c-545132c9cac6.png)
- PyIceberg - Getting Started
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/dcf43fdd-447e-6a75-7592-49fc0ea968d9.png)
- PyIceberg - Write support
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/3e3a60ca-68b2-1832-7f0d-7ada072d4aaf.png)