# Учебный Lakehouse-стенд (Trino + Spark + Iceberg + MinIO)
Учебный стенд для демонстрации **Lakehouse-архитектуры** на одном ноутбуке:
- объектное хранилище S3-класса (MinIO),
- движок запросов Trino,
- Spark для batch/ETL и интерактивных экспериментов,
- формат таблиц Iceberg,
- JDBC-каталог (PostgreSQL) для метаданных Iceberg в Trino,
- конфиг Spark, заточенный под работу с Iceberg + S3.
Стенд ориентирован на обучение менти и быструю демонстрацию концепции Lakehouse: разделение **storage / compute / catalog** без лишней обвязки (Hive Metastore, полноценный Hadoop-кластер и т.п.).
---
## Архитектура
```mermaid
graph LR
subgraph Storage
M[(MinIO
S3-compatible)]
end
subgraph Catalog
P[(PostgreSQL
Iceberg JDBC catalog)]
end
subgraph Compute
S[Spark 3.5.1
PySpark / SQL]
T[Trino 478
Iceberg connector]
end
S <--> M
S <--> P
T <--> M
T <--> P
```
**Основные идеи:**
* **Данные** (Parquet-файлы + служебные каталоги Iceberg) лежат в бакете MinIO (`s3a://lakehouse/warehouse/...`).
* **Метаданные** Iceberg хранятся в PostgreSQL (JDBC-каталог `lakehouse`).
* **Spark и Trino** используют один и тот же JDBC-каталог: таблицы, созданные в Spark, доступны в Trino, и наоборот.
---
## Быстрый старт
Подробный маршрут старта вынесен в [START_HERE.md](./START_HERE.md). Если нужен только краткий запуск, достаточно:
1. Установить Docker и Docker Compose (см. раздел «Требования» ниже).
2. В корне репозитория выполнить:
```bash
docker compose build
docker compose up -d
```
3. Открыть основные интерфейсы:
- Spark Master UI: `http://localhost:8080`
- Trino Web UI: `http://localhost:8090`
- MinIO Console: `http://localhost:9001`
- JupyterLab (если включён): `http://localhost:8888`
4. Для входа в курс и Модуль 1 открыть `START_HERE.md`.
5. Актуальная структура курса описана в `docs/course_program.md`.
---
## Состав репозитория
* `docker-compose.yml` — описание всех сервисов стенда:
* Spark master / worker(ы) на базе кастомного образа `spark-iceberg`,
* Trino,
* MinIO (S3-совместимое хранилище),
* PostgreSQL под Iceberg JDBC-каталог,
* (опционально) Jupyter / notebook для PySpark.
* `spark/Dockerfile` — сборка кастомного образа **Spark** с зависимостями:
* `hadoop-aws`, `aws-java-sdk`,
* библиотека Iceberg нужной версии,
* `pyspark`, `pyarrow` и базовый набор инструментов.
* `jupyter/Dockerfile` — образ JupyterLab на базе Spark-образа.
* `START_HERE.md` — канонический маршрут входа в курс до первого ноутбука.
* `plans/` — внутренние living docs с планами работ по модулям курса.
* `spark/spark-defaults.conf` — конфигурация Spark для работы с:
* Iceberg-каталогом `lakehouse` (тип `jdbc`, метаданные в Postgres),
* MinIO через `s3a://`,
* расширениями `IcebergSparkSessionExtensions`.
* `src/` — учебные примеры для Spark и Trino:
* `src/spark/cluster_smoke.py` — проверка, что кластер жив (Spark master/worker).
* `src/spark/iceberg_smoke.py` — Spark создаёт Iceberg-таблицу и читает её.
* `src/spark/iceberg_demo.sql` — пример создания Iceberg-таблицы через Spark SQL.
* `src/trino/iceberg_smoke.sql` — Trino читает таблицу, созданную в Spark.
* `trino/catalog/lakehouse.properties` — конфиг каталога Trino `lakehouse`:
* коннектор `iceberg`,
* `jdbc`-каталог (PostgreSQL),
* доступ к MinIO как к S3-хранилищу.
---
## Требования
- Docker и Docker Compose.
- Порты по умолчанию должны быть свободны (см. таблицу «Сервисы и порты» ниже).
### Сервисы и порты
| Сервис | Контейнер | Порт (host → container) | Назначение / UI |
|------------------------|-------------------|-------------------------|-------------------------------------|
| Trino | `trino` | `8090 → 8080` | Web UI Trino |
| MinIO API | `minio` | `9000 → 9000` | S3 endpoint |
| MinIO Console | `minio` | `9001 → 9001` | Веб-консоль MinIO |
| PostgreSQL (каталог) | `postgres-iceberg`| `5432 → 5432` | Доступ для psql/DBeaver и т.п. |
| Spark master UI | `spark-master` | `8080 → 8080` | Web UI мастера Spark |
| Spark worker-1 UI | `spark-worker-1` | `8081 → 8081` | Web UI первого воркера |
| Spark worker-2 UI | `spark-worker-2` | `8082 → 8081` | Web UI второго воркера (host 8082) |
| JupyterLab | `jupyter` | `8888 → 8888` | JupyterLab с PySpark |
---
## Сборка и запуск
Все команды в этом разделе выполняются из корня репозитория.
### 1. Собрать образы
```bash
docker compose build
```
Будут собраны кастомные образы Spark/Jupyter с зависимостями Iceberg, S3A, JDBC-драйвером Postgres и Python-библиотеками.
### 2. Поднять стенд
```bash
docker compose up -d
```
Что происходит при старте:
* MinIO поднимается с root-пользователем/паролем (по умолчанию смотри в `docker-compose.yml`, обычно `minioadmin/minioadmin`), init-контейнер создаёт бакет `lakehouse`.
* PostgreSQL под каталог Iceberg создаёт БД и пользователя (значения — в `docker-compose.yml` / `.env`).
* Trino стартует с каталогом `lakehouse`, описанным в `lakehouse.properties`.
* Spark master/worker получают конфиг из `spark-defaults.conf` (общий JDBC-каталог Iceberg + MinIO).
Проверить статус:
```bash
docker compose ps
```
### 3. Остановить стенд и очистить данные
Чтобы остановить все сервисы и удалить данные в MinIO/PostgreSQL (Docker volumes), можно выполнить:
```bash
docker compose down -v
```
---
## Доступ к сервисам
### Trino
Web UI (координатор):
```text
http://localhost:8090
```
(точный порт см. в `docker-compose.yml`).
Подключение **из контейнера trino**:
```bash
docker exec -it trino trino \
--server http://localhost:8080 \
--catalog lakehouse
```
Проверка:
```sql
SHOW CATALOGS;
SHOW SCHEMAS FROM lakehouse;
```
### MinIO
* Консоль: `http://localhost:9001`
* S3 endpoint: `http://localhost:9000`
Учётные данные — `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` из `docker-compose.yml` или `.env`.
Создай в MinIO бакет `lakehouse` (если его нет) — данные Iceberg будут храниться именно там.
### PostgreSQL (каталог Iceberg для Trino)
Подключение (пример):
```bash
psql -h localhost -p 5432 -U iceberg -d iceberg
```
(имя пользователя, БД и порт уточняются в `docker-compose.yml`).
Служебные таблицы Iceberg JDBC-каталога (`iceberg_tables`, `iceberg_namespace_properties`) создаёт однократный контейнер `iceberg-catalog-init`. Если база уже запускалась без них, можно переинициализировать вручную:
```bash
docker compose run --rm iceberg-catalog-init
```
### Jupyter / notebooks
Веб-интерфейс: `http://localhost:8888` (по умолчанию без токена).
* В контейнере монтируется `./notebooks` в `/opt/work`.
* `./src` доступен read-only в `/opt/src`, переменная `PYTHONPATH=/opt/src` уже установлена — можно импортировать функции из скриптов прямо в ноутбуках.
* Первый канонический ноутбук курса: `notebooks/01_environment_and_smoke_test.ipynb`.
---
## Конфигурация Trino (каталог `lakehouse`)
Файл `lakehouse.properties` монтируется в `/etc/trino/catalog/lakehouse.properties`.
Ключевые параметры:
```properties
connector.name=iceberg
# Каталог Iceberg типа JDBC (метаданные в PostgreSQL)
iceberg.catalog.type=jdbc
iceberg.jdbc-catalog.catalog-name=lakehouse
iceberg.jdbc-catalog.driver-class=org.postgresql.Driver
iceberg.jdbc-catalog.connection-url=jdbc:postgresql://postgres-iceberg:5432/iceberg
iceberg.jdbc-catalog.connection-user=iceberg
iceberg.jdbc-catalog.connection-password=iceberg
iceberg.jdbc-catalog.schema-version=V1
# Хранилище файлов Iceberg – S3 (MinIO) через hadoop-клиент
iceberg.file-system.type=hadoop
fs.native-s3.enabled=true
s3.endpoint=http://minio:9000
s3.region=us-east-1
s3.path-style-access=true
s3.aws-access-key=minioadmin
s3.aws-secret-key=minioadmin
```
**Что это даёт:**
* Trino хранит *метаданные* Iceberg в PostgreSQL (таблицы каталога, снапшоты, манифесты и т.п.).
* *Файлы данных* лежат в MinIO, в бакете `lakehouse`, к которому Trino ходит по `s3://`/`s3a://` через S3-клиент.
Пример создания схемы и таблицы из Trino:
```sql
-- Схема в каталоге lakehouse (метаданные в PostgreSQL)
CREATE SCHEMA lakehouse.default;
-- Таблица Iceberg с данными в s3://lakehouse/default/test_table/
CREATE TABLE lakehouse.default.test_table (
id bigint,
name varchar
);
```
---
## Конфигурация Spark (spark-defaults.conf)
`spark-defaults.conf` монтируется в `/opt/spark/conf/spark-defaults.conf` в контейнеры Spark.
Ключевые моменты:
```properties
# Iceberg Spark extensions
spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
# Каталог Iceberg для Spark (JDBC, метаданные в Postgres)
spark.sql.catalog.lakehouse=org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.lakehouse.catalog-impl=org.apache.iceberg.jdbc.JdbcCatalog
spark.sql.catalog.lakehouse.uri=jdbc:postgresql://postgres-iceberg:5432/iceberg
spark.sql.catalog.lakehouse.jdbc.user=iceberg
spark.sql.catalog.lakehouse.jdbc.password=iceberg
spark.sql.catalog.lakehouse.jdbc.driver=org.postgresql.Driver
spark.sql.catalog.lakehouse.warehouse=s3a://lakehouse/warehouse
spark.sql.catalog.lakehouse.default-namespace=default
# S3/MinIO через s3a
spark.hadoop.fs.s3a.endpoint=http://minio:9000
spark.hadoop.fs.s3a.access.key=minioadmin
spark.hadoop.fs.s3a.secret.key=minioadmin
spark.hadoop.fs.s3a.path.style.access=true
spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem
spark.hadoop.fs.s3a.connection.ssl.enabled=false
```
**Важно:** Spark и Trino используют единый JDBC-каталог `lakehouse`: метаданные лежат в Postgres, данные — в MinIO. Таблица, созданная в Spark, видна в Trino без дополнительной настройки.
Пример создания таблицы из Spark:
```python
from pyspark.sql import SparkSession
spark = (SparkSession.builder
.appName("lakehouse-demo")
.getOrCreate())
# Каталог lakehouse указан явно
spark.sql("""
CREATE TABLE lakehouse.default.spark_table (
id BIGINT,
name STRING
)
USING iceberg
""")
spark.sql("INSERT INTO lakehouse.default.spark_table VALUES (1, 'Alice'), (2, 'Bob')")
```
Файлы окажутся в `s3a://lakehouse/warehouse/default/spark_table/`.
---
## Типовой учебный сценарий
1. **Поднять стенд** (`docker compose build`, затем `docker compose up -d`).
2. **Создать бакет `lakehouse`** в MinIO Console (если ещё нет).
3. **Создать таблицу из Spark**, записать туда данные, показать дерево файлов Iceberg в MinIO (data/manifest/metadata).
4. **Прочитать ту же таблицу из Trino** (проверка общего каталога).
5. **Создать таблицу из Trino** и прочитать её из Spark.
6. Обсудить архитектуру: Postgres хранит метаданные Iceberg, MinIO — данные, Spark/Trino — compute.
---
## Smoke-тесты Spark → Trino
Быстрая проверка, что таблица, созданная в Spark, читается в Trino через общий JDBC-каталог.
1. Убедиться, что стенд запущен: `docker compose up -d`.
2. Скопировать скрипты внутрь контейнеров:
```bash
docker compose cp src/spark/iceberg_smoke.py spark-master:/tmp/
docker compose cp src/trino/iceberg_smoke.sql trino:/tmp/
```
3. Выполнить smoke из Spark:
```bash
docker compose exec spark-master /opt/spark/bin/spark-submit /tmp/iceberg_smoke.py
```
Скрипт создаст `lakehouse.default.spark_trino_smoke`, вставит строку `1, from_spark` и прочитает её.
4. Прочитать ту же таблицу из Trino:
```bash
docker compose exec trino trino --file /tmp/iceberg_smoke.sql
```
В выводе должны быть строки `spark_trino_smoke` в списке таблиц и `1, from_spark` в результате выборки.
5. Очистка (опционально):
```bash
docker compose exec spark-master /opt/spark/bin/spark-sql -e "DROP TABLE IF EXISTS lakehouse.default.spark_trino_smoke"
```
---
## Дальнейшее развитие
Планируемые/возможные расширения:
* Подключить **Airflow** и запускать Spark-job’ы поверх этого же Lakehouse.
* Вынести настройки (`MINIO_ROOT_USER`, `ICEBERG_*`, `POSTGRES_*`) в `.env` с шаблоном для студентов.
* Добавить отдельные каталоги Trino (например, `hive`, `tpch`) для демонстрации федеративных запросов.
* Добавить пример интеграции с BI-инструментом (DBeaver/Metabase/Superset) поверх Trino.
Актуальная структура курса описана в `docs/course_program.md`. Старый `HOWTO` сохранён как архивный материал в `docs/archive/legacy_howto.md`.
---
## Лицензия
Материалы этого репозитория лицензированы на условиях Creative Commons Attribution 4.0 International (CC BY 4.0).
См. файл `LICENSE` или .