diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..34fea71
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,9 @@
+# Копировать в .env необязательно: это значения по умолчанию.
+# Если .env уже есть, добавь нужную настройку в него, сохранив остальные.
+
+# GHCR — основной источник MinIO. Резерв того же автора и digest: docker.io.
+MINIO_REGISTRY=ghcr.io
+
+# На Linux при необходимости укажи свой UID и GID (команды: id -u; id -g).
+NB_UID=1000
+NB_GID=100
diff --git a/README.md b/README.md
index 166b9c9..a638adf 100755
--- a/README.md
+++ b/README.md
@@ -17,14 +17,15 @@
## Стек
-Всё работает локально в Docker. Никаких облаков, внешних зависимостей и регистраций.
+Всё работает локально в Docker. Для первой сборки и скачивания учебных данных
+нужен интернет; облачный аккаунт не требуется.
```mermaid
graph TB
U[Student / engineer]
subgraph Access
- J[JupyterLab
notebooks / PySpark]
+ J[VS Code в браузере
Jupyter notebooks / PySpark]
Q[Trino UI / CLI
ad hoc SQL]
end
@@ -54,41 +55,56 @@ graph TB
| `PostgreSQL` | Catalog — метаданные JDBC-каталога `lakehouse` |
| `Spark` | Compute — ETL, запись и чтение Iceberg-таблиц |
| `Trino` | Compute — ad hoc SQL, чтение тех же таблиц |
-| `JupyterLab` | Точка входа — практические ноутбуки курса |
+| `code-server` | VS Code в браузере — ноутбуки курса, Python и терминал внутри Docker |
+| `JupyterLab` | Запасной интерфейс для тех же ноутбуков |
Ноутбуки подключаются к Spark-кластеру с
[готовыми настройками стенда](./docs/stack_reference.md#spark).
## С чего начать
-1. Открой [START_HERE.md](./START_HERE.md) — запуск стенда, диагностика, первый ноутбук.
-2. Пройди `notebooks/01_environment_and_smoke_test.ipynb`.
-3. Дальше по порядку: [docs/course_program.md](./docs/course_program.md).
+1. Открой [START_HERE.md](./START_HERE.md) — подготовка, запуск и первый ноутбук
+ с ожидаемым результатом.
+2. Продолжи по [маршруту из восьми модулей](./docs/course_program.md#маршрут-по-модулям).
+3. Между занятиями используй [памятку учащегося](./docs/study_workflow.md):
+ остановка, продолжение работы и диагностика.
-Краткий запуск из корня репозитория:
+Для одного учащегося рекомендуем компьютер с 16 ГБ RAM, не менее 4 CPU и
+ориентировочно 8 ГБ памяти для Docker, а также от 20 ГБ свободного места перед сборкой.
+Trino ограничен 2 ГиБ памяти контейнера и 1 ГиБ Java heap.
+Подробности и границы проверки — в [системных требованиях](./START_HERE.md#системные-требования).
+
+Краткий запуск из корня репозитория после подготовки по `START_HERE`:
```bash
+docker compose pull minio minio-init postgres trino
docker compose build
docker compose up -d
-docker compose ps
+docker compose ps -a
```
-Зафиксированные исторические образы MinIO загружаются из официального Quay registry; подробности — в [техническом reference](./docs/stack_reference.md#minio).
+MinIO скачивается готовым из GHCR, его версия и digest закреплены. Если какой-то
+источник недоступен из твоей сети, начни с
+[инструкции по доступу к репозиториям](./docs/network_access.md).
После старта:
| UI | Адрес |
| --- | --- |
-| JupyterLab | `http://localhost:8888` |
+| VS Code в браузере | `http://localhost:8889` |
+| JupyterLab (запасной интерфейс) | `http://localhost:8888` |
| Spark Master | `http://localhost:8080` |
| Trino | `http://localhost:8090` |
| MinIO Console | `http://localhost:9001` |
-JupyterLab опубликован только на loopback-интерфейсе хоста и рассчитан на локальный одно-пользовательский стенд. Подключение через VS Code Remote-SSH описано в [START_HERE.md](./START_HERE.md#как-работать-в-jupyter).
+Открой ноутбук в браузерном VS Code и выбери Python внутри контейнера: локальный
+Python и venv не нужны. Оба интерфейса доступны только с хоста стенда.
+Первый запуск ноутбука описан в [START_HERE.md](./START_HERE.md#как-работать-с-ноутбуками).
## Документы для прохождения
-- [START_HERE.md](./START_HERE.md) — первый маршрут: prerequisites, запуск, диагностика.
+- [START_HERE.md](./START_HERE.md) — подготовка компьютера, запуск и первый результат.
+- [docs/study_workflow.md](./docs/study_workflow.md) — памятка для каждого занятия и помощь при ошибках.
- [docs/course_program.md](./docs/course_program.md) — модульная структура и состав учебных материалов.
- [docs/stack_reference.md](./docs/stack_reference.md) — порты, команды, конфиги, шпаргалка.
- [docs/glossary.md](./docs/glossary.md) — справочник терминов (storage, catalog, compute, snapshot и др.).
diff --git a/START_HERE.md b/START_HERE.md
index f4ad300..a9e8514 100644
--- a/START_HERE.md
+++ b/START_HERE.md
@@ -1,31 +1,88 @@
# START HERE
-Этот документ нужен для первого входа в курс: поднять стенд, проверить сервисы, открыть интерфейсы, выполнить базовый smoke test и подготовить локальный data bundle для следующих модулей.
+Здесь ты подготовишь окружение и выполнишь первое вычисление в Spark.
+К концу запуска в ноутбуке появится `Spark cluster smoke test passed.`, а результат
+сохранится в файле на твоем компьютере. Smoke test — короткая проверка, что
+основные компоненты работают вместе.
+
+Если стенд уже собран, открой [памятку для следующего занятия](./docs/study_workflow.md).
+Она объясняет, как продолжить работу, что сохраняется и куда смотреть при ошибках.
## Маршрут прохождения
-1. Проверить prerequisites.
-2. Собрать и поднять стенд.
-3. Убедиться, что контейнеры живы.
-4. Открыть основные UI.
-5. Зайти в Jupyter и выполнить `notebooks/01_environment_and_smoke_test.ipynb`.
-6. Перед Модулем 3 подготовить учебный датасет в `./data/nyc_taxi`.
-7. При проблемах использовать логи и шаги диагностики из этого документа.
+1. Подготовь Docker, ресурсы и локальную копию репозитория.
+2. Скачай образы, собери окружение и проверь запуск сервисов.
+3. Открой первый ноутбук в браузерном VS Code и проверь результат вычисления.
+4. Продолжи по [программе курса](./docs/course_program.md#маршрут-по-модулям).
-## Prerequisites
+Датасет Taxi понадобится перед Модулем 3, DBeaver необязателен и понадобится
+только перед Модулем 6. Для первого ноутбука их готовить не нужно.
-Нужно заранее установить:
+
-- Docker;
-- Docker Compose;
-- современный браузер для UI;
-- свободные порты на хосте.
+## Что подготовить
-Рекомендуемые ресурсы хоста:
+- Docker с запущенным движком и Docker Compose 2.17+ с BuildKit.
+- Современный браузер; основной интерфейс курса — VS Code на `localhost:8889`.
+- Git для скачивания репозитория или его распакованную ZIP-копию.
+- Свободные порты из таблицы ниже и доступ к источникам образов и зависимостей.
-- не менее `4 vCPU`;
-- не менее `10 GB RAM`, иначе `Spark`, `Trino` и `Jupyter` могут стартовать нестабильно;
-- хотя бы `8-10 GB` свободного места под образы и контейнеры.
+Команды ниже рассчитаны на Bash: терминал Linux/macOS или терминал Linux
+в WSL2 на Windows с включенной интеграцией Docker Desktop. Выполняй их на
+машине, где работает Docker. Терминал внутри браузерного VS Code для запуска
+`docker compose` не подходит.
+
+### Системные требования
+
+Ориентир для одного учащегося и основного набора Taxi за три месяца:
+
+| Ресурс | Рекомендация | Что проверить |
+| --- | --- | --- |
+| Процессор | Не менее 4 CPU, доступных Docker | В Docker Desktop/WSL2 может стоять меньший лимит |
+| Память Docker | Ориентир — 8 ГБ RAM; 10 ГБ дают дополнительный запас | Это память для среды Docker, а не общий объем памяти компьютера |
+| Память компьютера | 16 ГБ RAM или больше | Оставь память ОС, браузеру и другим приложениям |
+| Свободное место | Не менее 20 ГБ перед первой сборкой; 30 ГБ с запасом | На диске Docker и в папке репозитория: образы, кэш сборки, исходные данные и таблицы |
+
+Это рекомендуемый бюджет для одного активного ноутбука, а не измеренный минимум.
+На Windows с WSL2 по умолчанию доступно 50% RAM компьютера: при 16 ГБ это 8 ГБ
+для всей WSL2, включая Docker. Лимит можно настроить, это не аппаратное
+ограничение. См. [настройки WSL2](https://learn.microsoft.com/en-us/windows/wsl/wsl-config#main-wsl-settings)
+и [ресурсы Docker Desktop](https://docs.docker.com/desktop/settings-and-maintenance/settings/#resources).
+
+Trino ограничен 2 ГиБ памяти контейнера и 1 ГиБ Java heap. Проверка запросов
+на данных курса описана в [справочнике](./docs/stack_reference.md#ресурсы-и-проверенная-среда).
+Полный курс под общим жестким лимитом Docker 8 ГБ пока не проверен. В WSL2 часть
+памяти также нужна самой Linux-среде; закрой другие тяжелые задачи на время урока.
+Место внутри диска Docker и свободное место на диске компьютера — отдельные ограничения.
+
+С добавлением VS Code образ окружения вырос примерно на 1,2 ГБ относительно
+Jupyter; базовые слои у них общие. MinIO скачивается готовым, компилировать его
+не нужно. [Основания оценок и границы проверки](./docs/stack_reference.md#ресурсы-и-проверенная-среда).
+
+Python, Java, Jupyter и расширения VS Code устанавливаются при сборке образов.
+На компьютере не нужно создавать venv или устанавливать Python-пакеты.
+
+Для первой сборки нужен интернет. Если ты работаешь из РФ или сети с
+ограничениями, при ошибке скачивания используй
+[инструкцию по доступу к репозиториям](./docs/network_access.md). Она различает
+проблемы скачивания образов и зависимостей внутри сборки.
+
+### Проверка до сборки
+
+Открой терминал в папке репозитория — там, где лежит `docker-compose.yml`:
+
+```bash
+docker version
+docker compose version
+docker info --format 'CPUs={{.NCPU}} MemoryBytes={{.MemTotal}}'
+docker compose config --quiet
+```
+
+В `docker version` должны быть разделы Client и Server, версия Compose — не
+ниже 2.17. `docker info` показывает ресурсы, которые видит Docker; память дана
+в байтах (8 ГиБ — 8 589 934 592 байта). Последняя команда при успехе
+ничего не печатает. Если Docker не отвечает или конфигурация не читается,
+исправь эту ошибку до сборки.
## Порты стенда
@@ -37,20 +94,46 @@
| Trino UI | `http://localhost:8090` | Проверка координатора Trino |
| MinIO API | `http://localhost:9000` | S3-compatible endpoint |
| MinIO Console | `http://localhost:9001` | Просмотр бакетов и файлов |
-| JupyterLab | `http://localhost:8888` | Основная точка входа в практику |
+| VS Code в браузере | `http://localhost:8889` | Основная точка входа в практику |
+| JupyterLab | `http://localhost:8888` | Запасной интерфейс для ноутбуков |
| PostgreSQL | `localhost:5432` | JDBC-каталог Iceberg, нужен для диагностики |
## Быстрый запуск
Все команды выполняются из корня репозитория.
-### 1. Собрать образы
+На Linux и в WSL2 проверь владельца файлов:
```bash
+id -u
+id -g
+```
+
+Это UID и GID — числовые идентификаторы пользователя и группы. В контейнере по
+умолчанию используются `1000` и `100`. Если значения отличаются, до сборки
+создай `.env` в корне репозитория и укажи `NB_UID` и `NB_GID` со своими числами.
+Например, для вывода `1001` и `1001`:
+
+```dotenv
+NB_UID=1001
+NB_GID=1001
+```
+
+Если `.env` уже есть, измени только эти строки, сохранив остальные настройки.
+Файл остается между занятиями. Образец — [`.env.example`](./.env.example).
+На macOS с Docker Desktop обычно подходят значения по умолчанию.
+
+### 1. Скачать готовые образы и собрать окружение
+
+```bash
+docker compose pull minio minio-init postgres trino
docker compose build
```
-Зафиксированные образы MinIO загружаются из `quay.io`; их версии и назначение описаны в [техническом reference](./docs/stack_reference.md#minio).
+Первая команда проверяет загрузку готовых сервисов до сборки окружения.
+MinIO уже собран; для Spark, Jupyter и IDE первая сборка скачивает зависимости
+и зависит по времени от сети и компьютера. Дождись завершения без ошибок;
+не переходи к запуску после неудачной сборки. Повторная сборка использует кэш Docker.
### 2. Поднять стенд
@@ -61,33 +144,36 @@ docker compose up -d
### 3. Проверить статус контейнеров
```bash
-docker compose ps
+docker compose ps -a
```
Ожидаемое состояние:
-- сервисы `spark-master`, `spark-worker-1`, `spark-worker-2`, `minio`, `postgres-iceberg`, `trino`, `jupyter` находятся в состоянии `Up`;
-- однократные init-контейнеры вроде `minio-init` и `iceberg-catalog-init` могут завершиться после успешной инициализации.
+- `spark-master`, `spark-worker-1`, `spark-worker-2`, `minio`, `postgres-iceberg`,
+ `trino`, `jupyter`, `ide` работают (`Up`); у `postgres-iceberg`, `trino` и `ide`
+ дождись `healthy`;
+- `minio-init` и `iceberg-catalog-init` заканчивают работу со статусом `Exited (0)`.
+ Это успешная разовая подготовка хранилища и каталога. `Exited (1)` и другой
+ ненулевой код требуют проверки логов.
+
+Флаг `-a` показывает и завершившиеся контейнеры. Сам по себе статус `Up` еще
+не доказывает, что вычисления работают: дальше проверим их в ноутбуке.
## Куда заходить после старта
-Открой в браузере:
+| Что открыть | Что должно быть видно |
+| --- | --- |
+| [Spark Master](http://localhost:8080) | Два worker-а в состоянии `ALIVE` |
+| [MinIO Console](http://localhost:9001) | После входа `minioadmin` / `minioadmin` виден бакет `lakehouse` — контейнер для файлов курса |
+| [VS Code](http://localhost:8889) | Слева папки **Ноутбуки**, **Примеры (только чтение)**, **Документация (только чтение)** |
-- `http://localhost:8080` для `Spark Master UI`;
-- `http://localhost:8090` для `Trino UI`;
-- `http://localhost:9001` для `MinIO Console`;
-- `http://localhost:8888` для `JupyterLab`.
+В [Trino UI](http://localhost:8090) для входа достаточно имени, например `student`;
+пароль в стенде не задан. Сейчас достаточно проверить, что интерфейс открывается.
+Чтение общих таблиц через Trino разберем в Модуле 6.
-Для входа в `MinIO Console` используй:
-
-- логин: `minioadmin`;
-- пароль: `minioadmin`.
-
-Если интерфейсы открываются, переходи в Jupyter и запускай:
-
-```text
-notebooks/01_environment_and_smoke_test.ipynb
-```
+Этот маршрут предполагает Docker на твоем компьютере: `localhost` относится
+к машине с браузером. Доступ к VS Code на удаленном сервере описан отдельно
+в [инструкции по SSH-туннелю](./docs/stack_reference.md#vs-code-в-браузере).
## Роли сервисов в стенде
@@ -97,68 +183,76 @@ notebooks/01_environment_and_smoke_test.ipynb
| `PostgreSQL` | `catalog`: хранит метаданные JDBC-каталога Iceberg |
| `Spark` | `compute`: выполняет PySpark и Spark SQL задания |
| `Trino` | `compute`: читает те же таблицы через общий каталог |
-| `Jupyter` | Точка входа в учебные ноутбуки |
+| `VS Code / JupyterLab` | Интерфейсы для учебных ноутбуков |
-## Как работать в Jupyter
+## Как работать с ноутбуками
-- JupyterLab доступен без логина только с хоста, на котором запущен стенд;
-- при работе через VS Code Remote-SSH выбери `Existing Jupyter Server` и укажи `http://127.0.0.1:8888/`;
-- token в адрес добавлять не нужно: аутентификация и XSRF-проверка отключены для локального одно-пользовательского сценария;
-- `./notebooks` смонтирован в контейнер как `/opt/work`;
-- `./src` смонтирован read-only как `/opt/src`;
-- `./data` смонтирован read-only как `/opt/data`;
-- `PYTHONPATH=/opt/src`, поэтому helper-скрипты из `src/` доступны для импорта в ноутбуках;
-- первый ноутбук курса: `01_environment_and_smoke_test.ipynb`;
-- локальный учебный data bundle для Модуля 3 должен быть доступен внутри Jupyter по пути `/opt/data/nyc_taxi`.
+Ноутбук — файл с пояснениями, кодом и сохраненным выводом. Ядро — процесс Python,
+который исполняет код и хранит переменные до остановки.
-Ноутбуки берут адрес кластера из
-[`spark/spark-defaults.conf`](./spark/spark-defaults.conf). Если изменил его,
-перезапусти ядро и заново создай Spark-сессию. Повторный запуск ячейки с
-`getOrCreate()` использует текущую сессию. При перезапуске ядра переменные Python
-сбрасываются, а учебные таблицы и файлы сохраняются.
+1. Открой `http://localhost:8889` в отдельной вкладке браузера.
+2. Если сверху виден **Restricted Mode**, нажми **Manage → Trust** и закрой окно
+ Workspace Trust. Подтверди доверие только к своей копии этого учебного
+ репозитория: в Restricted Mode запуск ноутбуков ограничен.
+3. В папке **Ноутбуки** открой `01_environment_and_smoke_test.ipynb`.
+4. Нажми **Select Kernel → Python Environments** и выбери Python с путём
+ `/usr/bin/python3`. Он находится внутри контейнера `ide`, где уже есть
+ зависимости курса. Пункт **Existing Jupyter Server** здесь не нужен.
+5. Запусти первую кодовую ячейку кнопкой слева или `Shift+Enter`, затем остальные
+ ячейки по порядку. Результат появится под ячейкой. Сохрани ноутбук через
+ **File → Save** (`Ctrl+S`; на macOS — `Cmd+S`).
+
+Терминал VS Code тоже работает внутри контейнера. Команды `docker compose`
+выполняй в терминале хоста, откуда запускал стенд.
+
+### Как понять, что первый запуск прошел
+
+В третьей кодовой ячейке первого ноутбука появится отчет, содержащий:
+
+```text
+Spark cluster smoke test passed.
+master_url=spark://spark-master:7077
+result_sum=499999500000
+expected_sum=499999500000
+```
+
+В отчете есть и другие строки; значения выше должны совпасть. Затем выполни
+самостоятельное задание: замени `TODO` в словаре `service_roles` ролями сервисов.
+Сам факт запуска ячейки с `TODO` не означает, что задание выполнено.
+
+В конце выполни ячейку `spark.stop()`, сохрани ноутбук и проверь, что его изменения
+видны в `notebooks/01_environment_and_smoke_test.ipynb` на хосте. После повторного
+открытия сохраненный вывод остается виден. Такой вывод — запись прошлого запуска;
+он не означает, что ядро Python и Spark-сессия продолжают работать.
+
+Правила запуска ячеек, перехода между модулями и сохранения данных собраны в
+[памятке учащегося](./docs/study_workflow.md).
+
+Запасной интерфейс — JupyterLab на `http://localhost:8888`. Файлы ноутбуков общие,
+а ядра и переменные Python отдельные. Перед переходом сохрани ноутбук и заверши
+Spark-сессию через `spark.stop()`; не редактируй один файл в двух интерфейсах
+одновременно.
## Базовая диагностика
-### Посмотреть список контейнеров
+Начни с `docker compose ps -a` и логов проблемного сервиса, например:
```bash
-docker compose ps
+docker compose logs --tail=100 ide
```
-### Посмотреть логи конкретного сервиса
-
-```bash
-docker compose logs -f spark-master
-docker compose logs -f trino
-docker compose logs -f minio
-docker compose logs -f jupyter
-```
-
-### Типовые первые проверки
-
-- `Spark UI` не открывается: проверь `docker compose ps` и логи `spark-master`.
-- `Trino UI` не открывается: проверь `docker compose ps` и логи `trino`.
-- `JupyterLab` не открывается: проверь `docker compose ps` и логи `jupyter`.
-- `MinIO Console` не открывается: проверь `docker compose ps` и логи `minio`.
-- smoke test падает из ноутбука: сначала убедись, что `spark-master` и worker-ы видны в `Spark UI`.
+Выбери свой симптом в [таблице диагностики](./docs/study_workflow.md#если-что-то-не-работает).
+При ошибке скачивания сразу открой [диагностику сети](./docs/network_access.md).
## Restart и reset
-### Мягкий перезапуск стенда
+Для обычной паузы используй `docker compose stop`, на следующем занятии —
+`docker compose up -d`. Порядок действий и последствия удаления контейнеров
+описаны в [памятке](./docs/study_workflow.md#закончить-занятие).
-```bash
-docker compose down
-docker compose up -d
-```
-
-### Полный reset с удалением данных
-
-```bash
-docker compose down -v
-docker compose up -d
-```
-
-Используй полный reset, если хочешь пройти практику заново с чистого состояния.
+Полный сброс удаляет учебные таблицы и каталог. Его команда вынесена в
+[отдельный раздел](./docs/study_workflow.md#начать-с-чистых-данных), чтобы не путать
+с окончанием занятия.
## Подготовка учебного датасета (перед Модулем 3)
@@ -167,6 +261,7 @@ docker compose up -d
Скачай каноническое учебное подмножество `NYC TLC Yellow Taxi Trip Records`:
```bash
+mkdir -p data/nyc_taxi
curl -fLo data/nyc_taxi/yellow_tripdata_2024-01.parquet \
https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2024-01.parquet
curl -fLo data/nyc_taxi/yellow_tripdata_2024-02.parquet \
@@ -197,13 +292,12 @@ ls -lh data/nyc_taxi/
- Зачем: в Модуле 6 можно работать с Trino через DBeaver параллельно с ноутбуком — привычный SQL-интерфейс.
- Предусловие: DBeaver установлен (ссылка на [dbeaver.io/download](https://dbeaver.io/download/)). Необязателен — ноутбук работает без DBeaver.
- Шаги: New Database Connection -> Trino. Host: `localhost`. Port: `8090`. Database/Catalog: `lakehouse`. Username: любая строка (напр. `student`). Password: пусто. Test Connection -> Finish.
-- Проверка: `SHOW SCHEMAS FROM lakehouse;`. Ожидаем: `bronze`, `default`, `information_schema`, `silver`.
+- Проверка: `SHOW SCHEMAS FROM lakehouse;`. После модулей 4–5 должны быть видны `bronze` и `silver`; до их прохождения этих схем может еще не быть.
- Troubleshooting: стенд поднят? контейнер `trino` Up? порт 8090 свободен?
## Что делать дальше
-- пройти `notebooks/01_environment_and_smoke_test.ipynb`;
-- пройти `notebooks/02_lakehouse_mental_model.ipynb`;
-- подготовить data bundle по инструкции выше и пройти `notebooks/03_raw_ingest_and_first_read.ipynb`;
-- свериться с программой курса в `docs/course_program.md`;
-- после прохождения первых модулей переходить к следующим учебным материалам.
+После первого ноутбука открой [маршрут по модулям](./docs/course_program.md#маршрут-по-модулям).
+В нем есть ссылки на все восемь уроков и результат, который нужно получить
+перед переходом дальше. Для следующего занятия сохрани ссылку на
+[памятку учащегося](./docs/study_workflow.md).
diff --git a/docker-compose.yml b/docker-compose.yml
index 73bf191..8e04b70 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -121,7 +121,8 @@ services:
- ./spark/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro
minio:
- image: quay.io/minio/minio:RELEASE.2024-10-02T17-50-41Z
+ # Один и тот же образ опубликован автором в GHCR и Docker Hub.
+ image: &minio-image ${MINIO_REGISTRY:-ghcr.io}/coollabsio/minio:RELEASE.2025-04-22T22-12-26Z@sha256:a4938f37f1be1841b8e7b627ad0207b265345fd0d063e42d7410c78af0e63e68
container_name: minio
hostname: minio
networks:
@@ -137,7 +138,7 @@ services:
- minio-data:/data
minio-init:
- image: quay.io/minio/mc:RELEASE.2024-06-24T19-40-33Z-cpuv1
+ image: *minio-image
container_name: minio-init
hostname: minio-init
networks:
@@ -145,13 +146,13 @@ services:
depends_on:
- minio
entrypoint: >
- /bin/sh -c "
+ /bin/sh -ec "
echo 'Waiting for MinIO...';
until mc alias set minio http://minio:9000 minioadmin minioadmin 2>/dev/null; do
sleep 1;
done;
echo 'MinIO is up, creating bucket...';
- mc mb -p minio/lakehouse || true;
+ mc mb --ignore-existing minio/lakehouse;
echo 'Bucket lakehouse is ready';
exit 0;
"
@@ -160,6 +161,8 @@ services:
jupyter:
build:
context: ./jupyter
+ additional_contexts:
+ spark-runtime: service:spark-master
args:
NB_UID: ${NB_UID:-1000}
NB_GID: ${NB_GID:-100}
@@ -192,10 +195,45 @@ services:
- ./src:/opt/src:ro
- ./data:/opt/data:ro
+ ide:
+ build:
+ context: ./ide
+ additional_contexts:
+ jupyter-runtime: service:jupyter
+ container_name: ide
+ hostname: ide
+ depends_on:
+ - spark-master
+ - minio
+ - iceberg-catalog-init
+ networks:
+ - spark-net
+ environment:
+ - AWS_ACCESS_KEY_ID=minioadmin
+ - AWS_SECRET_ACCESS_KEY=minioadmin
+ - PYTHONPATH=/opt/src
+ ports:
+ - "127.0.0.1:8889:8889"
+ volumes:
+ - ./spark/spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro
+ - ./notebooks:/opt/work
+ - ./src:/opt/src:ro
+ - ./data:/opt/data:ro
+ - .:/opt/course:ro
+ - ./notebooks:/opt/course/notebooks
+ healthcheck:
+ test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8889/healthz"]
+ interval: 10s
+ timeout: 5s
+ retries: 6
+
trino:
image: trinodb/trino:478
container_name: trino
hostname: trino
+ # Heap JVM ограничен отдельно в trino/jvm.config; остальное — запас вне heap.
+ mem_limit: 2g
+ memswap_limit: 2g
networks:
- spark-net
depends_on:
@@ -210,6 +248,7 @@ services:
- "8090:8080" # Web UI Trino
volumes:
- ./trino/catalog:/etc/trino/catalog:ro
+ - ./trino/jvm.config:/etc/trino/jvm.config:ro
networks:
spark-net:
diff --git a/docs/course_prd.md b/docs/course_prd.md
index 716c2e0..c9a0024 100644
--- a/docs/course_prd.md
+++ b/docs/course_prd.md
@@ -96,7 +96,7 @@
* snapshot/time travel на прикладном уровне;
* базовый `compaction/vacuum` с параллелями к `Greenplum AppendOnly`;
* набор практик в Jupyter Notebooks с обязательными заданиями и checkpoints;
-* стартовая документация до первого ноутбука: prerequisites, запуск стенда, вход в Jupyter и базовый troubleshooting.
+* стартовая документация до первого ноутбука: prerequisites, запуск стенда, вход в браузерный VS Code и базовый troubleshooting.
Ориентировочная программа курса вынесена в отдельный документ: **[course_program.md](./course_program.md)**.
@@ -152,7 +152,7 @@
**Acceptance Criteria для учебных материалов и результата:**
-1. В составе курса есть стартовая документация до первого ноутбука, включая prerequisites, запуск стенда, вход в `Jupyter` и получение учебного data bundle.
+1. В составе курса есть стартовая документация до первого ноутбука, включая prerequisites, запуск стенда, вход в браузерный VS Code и получение учебного data bundle.
2. Студент проходит обязательные `checkpoints` по урокам.
3. Студент самостоятельно выполняет набор практик на одном датасете, включая:
* подключение к Spark и загрузку raw-данных;
@@ -174,7 +174,7 @@
**В скоупе:**
-* использование существующего `docker-compose.yml` (`Spark`, `Trino`, `MinIO`, `PostgreSQL`, `Jupyter`);
+* использование существующего `docker-compose.yml` (`Spark`, `Trino`, `MinIO`, `PostgreSQL`, `code-server`, `JupyterLab`);
* подготовка учебных датасетов и/или инструкций по их загрузке в репозиторий;
* наличие воспроизводимого способа получить учебный data bundle без ручного поиска по внешним сайтам;
* поддержка выполнения запросов к `Trino` из ноутбуков;
diff --git a/docs/course_program.md b/docs/course_program.md
index 9ce667f..6f9802c 100644
--- a/docs/course_program.md
+++ b/docs/course_program.md
@@ -1,6 +1,29 @@
# Программа учебного курса: Lakehouse без магии
-Этот документ заменяет предыдущий черновой план и выравнивает программу с PRD из [course_prd.md](./course_prd.md).
+Здесь собраны порядок модулей, ссылки на ноутбуки и результаты, по которым можно
+проверить свою работу. Рамки курса и learning outcomes описаны в [course_prd.md](./course_prd.md).
+
+## Маршрут по модулям
+
+Перед первым уроком подготовь стенд по [START_HERE](../START_HERE.md).
+Дальше проходи ноутбуки по порядку: файлы и таблицы из ранних модулей нужны в
+следующих. Пауза между занятиями описана в [памятке учащегося](./study_workflow.md).
+
+| Модуль и ноутбук | Что понадобится | Результат перед переходом дальше |
+| --- | --- | --- |
+| [1. Вход в стенд](../notebooks/01_environment_and_smoke_test.ipynb) | Работающий стенд | Smoke test вернул `499999500000`; ты можешь показать два worker-а и объяснить роли сервисов |
+| [2. Модель Lakehouse](../notebooks/02_lakehouse_mental_model.ipynb) | Модуль 1 | Ты нашел файлы демо-таблицы в MinIO и запись каталога в PostgreSQL; выполнил очистку демо по ноутбуку |
+| [3. Исходные данные](../notebooks/03_raw_ingest_and_first_read.ipynb) | [Четыре файла Taxi](../START_HERE.md#подготовка-учебного-датасета-перед-модулем-3) на хосте | Файлы загружены в raw-зону MinIO, Spark читает их; проверены схема и основные метрики |
+| [4. Bronze и Iceberg](../notebooks/04_bronze_with_iceberg.ipynb) | Raw-файлы из Модуля 3 | Созданы bronze-таблица поездок и таблица зон из самостоятельного задания; видны их данные и metadata-файлы |
+| [5. Silver](../notebooks/05_silver_layer.ipynb) | Обе bronze-таблицы | Создана silver-таблица, проверены правила очистки и качество результата |
+| [6. Spark и Trino](../notebooks/06_spark_and_trino_on_same_table.ipynb) | Silver из Модуля 5; DBeaver по желанию | Одна таблица читается обоими движками; ты можешь объяснить, какие компоненты у них общие |
+| [7. Изменения таблиц](../notebooks/07_safe_table_changes.ipynb) | Результаты предыдущих модулей | На демо-таблице проверены изменение схемы, чтение прошлого состояния и rollback; silver сохранена |
+| [8. Обслуживание и финальная практика](../notebooks/08_maintenance_and_final_lab.ipynb) | Raw, bronze и silver | Проверено обслуживание таблиц; самостоятельно пройден поток от исходных файлов до чтения и проверок результата |
+
+В каждом ноутбуке сначала прочитай объяснение, затем выполни демонстрацию и
+самостоятельное задание. Checkpoint в конце урока — вопросы и проверки для
+самопроверки. Сохраненный вывод помогает вернуться к результату, но не заменяет
+собственный запуск и объяснение.
## 1. Рамка курса
@@ -27,7 +50,7 @@
## 3. Состав учебных материалов
-Курс не должен состоять только из ноутбуков. Ноутбуки являются ядром практики, но вход в курс, запуск стенда и правила работы должны быть вынесены в обычную документацию, доступную до старта `Jupyter`.
+Курс не должен состоять только из ноутбуков. Ноутбуки являются ядром практики, но вход в курс, запуск стенда и правила работы должны быть вынесены в обычную документацию, доступную до открытия IDE.
### 3.1. Стартовая документация до первого ноутбука
@@ -38,15 +61,17 @@
* `START_HERE.md` или аналогичный стартовый документ с маршрутом прохождения курса;
* инструкция по prerequisites: `Docker`, `Docker Compose`, свободные порты, базовые команды;
* пошаговый гайд по запуску стенда: `build`, `up`, проверка сервисов, открытие UI;
-* краткое объяснение, как пользоваться `Jupyter`, где лежат ноутбуки и как читать структуру урока;
+* краткое объяснение, как запускать Jupyter-ноутбуки в браузерном VS Code, где лежат ноутбуки и как читать структуру урока;
* troubleshooting по типовым проблемам старта;
-* инструкция по reset/restart стенда для повторного прохождения практик.
+* [памятка учащегося](./study_workflow.md): продолжение занятия, сохранение данных,
+ перезапуск ядра и диагностика;
+* отдельная инструкция по сбросу учебных данных для повторного прохождения практик.
**Что должен закрывать этот слой:**
* студент понимает, что нужно установить и проверить до начала курса;
* студент может поднять стенд без ментора;
-* студент знает, как попасть в `Jupyter`, `MinIO`, `Trino UI`;
+* студент знает, как попасть в браузерный VS Code, `MinIO`, `Trino UI`;
* студент понимает, как устроен формат уроков и что от него ожидается.
### 3.2. Практическое ядро курса
@@ -85,10 +110,10 @@
**Содержание:**
-* запуск `docker compose build` и `docker compose up -d`;
+* скачивание готовых образов, сборка окружения и запуск по `START_HERE.md`;
* проверка сервисов и веб-интерфейсов;
* базовый smoke test для Spark;
-* первый обзор того, где в стенде `MinIO`, `PostgreSQL`, `Spark`, `Trino`, `Jupyter`.
+* первый обзор того, где в стенде `MinIO`, `PostgreSQL`, `Spark`, `Trino` и IDE.
**Практика студента:**
diff --git a/docs/maintainer_guide.md b/docs/maintainer_guide.md
index c0deab3..ee2d768 100644
--- a/docs/maintainer_guide.md
+++ b/docs/maintainer_guide.md
@@ -17,7 +17,7 @@
| Зона | Где лежит | Назначение |
| --- | --- | --- |
-| Runtime / stand | `docker-compose.yml`, `spark/`, `trino/`, `jupyter/` | Топология сервисов, образы, конфиги, порты |
+| Runtime / stand | `docker-compose.yml`, `spark/`, `trino/`, `jupyter/`, `ide/` | Топология сервисов, образы, конфиги, порты |
| Onboarding | `README.md`, `START_HERE.md` | Вход в репозиторий и первый пользовательский маршрут |
| Runtime reference | `docs/stack_reference.md` | Технические детали стенда: порты, доступ, команды, smoke-тесты |
| Course definition | `docs/course_prd.md`, `docs/course_program.md` | Границы курса, learning outcomes, структура модулей |
@@ -31,9 +31,10 @@
| Файл или каталог | За что отвечает | Что не стоит туда складывать |
| --- | --- | --- |
| `docker-compose.yml` | Реальный состав сервисов, контейнеров, сетей, портов и зависимостей | Учебные пояснения, которые не нужны для запуска |
-| `spark/`, `trino/`, `jupyter/` | Конкретные runtime-конфиги и образы | Описание программы курса |
+| `spark/`, `trino/`, `jupyter/`, `ide/` | Конкретные runtime-конфиги и образы | Описание программы курса |
| `README.md` | Верхнеуровневое объяснение репозитория, архитектуры и точек входа | Пошаговый student onboarding во всех деталях и low-level runtime reference |
| `START_HERE.md` | Первый маршрут студента: prerequisites, запуск, первые UI, первый ноутбук, базовая диагностика | Полный PRD курса или подробный бэклог модулей |
+| `docs/study_workflow.md` | Повседневный маршрут: продолжить и закончить занятие, сохранить результат, восстановиться после ошибки | Состав модулей, установка окружения с нуля |
| `docs/stack_reference.md` | Технический reference стенда: сервисы, порты, доступ, reset, smoke-тесты | Роль основного onboarding-документа или описание всей программы курса |
| `docs/course_prd.md` | Product scope, аудитория, learning outcomes, dataset strategy, out of scope | Технические мелочи запуска контейнеров |
| `docs/course_program.md` | Модульная структура курса, состав материалов, checkpoints | Подробные docker-команды и legacy-лабы |
@@ -75,6 +76,7 @@
- `START_HERE.md`;
- `docs/course_program.md`;
+- `docs/study_workflow.md`;
- соответствующие `notebooks/`;
- `README.md`, если меняется верхнеуровневый вход в репозиторий.
diff --git a/docs/mentor_notes.md b/docs/mentor_notes.md
index 45eb719..8df920c 100644
--- a/docs/mentor_notes.md
+++ b/docs/mentor_notes.md
@@ -17,9 +17,9 @@
| Модуль | Тема | Самостоятельная работа | Обсуждение с ментором | Комментарий |
| --- | --- | --- | --- | --- |
-| 1 | Вход в стенд | 30-40 мин | 10-15 мин | Если Docker знаком, идёт быстро |
+| 1 | Вход в стенд | 30-40 мин | 10-15 мин | Без учета первой сборки: ее время зависит от сети и компьютера |
| 2 | Ментальная модель | 40-60 мин | 15-20 мин | Ключевой модуль; убедись, что студент заходил в MinIO |
-| 3 | Raw-данные | 40-50 мин | 10 мин | Зависит от скорости загрузки данных |
+| 3 | Raw-данные | Около 2 часов | 10 мин | Включая 20–30 минут на практику со Spark-сессией; датасет скачивается заранее |
| 4 | Bronze + Iceberg | 60-80 мин | 15-20 мин | Первая таблица — много новых концепций |
| 5 | Silver | 50-70 мин | 15 мин | Трансформации + проверки качества |
| 6 | Spark + Trino | 40-60 мин | 15 мин | Если DBeaver настроен заранее — быстрее |
@@ -34,8 +34,9 @@
### Модуль 1. Вход в стенд
-- **Docker не хватает ресурсов.** Стенд требует ~4-6 ГБ RAM. На машинах с 8 ГБ бывают проблемы. Решение: увеличить лимиты Docker Desktop или закрыть лишние приложения.
-- **Порты заняты.** 8080, 8888, 9000 — популярные порты. `docker compose ps` покажет, какой сервис не стартовал. Решение: остановить конфликтующий процесс или (крайний вариант) поменять порт в `docker-compose.yml`.
+- **Docker не хватает ресурсов.** Сверься с [системными требованиями](../START_HERE.md#системные-требования). Проверь отдельно память компьютера и лимит памяти Docker Desktop/WSL2: контейнерам может быть доступна лишь часть RAM хоста.
+- **После паузы виден старый вывод, но переменные пропали.** Разбери разницу между файлом ноутбука и ядром Python по [памятке учащегося](./study_workflow.md). Передай студенту ее до второго занятия; для обращения за помощью там есть список нужных сведений.
+- **Порты заняты.** Проверь 8080, 8889, 9000 и остальные [порты стенда](../START_HERE.md#порты-стенда). `docker compose ps -a` покажет, какой сервис не стартовал. Решение: остановить конфликтующий процесс или (крайний вариант) поменять порт в `docker-compose.yml`.
- **Студент не читает `START_HERE.md`.** Начинает с ноутбуков до поднятия стенда. Направь обратно к стартовому документу.
### Модуль 2. Ментальная модель
diff --git a/docs/network_access.md b/docs/network_access.md
new file mode 100644
index 0000000..a47b947
--- /dev/null
+++ b/docs/network_access.md
@@ -0,0 +1,74 @@
+# Если образы или зависимости не скачиваются
+
+Доступность репозиториев зависит от сети и провайдера, в том числе в РФ.
+GHCR выбран основным источником MinIO, чтобы его загрузка не зависела от Docker
+Hub. Это не гарантия доступности без VPN у каждого провайдера: у стенда остаются
+другие внешние зависимости. Для первой сборки и скачивания датасета нужен интернет.
+
+## Сначала найди источник ошибки
+
+Запускай команды в терминале хоста, из корня репозитория:
+
+```bash
+docker compose pull minio minio-init postgres trino
+docker compose --progress plain build
+```
+
+Если первая команда завершилась с ошибкой, сначала реши проблему загрузки образа.
+Если ошибка возникла во второй, найди имя сервиса и адрес, с которого скачивался
+файл. Открывающийся в браузере сайт ещё не подтверждает доступ из Docker.
+
+| Где возникла ошибка | Что скачивается | Что проверить |
+| --- | --- | --- |
+| `ghcr.io` при загрузке `minio` | Готовые MinIO и `mc` | Резервный источник ниже |
+| `docker.io`, `registry-1.docker.io`, `auth.docker.io` | PostgreSQL, Trino, базовый Spark; резерв MinIO | Доступ Docker к Docker Hub и настройки прокси |
+| Ubuntu archive, PyPI, Maven Central при сборке `spark` или `jupyter` | Системные пакеты, Python-пакеты и JAR | Доступ к зависимостям из сборки |
+| GitHub Releases, Open VSX при сборке `ide` | code-server и расширения | Доступ к зависимостям из сборки |
+
+## Резервный источник MinIO
+
+Автор образа публикует его в двух местах: GHCR и Docker Hub. В обоих закреплён
+один digest, поэтому смена registry не меняет версию или содержимое образа.
+Ссылка на сборку и версия приведены в [техническом reference](./stack_reference.md#minio).
+
+Если GHCR недоступен, создай файл `.env` в корне репозитория и добавь строку:
+
+```dotenv
+MINIO_REGISTRY=docker.io
+```
+
+Если файл уже существует, измени только эту настройку. Другие значения, например
+`NB_UID` и `NB_GID`, сохрани. Затем повтори:
+
+```bash
+docker compose pull minio minio-init
+```
+
+Дальше используй обычные команды из [START_HERE](../START_HERE.md#быстрый-запуск).
+Для возврата к основному источнику установи `MINIO_REGISTRY=ghcr.io`.
+Оба источника публичные; регистрация для скачивания MinIO не требуется.
+`.env.example` содержит пример настроек, а `.env` исключён из Git.
+
+## Если недоступны другие источники
+
+Переключение MinIO не меняет источник PostgreSQL, Trino, Spark или зависимостей
+сборки. Если в твоей сети нужен VPN или HTTP-прокси, важно, чтобы им мог
+пользоваться Docker, а не только браузер.
+
+- **Docker Desktop:** настрой прокси в разделе **Settings → Resources → Proxies**.
+ См. [официальную инструкцию](https://docs.docker.com/desktop/settings-and-maintenance/settings/#proxies).
+- **Docker Engine на Linux:** скачиванием образов занимается демон Docker.
+ Для него есть [отдельная настройка прокси](https://docs.docker.com/engine/daemon/proxy/).
+- **Команды внутри сборки:** `apt`, `pip`, `curl` и установка расширений требуют
+ доступа из build-контейнера. Docker описывает
+ [настройки прокси для сборок](https://docs.docker.com/engine/cli/proxy/#build-with-a-proxy-configuration)
+ отдельно от настроек демона. Прокси на `localhost` хоста не обязательно
+ доступен как `localhost` внутри контейнера.
+
+Ошибка `429` обычно означает лимит запросов, а `401`/`403` может быть связана
+с доступом к registry. Сам по себе этот код не доказывает блокировку по стране.
+Не меняй версии образов и не удаляй данные стенда для исправления сетевой ошибки.
+
+Если нужна помощь, передай ментору ОС, вывод `docker compose version`, команду,
+имя сервиса и фрагмент ошибки с адресом источника. Пароли и адреса прокси
+с учётными данными в сообщение не добавляй.
diff --git a/docs/stack_reference.md b/docs/stack_reference.md
index 3537a06..b845256 100644
--- a/docs/stack_reference.md
+++ b/docs/stack_reference.md
@@ -15,18 +15,22 @@
| MinIO API | `minio` | `9000` | S3-compatible endpoint |
| MinIO Console | `minio` | `9001` | веб-консоль бакетов и объектов |
| PostgreSQL | `postgres-iceberg` | `5432` | JDBC-каталог Iceberg |
-| JupyterLab | `jupyter` | `8888` | практические ноутбуки |
+| VS Code в браузере (code-server) | `ide` | `8889` | основной интерфейс для ноутбуков и терминала |
+| JupyterLab | `jupyter` | `8888` | запасной интерфейс для тех же ноутбуков |
## Основные команды
-Все команды выполняются из корня репозитория.
+Все команды выполняются в терминале хоста из корня репозитория.
+Для первого запуска и настройки прав используй [START_HERE](../START_HERE.md),
+для продолжения работы — [памятку учащегося](./study_workflow.md).
### Сборка и запуск
```bash
+docker compose pull minio minio-init postgres trino
docker compose build
docker compose up -d
-docker compose ps
+docker compose ps -a
```
### Логи
@@ -36,16 +40,50 @@ docker compose logs -f spark-master
docker compose logs -f trino
docker compose logs -f minio
docker compose logs -f jupyter
+docker compose logs -f ide
```
### Остановка и reset
-```bash
-docker compose down
-docker compose down -v
-```
+| Действие | Команда | Что происходит с данными |
+| --- | --- | --- |
+| Пауза до следующего занятия | `docker compose stop` | Контейнеры остановлены, файлы и volumes сохранены |
+| Продолжение | `docker compose up -d` | Сервисы запускаются с прежними данными |
+| Удаление контейнеров и сети | `docker compose down` | Volumes и файлы на хосте сохраняются, состояние внутри удаленных контейнеров теряется |
+| Полный сброс учебных данных | См. [отдельную инструкцию](./study_workflow.md#начать-с-чистых-данных) | Удаляются MinIO volume и каталог PostgreSQL |
-`down -v` удаляет volumes и возвращает стенд в чистое состояние.
+Переменные Python и живые Spark-сессии после остановки нужно создать заново.
+
+## Ресурсы и проверенная среда
+
+Актуальный бюджет для учащегося приведен в
+[системных требованиях](../START_HERE.md#системные-требования).
+Он учитывает одновременную работу двух Spark worker-ов, драйвера в IDE, Trino,
+MinIO и PostgreSQL. У каждого worker-а в Compose указаны 2 CPU и 2 ГБ для
+приложений Spark; это не предел потребления всей JVM или контейнера.
+Потребление при простое не показывает пик во время урока.
+
+Проверка 19 октября 2026 года: Linux x86_64, Docker Engine, 6 CPU и около
+11,1 ГиБ памяти, видимых Docker. Выполнены первый ноутбук в Chromium и проверка
+Spark → Iceberg → Trino. Затем Trino проверен с лимитом контейнера 2 ГиБ и heap
+1 ГиБ на трех месяцах Taxi: 9 554 778 строк bronze, 9 418 184 строк silver
+и 265 зон. Запросы Модуля 6 и дополнительный JOIN со справочником прошли;
+агрегаты и JOIN сверены со Spark. Пик памяти контейнера Trino по cgroup составил
+около 1,25 ГиБ, событий OOM и перезапусков не было.
+
+Наблюдавшийся суммарный пик памяти контейнеров без неактивного файлового кэша
+составил около 5,44 ГиБ. Это измерение выбранного сценария, а не проверка всей
+среды под общим лимитом 8 ГБ: память Docker daemon, ядра и других процессов
+Linux/WSL2 в сумму не входит. Прогона всех восьми модулей под таким лимитом
+не было. Docker Desktop и ARM64 в этой проверке не запускались.
+Методика и результаты сохранены в [отчете проверки](../plans/trino-memory-validation.md).
+
+Размеры локально собранных образов на этой машине: Spark — около 3,37 ГБ,
+Jupyter — 3,72 ГБ, IDE — 4,90 ГБ. Jupyter включает слои Spark, IDE включает слои
+Jupyter: складывать эти три числа для оценки места неправильно. IDE добавляет
+около 1,18 ГБ. IDE вместе с отдельными образами Trino, PostgreSQL и MinIO дает
+около 8,1 ГБ до учета кэша сборки, данных и особенностей хранилища Docker.
+Поэтому перед первой сборкой нужен запас места сверх размера готовых образов.
## Доступ к сервисам
@@ -72,13 +110,33 @@ docker compose down -v
Пример запуска smoke-скрипта:
```bash
-docker compose exec spark-master \
- /opt/spark/bin/spark-submit /opt/src/spark/cluster_smoke.py
+docker compose exec ide python3 /opt/src/spark/cluster_smoke.py
```
### Trino
- Web UI: `http://localhost:8090`
+- Контейнер ограничен 2 ГиБ RAM (`mem_limit: 2g`). `memswap_limit: 2g`
+ отключает swap для него: одинаковое значение задает общий предел RAM + swap.
+- В [`trino/jvm.config`](../trino/jvm.config) heap Java начинается с 512 МиБ
+ и может вырасти до 1 ГиБ (`-Xms512M`, `-Xmx1G`). Остальная память контейнера
+ оставлена для потоков, служебных структур JVM и буферов вне heap.
+
+Эти ограничения относятся к Trino; у всего Compose-проекта общего лимита
+памяти нет. Если контейнер достигнет жесткого предела, он может быть завершен
+системой. Увеличение только heap без изменения бюджета контейнера уменьшает
+запас для памяти вне heap. См. [лимиты Compose](https://docs.docker.com/reference/compose-file/services/#mem_limit)
+и [настройку JVM Trino](https://trino.io/docs/current/installation/deployment.html#jvm-config).
+
+После обновления этих файлов примени настройки из терминала хоста:
+
+```bash
+docker compose up -d --no-deps --force-recreate trino
+docker compose ps -a trino
+```
+
+Дождись `healthy`. Эта операция пересоздает только Trino и применяет в том числе
+изменения содержимого `jvm.config`; таблицы остаются в MinIO и PostgreSQL.
CLI внутри контейнера:
@@ -106,7 +164,20 @@ SHOW TABLES FROM lakehouse.default;
- логин: `minioadmin`
- пароль: `minioadmin`
-Стенд использует зафиксированные исторические образы MinIO Server и `mc` из официального namespace `quay.io/minio`. Эти версии подходят для локального курса, но больше не получают обновлений upstream и не предназначены для production.
+Стенд использует готовый образ `coollabsio/minio:RELEASE.2025-04-22T22-12-26Z`.
+Это сторонняя [сборка из официальных исходников MinIO](https://github.com/coollabsio/minio),
+которая содержит сервер и клиент `mc` версии `RELEASE.2025-08-13T08-35-41Z`.
+Версия сервера предшествует [изменению встроенной консоли в мае 2025 года](https://github.com/minio/minio/releases/tag/RELEASE.2025-05-24T17-08-30Z).
+Образ подходит для локального курса; это историческая
+версия без текущих обновлений, не production-конфигурация.
+
+В Compose закреплён digest индекса для AMD64 и ARM64:
+`sha256:a4938f37f1be1841b8e7b627ad0207b265345fd0d063e42d7410c78af0e63e68`.
+Сервисы `minio` и `minio-init` используют один образ. Компиляция MinIO и установка
+Go не нужны. По умолчанию образ скачивается из GHCR без авторизации. Автор
+публикует тот же digest в Docker Hub; резервный источник выбирается через
+`MINIO_REGISTRY=docker.io` в `.env`. Подробности — в
+[инструкции по доступу к репозиториям](./network_access.md).
Бакет `lakehouse` обычно создаётся автоматически контейнером `minio-init`.
@@ -126,6 +197,44 @@ psql -h localhost -p 5432 -U iceberg -d iceberg
docker compose run --rm iceberg-catalog-init
```
+### VS Code в браузере
+
+Открой `http://localhost:8889`. Сервис `ide` запускает code-server с установленными
+расширениями Python и Jupyter. Python, ipykernel, Java, PySpark и зависимости курса
+уже находятся в образе. Локальный Python или venv не нужен.
+
+В рабочей области доступны ноутбуки, примеры из `src/` и документация. Репозиторий
+смонтирован в `/opt/course` только для чтения, а его каталог `notebooks/` — для
+чтения и записи. Так относительные ссылки между документами и ноутбуками работают
+и внутри IDE. Пути `/opt/work`, `/opt/src`, `/opt/data` также доступны примерам курса.
+Открой
+`01_environment_and_smoke_test.ipynb`, нажми **Select Kernel → Python Environments**
+и выбери `/usr/bin/python3`. Это Python внутри контейнера IDE. Расширение Jupyter
+запускает ядро там же; адрес отдельного Jupyter Server вводить не требуется.
+
+Порт IDE опубликован только на `127.0.0.1`, без пароля, для одного пользователя.
+Открывай именно `localhost` или `127.0.0.1`: браузер считает их доверенным
+контекстом даже по HTTP. Notebook webview использует service worker; при обращении
+по обычному HTTP к IP сервера или имени в локальной сети этот механизм может
+быть недоступен. Это ограничение secure context, отдельное от CORS и XSRF.
+См. [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Secure_Contexts)
+и [webviews в code-server](https://coder.com/docs/code-server/FAQ#why-do-web-views-not-work).
+
+Для стенда на удалённой машине используй SSH-туннель:
+
+```bash
+ssh -N -L 8889:127.0.0.1:8889 user@host
+```
+
+Затем открой `http://localhost:8889` на своём компьютере. Прямой доступ по
+публичному адресу не входит в эту конфигурацию.
+
+Образы собираются в порядке `spark-master → jupyter → ide` через Compose
+`additional_contexts` (нужен Docker Compose 2.17+ и BuildKit). Версии code-server
+и расширений зафиксированы в `ide/Dockerfile`; расширения скачиваются при сборке.
+Автоматическое обновление расширений отключено, чтобы повторный запуск использовал
+проверенные версии.
+
### JupyterLab
- адрес: `http://localhost:8888`
@@ -137,6 +246,10 @@ docker compose run --rm iceberg-catalog-init
- `./data` смонтирован read-only как `/opt/data`
- `PYTHONPATH=/opt/src`
+JupyterLab остаётся запасным интерфейсом. Он редактирует те же файлы, но запускает
+отдельные ядра: переменные Python между IDE и JupyterLab не общие. Перед сменой
+интерфейса сохрани ноутбук и останови его ядро.
+
Первый ноутбук курса: `notebooks/01_environment_and_smoke_test.ipynb`.
Для Модуля 3 локально скачанный data bundle должен лежать на хосте в `./data/nyc_taxi` и будет доступен внутри Jupyter по пути `/opt/data/nyc_taxi`.
@@ -157,16 +270,39 @@ docker compose run --rm iceberg-catalog-init
| `docker-compose.yml` | состав сервисов, порты, volumes, init-контейнеры |
| `spark/spark-defaults.conf` | Spark master, catalog `lakehouse`, `s3a`, Iceberg extensions |
| `trino/catalog/lakehouse.properties` | каталог Trino `lakehouse`, JDBC и S3-доступ |
+| `trino/jvm.config` | JVM Trino: heap до 1 ГиБ внутри контейнера с лимитом 2 ГиБ |
| `spark/Dockerfile` | образ Spark с Iceberg, S3A и Python-зависимостями |
| `jupyter/Dockerfile` | образ JupyterLab на базе Spark |
+| `ide/Dockerfile` | code-server и расширения на базе образа Jupyter |
+| `ide/lakehouse.code-workspace` | папки рабочей области и настройки Python |
+| `docs/network_access.md` | источники образов и зависимостей, резервный registry и диагностика сети |
+| `docs/study_workflow.md` | повседневная работа, сохранение состояния и диагностика ошибок |
## Smoke-тесты
+### Браузерная IDE и Jupyter-ноутбук
+
+На Linux с Docker Engine, после запуска стенда:
+
+```bash
+bash tests/browser_ide.sh
+```
+
+Проверка запускает Chromium в отдельном контейнере и проходит путь студента:
+доверие рабочей области → открытие копии Модуля 1 → выбор Python → Run All →
+вывод Spark в webview → сохранение → повторное открытие. Затем проверяет, что
+все кодовые ячейки выполнились без ошибок и результат сохранился. Исходный
+ноутбук не меняется. Скриншот остаётся в каталоге `/tmp/lakehouse-browser-check.*`,
+путь печатается перед запуском. Docker Desktop этим скриптом не проверяется:
+он использует Linux host networking.
+
+Для проверки отдельного стенда можно задать `COMPOSE_FILE` и `IDE_URL`; по
+умолчанию тест использует текущий Compose-проект и `http://localhost:8889`.
+
### 1. Проверка, что Spark-кластер жив
```bash
-docker compose exec spark-master \
- /opt/spark/bin/spark-submit /opt/src/spark/cluster_smoke.py
+docker compose exec ide python3 /opt/src/spark/cluster_smoke.py
```
### 2. Проверка Spark -> Trino через общий каталог
@@ -259,6 +395,7 @@ SELECT * FROM