Развёртывание кластера: координатор, воркеры, MinIO
Архитектура спроектирована. Теперь поднимаем её в работающем виде. В этом уроке мы разворачиваем платформу RetailScope через docker-compose: object storage (MinIO), REST catalog для Iceberg, координатор и воркеры Trino, и связываем всё конфигурацией каталогов.
Почему docker-compose: он даёт воспроизводимую среду — вся топология описана одним файлом, любой член команды поднимает идентичную систему одной командой. И ровно эта топология — координатор, воркеры, каталоги — масштабируется в продакшен; меняется оркестратор (на Kubernetes), но не структура.
Состав развёртывания
Платформе нужно пять видов компонентов. Распишем, что за что отвечает.
В этом уроке поднимаем первые четыре (MinIO, REST catalog, координатор, воркеры). PostgreSQL подключим в уроке 4, когда дойдём до федеративного слоя.
docker-compose: топология
Опишем кластер. Файл docker-compose.yml:
services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minio
MINIO_ROOT_PASSWORD: minio_secret_pw
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio-data:/data
# Создаёт bucket retailscope при старте
minio-init:
image: minio/mc:latest
depends_on:
- minio
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 minio minio_secret_pw &&
mc mb --ignore-existing local/retailscope &&
echo 'bucket retailscope ready';
"
iceberg-rest:
image: apache/iceberg-rest-fixture:latest
depends_on:
- minio
ports:
- "8181:8181"
environment:
CATALOG_WAREHOUSE: s3://retailscope/warehouse
CATALOG_IO__IMPL: org.apache.iceberg.aws.s3.S3FileIO
CATALOG_S3_ENDPOINT: http://minio:9000
CATALOG_S3_PATH__STYLE__ACCESS: "true"
AWS_ACCESS_KEY_ID: minio
AWS_SECRET_ACCESS_KEY: minio_secret_pw
AWS_REGION: us-east-1
trino-coordinator:
image: trinodb/trino:481
depends_on:
- iceberg-rest
ports:
- "8080:8080"
volumes:
- ./etc-coordinator:/etc/trino
trino-worker-1:
image: trinodb/trino:481
depends_on:
- trino-coordinator
volumes:
- ./etc-worker:/etc/trino
trino-worker-2:
image: trinodb/trino:481
depends_on:
- trino-coordinator
volumes:
- ./etc-worker:/etc/trino
volumes:
minio-data:
Разберём ключевое. Образ trinodb/trino:481 — конкретная версия, не latest: воспроизводимость требует фиксации. minio-init — одноразовый контейнер, создающий bucket retailscope. iceberg-rest — REST catalog: ему задаём warehouse-путь в MinIO и креды S3-доступа. Координатор и два воркера используют один и тот же образ Trino — роль определяется не образом, а конфигурацией в смонтированных каталогах etc.
Один и тот же образ Trino работает и координатором, и воркером — разницу задаёт только файл config.properties. Это прямое следствие архитектуры Trino: координатор и воркер — это роли одного бинарника. Поэтому масштабировать кластер означает добавить контейнеры с воркерской конфигурацией, и ничего больше.
Конфигурация координатора
Координатору в каталог etc-coordinator нужны четыре файла плюс каталоги.
etc-coordinator/config.properties — роль и параметры сервера:
coordinator=true
node-scheduler.include-coordinator=false
http-server.http.port=8080
discovery.uri=http://trino-coordinator:8080
query.max-memory=8GB
query.max-memory-per-node=2GB
coordinator=true назначает роль. node-scheduler.include-coordinator=false запрещает координатору обрабатывать данные — он только планирует, исполнение целиком на воркерах. discovery.uri — адрес discovery service (он встроен в координатор), по нему воркеры регистрируются при старте.
etc-coordinator/node.properties — идентичность ноды:
node.environment=retailscope
node.id=coordinator
node.data-dir=/data/trino
node.environment обязан совпадать у всех нод кластера; node.id уникален.
etc-coordinator/jvm.config — опции JVM:
-server
-Xmx4G
-XX:+UseG1GC
-XX:+ExitOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
В продакшене heap задают в десятки гигабайт; для локального проекта 4 GB достаточно. -XX:+ExitOnOutOfMemoryError — чтобы упавшая по памяти нода честно остановилась, а не работала в неопределённом состоянии.
Файл etc-coordinator/log.properties минимален: io.trino=INFO.
Конфигурация воркера
Воркеру в каталог etc-worker нужны те же типы файлов; отличается, по сути, только config.properties:
coordinator=false
http-server.http.port=8080
discovery.uri=http://trino-coordinator:8080
query.max-memory=8GB
query.max-memory-per-node=2GB
coordinator=false — единственная строка, которая делает ноду воркером. discovery.uri тот же — воркер по нему находит координатор и регистрируется. node.properties воркера повторяет node.environment=retailscope (обязательно тот же), а node.id — свой уникальный; на практике id воркеру удобно подставлять из переменной окружения, чтобы каждый контейнер получал свой. jvm.config аналогичен координаторскому.
Каталоги: подключение источников
Каталоги — это файлы .properties в etc/catalog, по файлу на источник. Они должны лежать у каждой ноды (и у координатора, и у воркеров), потому что воркерам каталоги нужны для чтения данных.
Главный каталог проекта — Iceberg. Файл etc-coordinator/catalog/iceberg.properties (и такой же в etc-worker/catalog):
connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=http://iceberg-rest:8181
iceberg.rest-catalog.warehouse=s3://retailscope/warehouse
fs.native-s3.enabled=true
s3.endpoint=http://minio:9000
s3.region=us-east-1
s3.path-style-access=true
s3.aws-access-key=minio
s3.aws-secret-key=minio_secret_pw
Здесь видна вся связка слоёв. iceberg.catalog.type=rest с iceberg.rest-catalog.uri говорит: каталог метаданных — REST-сервис. fs.native-s3.enabled=true включает нативную S3-файловую систему Trino (она заменила старый Hadoop-слой, удалённый в релизе 481). s3.endpoint и креды направляют Trino в MinIO; s3.path-style-access=true обязателен для MinIO. Полезно сразу включить и filesystem caching из урока модуля 15 — fs.cache.enabled=true с путём и лимитом, — но это можно сделать и на этапе тюнинга.
Удобно добавить для проверки и встроенный etc/catalog/tpch.properties с connector.name=tpch — он даст готовые тестовые данные без всякой загрузки.
Обратите внимание на принципиальную деталь: каталог Iceberg сам по себе ничего не «создаёт». Файл iceberg.properties — это лишь описание того, как Trino добирается до данных: к какому REST catalog обращаться за метаданными и в каком object storage лежат файлы. Пока в каталоге нет ни одной схемы и таблицы, он всё равно валиден — это пустое, но рабочее подключение. Таблицы появятся в уроке 3, когда мы начнём грузить данные. Такое разделение «каталог — это конфигурация подключения, а не данные» — фундаментальное для модели Trino: один и тот же object storage можно подключить разными каталогами с разными настройками, и наоборот, сменив креды в одном файле, переключить весь кластер на другое хранилище.
Почему именно такая топология
Стоит отдельно понять, почему в развёртывании RetailScope ровно эти компоненты и ровно в таком составе, а не, скажем, всё в одном контейнере.
MinIO и Trino — раздельно. Это материализация принципа разделения хранения и вычислений. Хранилище живёт своей жизнью: его можно наполнять данными, бэкапить, масштабировать по объёму — независимо от кластера Trino. Кластер Trino можно гасить на ночь, пересоздавать, менять число воркеров — данные в MinIO от этого не страдают. В продакшене MinIO заменяется на управляемый S3, и архитектура не меняется ни на строчку — меняется только endpoint в iceberg.properties.
REST catalog — отдельный сервис. Iceberg-таблице нужен компонент, который хранит указатель на её текущую версию (на актуальный файл метаданных). Этот компонент — catalog — намеренно вынесен из Trino: к одному и тому же REST catalog могут обращаться и Trino, и Spark, и любой другой движок, и все они увидят одни и те же таблицы консистентно. Если бы catalog был частью Trino, lakehouse оказался бы заперт внутри одного движка — а открытость как раз была требованием проекта.
Два воркера, а не один. Один воркер технически работал бы, но не показал бы сути: Trino — это MPP-движок, и параллелизм между несколькими нодами — его смысл существования. Два воркера в учебной среде позволяют наблюдать, как стадия распараллеливается на задачи на разных нодах, как данные обмениваются между ними. В продакшене воркеров десятки, но модель та же.
Этот поток — то, ради чего разворачивался кластер. Координатор не трогает данные, он только планирует; воркеры читают из MinIO и считают; REST catalog отвечает на вопрос «где метаданные таблицы». Каждый компонент делает ровно одну вещь.
Проверка: кластер жив
После docker compose up -d платформу проверяют тремя шагами.
# 1. Web UI на координаторе: видны ли воркеры
# Откройте http://localhost:8080 — Active Workers должно показать 2
# 2. Подключиться CLI и убедиться, что каталоги видны
docker compose exec trino-coordinator trino
trino> SHOW CATALOGS;
-- Catalog
-- ---------
-- iceberg
-- system
-- tpch
# 3. Проверить, что Iceberg-каталог отвечает (через него же — REST catalog и MinIO)
trino> SHOW SCHEMAS FROM iceberg;
trino> CREATE SCHEMA iceberg.retail;
-- CREATE SCHEMA
Если SHOW CATALOGS показывает iceberg, а CREATE SCHEMA iceberg.retail проходит без ошибки — значит работает вся цепочка: координатор принял запрос, обратился к REST catalog, тот записал метаданные схемы в MinIO. Платформа развёрнута и готова принимать данные.
В этом проекте креды (minio_secret_pw, ключи S3) записаны в конфигурацию открытым текстом — это допустимо только для локальной обучающей среды. В продакшене секреты не хранят в .properties-файлах: Trino поддерживает механизм secrets и подстановку чувствительных значений из переменных окружения. Безопасности развёртывания посвящён урок 6 этого модуля.
Попробуй сам
Разверните платформу RetailScope у себя.
- Создайте
docker-compose.ymlи каталогиetc-coordinatorиetc-workerс файлами конфигурации из урока. Запуститеdocker compose up -d. - Откройте Web UI на
http://localhost:8080и убедитесь, что Active Workers равно 2. Если воркеров не видно — проверьтеdiscovery.uriиnode.environment(он обязан совпадать у всех нод). - Подключитесь CLI и выполните
SHOW CATALOGS. Видны лиicebergиtpch? - Выполните
CREATE SCHEMA iceberg.retail, затем загляните в консоль MinIO наhttp://localhost:9001— появилась ли структура в bucketretailscope? - Эксперимент на масштабирование: добавьте в
docker-compose.ymlсервисtrino-worker-3с той же воркерской конфигурацией, выполнитеdocker compose up -dи проверьте в Web UI, что воркеров стало 3. Что вы для этого изменили, кроме добавления контейнера?
Цель — получить работающий кластер и на практике убедиться, что добавление воркера — это только добавление контейнера с воркерской конфигурацией.