🦆
オープンソースの S3 互換オブジェクトストレージ MinIO を DuckDB で使う
前提
- DuckDB が使える環境
- Docker Compose が使える環境
まとめ
DuckDB から S3 互換オブジェクトストレージを利用する際、MinIO を使う事で、ローカルに気軽に S3 互換オブジェクトストレージ環境を用意できる。
DuckDB とは
こちらをどうぞ。
DuckDB雑紹介(1.1対応版)@DuckDB座談会 - Speaker Deck
MinIO とは
MinIO はオープンソースの S3 互換オブジェクトストレージです。
DuckDB は S3 API 機能を持っており、S3 互換オブジェクトストレージとの相性がとても良いです。
ローカルで試したい時などに Docker Compose で MinIO を用意して利用すると、とても便利です。
Docker Compose
- ローカルということでセキュリティは一切考慮していません
- MinIO のイメージを pull して起動します
-
~/minio/data
にデータを保存する設定にしています - 起動時にヘルスチェックをして、起動が完了するまで待ちます
- MinIO にアクセスするための mc コマンドを使って、ユーザーを追加し、バケットを作成しています
- minioadmin はデフォルト値です
以下を compose.yaml ファイルとして保存してください。
services:
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
# environment:
# MINIO_ROOT_USER: ROOT_USER
# MINIO_ROOT_PASSWORD: ROOT_PASSWORD
volumes:
- ~/minio/data:/data
command: server /data --console-address ":9001"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
init-minio:
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
/usr/bin/mc alias set myminio http://minio:9000 minioadmin minioadmin;
/usr/bin/mc admin user add myminio duckdb-local duckdb-local;
/usr/bin/mc admin policy attach myminio readwrite --user=duckdb-local;
if /usr/bin/mc ls myminio/duckdb-local > /dev/null 2>&1; then
/usr/bin/mc rm -r --force myminio/duckdb-local;
fi;
if ! /usr/bin/mc ls myminio/duckdb-local > /dev/null 2>&1; then
/usr/bin/mc mb myminio/duckdb-local;
fi;
exit 0;"
使い方
$ docker compose up
[+] Running 3/3
✔ Network duckdb-minio_default Created 0.0s
✔ Container duckdb-minio-minio-1 Created 0.0s
✔ Container duckdb-minio-init-minio-1 Created 0.0s
Attaching to init-minio-1, minio-1
minio-1 | MinIO Object Storage Server
minio-1 | Copyright: 2015-2024 MinIO, Inc.
minio-1 | License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
minio-1 | Version: RELEASE.2024-10-29T16-01-48Z (go1.22.8 linux/arm64)
minio-1 |
minio-1 | API: http://172.18.0.2:9000 http://127.0.0.1:9000
minio-1 | WebUI: http://172.18.0.2:9001 http://127.0.0.1:9001
minio-1 |
minio-1 | Docs: https://docs.min.io
minio-1 | WARN: Detected default credentials 'minioadmin:minioadmin', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables
init-minio-1 | Added `myminio` successfully.
init-minio-1 | Added user `duckdb-local` successfully.
init-minio-1 | Attached Policies: [readwrite]
init-minio-1 | To User: duckdb-local
init-minio-1 exited with code 0
DuckDB で MinIO を利用する準備
DuckDB で MinIO をローカルで利用するためには、Secret Manager 機能を利用します。
- TYPE は S3 を指定します
- KEY_ID と SECRET は compose.yaml ファイルで指定した値を指定します
- ENDPOINT はローカルの値を指定します
- USE_SSL は false を指定します
- URL_STYLE は path を指定します
$ duckdb
v1.1.3 19864453f7
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D CREATE SECRET minio (
TYPE S3,
KEY_ID 'duckdb-local',
SECRET 'duckdb-local',
ENDPOINT '127.0.0.1:9000',
USE_SSL false,
URL_STYLE 'path'
);
これで DuckDB から MinIO を利用する準備が整いました。
MinIO に保存する
https://github.com/voluntas/duckdb-wasm-parquet こちらで公開している Parquet ファイルを DuckDB に読み込ませて、何もせずに MinIO に保存してみます。
D COPY (SELECT * FROM parquet_scan('https://duckdb-wasm.shiguredo.jp/P78BHZM3MD3MV47JDZG47PB8PW.parquet')) TO "s3://duckdb-local/spam.parquet" (FORMAT parquet, COMPRESSION zstd);
これで、 MinIO に Parquet ファイルが保存されました。
MinIO を確認する
http://127.0.0.1:9001 にアクセスして、 minioadmin / minioadmin でログインしてください。
spam.parquet が保存されていれば成功です。
MinIO から取得してみる
parquet_scan に MinIO の URL を指定して読み込んでみます。
D SELECT * FROM parquet_scan("s3://duckdb-local/spam.parquet");
┌──────────────────────┬──────────────────────┬─────────────────┬───┬──────────────────────┬───────────────────┬─────────────────────┐
│ connection_id │ id │ label │ … │ rtc_id │ rtc_timestamp │ rtc_type │
│ varchar │ varchar │ varchar │ │ varchar │ double │ varchar │
├──────────────────────┼──────────────────────┼─────────────────┼───┼──────────────────────┼───────────────────┼─────────────────────┤
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ AP │ 1726394702721.481 │ media-playout │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CF9A:6C:31:80:50:F… │ 1726394702721.481 │ certificate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CFAF:51:8E:09:37:2… │ 1726394702721.481 │ certificate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ COTdata1_109_maxpl… │ 1726394702721.481 │ codec │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ COTdata1_120_profi… │ 1726394702721.481 │ codec │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CP/NH8cQqe_eL6zYNON │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CP08b8N49i_eL6zYNON │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CPBDwwCTaU_eL6zYNON │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CPYLu975lS_eL6zYNON │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CPki/8xp1m_eL6zYNON │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ CPki/8xp1m_eYTJlek6 │ 1726394702721.481 │ candidate-pair │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ D5 │ 1726394702721.481 │ data-channel │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ D6 │ 1726394702721.481 │ data-channel │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ D7 │ 1726394702721.481 │ data-channel │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ D8 │ 1726394702721.481 │ data-channel │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ I/NH8cQqe │ 1726394702721.481 │ local-candidate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ I08b8N49i │ 1726394702721.481 │ local-candidate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ I4uDdiSuP │ 1726394702721.481 │ local-candidate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ IBDwwCTaU │ 1726394702721.481 │ local-candidate │
│ F6WJ4SY2HD63Z25XD3… │ DE5MC2JG3H2PK667ZG… │ WebRTC SFU Sora │ … │ IT0OYEKLy │ 1726394702721.481 │ local-candidate │
│ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │
│ · │ · │ · │ · │ · │ · │ · │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ CPp2HkMADY_vKxy9BYC │ 1726403015487.149 │ candidate-pair │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ D5 │ 1726403015487.149 │ data-channel │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ D6 │ 1726403015487.149 │ data-channel │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ D7 │ 1726403015487.149 │ data-channel │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ D8 │ 1726403015487.149 │ data-channel │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ITdata1A2376627921 │ 1726403015487.149 │ inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ITdata1A3686590866 │ 1726403015487.149 │ inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ITdata1V3657862824 │ 1726403015487.149 │ inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ITdata1V494618204 │ 1726403015487.149 │ inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ OTdata1A3589631305 │ 1726403015487.149 │ outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ OTdata1V886471106 │ 1726403015487.149 │ outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ RIA3589631305 │ 1726403011612.0 │ remote-inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ RIV886471106 │ 1726403014647.0 │ remote-inbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ROA2376627921 │ 1726403013580.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ROA3686590866 │ 1726403011612.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ROV3657862824 │ 1726403015068.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ ROV494618204 │ 1726403014750.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ SA3 │ 1726403015487.149 │ media-source │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ SV4 │ 1726403015487.149 │ media-source │
│ ZVWETDA641527D2A6T… │ S31C88V2SX7M76DD6M… │ WebRTC SFU Sora │ … │ Tdata1 │ 1726403015487.149 │ transport │
├──────────────────────┴──────────────────────┴─────────────────┴───┴──────────────────────┴───────────────────┴─────────────────────┤
│ 83911 rows (40 shown) 20 columns (6 shown) │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Discussion