DockerでローカルにIceberg + Spark環境を構築し、DBeaverで接続してみた
データ事業本部のueharaです。
今回は、DockerでローカルにIceberg + Spark環境を構築し、DBeaverで接続してみたいと思います。
はじめに
re:Invent 2024でフルマネージドなApache Iceberg tablesであるといえる S3 Tables が発表されるなど、大量のデータを効率よく扱うためのオープンソースのテーブルフォーマットであるIcebergがより勢いづいている印象を受けます。
AWS上ではIcebergを扱うためにS3 + Athenaという構成が取れたりするのですが、今回は手軽に試すためにローカル環境で構築してみたいと思います。
(簡単に)Icebergについて
Icebergはあくまで「Table Spec」の1つになります。
(引用元)
すなわち、「データへの効率的なアクセスをするためにこういう風にデータファイルを管理します」という枠組みであり、Icebergそのものがコンピューティング能力やストレージ機能を有しているといったものではありません。
実際にIcebergテーブルを扱うのはSparkやTrinoといったクエリエンジンになります。
補足ですが、Icebergのアーキテクチャは以下のようになっています。
環境構築
docker-compose.ymlの作成
公開されているIceberg + SparkのDockerイメージはいくつかありますが、今回は公式でもサンプルとして利用されているtabulario/spark-icebergのものを利用します。
公式サンプルではストレージとしてMinIO(S3互換のオブジェクトストレージ)を利用しているようで、これもそのまま使わせてもらいます。
作成した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
volumes:
- ./data:/data
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:
マウント用ディレクトリの作成
docker-compose.yml
の作成が完了したら、同じ階層にdata
, notebooks
, warehouse
というディレクトリを作成し、アクセス権限の設定をしておきます。
$ mkdir data notebooks warehouse
$ chmod 755 data notebooks warehouse
この段階で、ディレクトリ構成は以下のようになっていると思います。
.
├── data
├── docker-compose.yml
├── notebooks
└── warehouse
Dockerコンテナの起動とデータベースの作成
Dockerコンテナの起動は以下のコマンドで実行可能です。
$ docker-compose up
各サービスの立ち上げが完了したら、以下コマンドでSparkのセッションを開始します。
$ docker exec -it spark-iceberg spark-sql
セッションが開始されたら default
というデータベースを作成しておきます。
spark-sql ()> CREATE DATABASE IF NOT EXISTS default;
これで一旦準備は完了です。
DBeaverでアクセス
接続設定
DBeaverを開き、「新しい接続の作成」を行います。接続タイプは「Apache Hive 4+」を選択します。
ドライバのダウンロードがされていなければ以下の通り「ドライバの設定」の画面が立ちあがるので、ダウンロードしておきます。
データベースは先に作成した「default」を指定します。ユーザ名とパスワードは空欄で問題ありません。
左下の「テスト接続」ボタンを押して接続が成功すれば接続設定は完了です。
クエリの実行
DBeaverで接続ができたら、適当にテーブルを作成しデータを挿入してみます。
-- テーブルの作成
CREATE TABLE default.test_table
(
location_id STRING,
user_id INT,
name STRING
)
USING iceberg
PARTITIONED BY (location_id);
-- データの挿入
INSERT INTO default.test_table
VALUES ('aaa-111', 1, 'Alice'), ('bbb-222', 2, 'Bob');
パーティションを確認したいので、テーブル作成時にlocation_id
というカラムをパーティションに指定しています。
データの挿入が完了したら、SELECT
クエリでデータが取得できるか確認します。
SELECT *
FROM default.test_table
取得できました!
ファイルの確認
ここまで完了したら、MinIOにマウントしている data
フォルダを確認します。
$ tree data
data
└── warehouse
└── default
└── test_table
├── data
│ ├── location_id=aaa-111
│ │ └── 00000-9-e6eca642-bf54-4c83-ba93-a5737dcfe3fd-0-00001.parquet
│ │ └── xl.meta
│ └── location_id=bbb-222
│ └── 00000-9-e6eca642-bf54-4c83-ba93-a5737dcfe3fd-0-00002.parquet
│ └── xl.meta
└── metadata
├── 00000-d68c1a54-03f8-4caa-a26a-ed2e44c16a79.metadata.json
│ └── xl.meta
├── 00001-f38636ef-3f70-47ee-af49-cbef2c84cac4.metadata.json
│ └── xl.meta
├── 07de197b-736c-46b9-8e32-748d558cafb8-m0.avro
│ └── xl.meta
└── snap-6608030360858061675-1-07de197b-736c-46b9-8e32-748d558cafb8.avro
└── xl.meta
パーティションに設定した location_id
で区切られているデータファイル(.parquet
)や、各種メタデータファイルが確認できると思います。
MinIOの構造により .parquet
ファイルをローカルから直接参照することはできないので、以下のようにしてファイルをホスト側に持ってきて確認してみます。
# まずコンテナ内の一時ディレクトリにコピー
$ docker exec -it mc mc cp minio/warehouse/default/test_table/data/location_id=aaa-111/00000-9-e6eca642-bf54-4c83-ba93-a5737dcfe3fd-0-00001.parquet /tmp/
# そのファイルをホストにコピー
docker cp mc:/tmp/00000-9-e6eca642-bf54-4c83-ba93-a5737dcfe3fd-0-00001.parquet ./
コピーされたファイルを適当なParquetビューワーで確認すると、以下の通りデータが確認できます。
最後に
今回は、DockerでローカルにIceberg + Spark環境を構築し、DBeaverで接続してみました。
参考になりましたら幸いです。