Learning Platform
Глоссарий Troubleshooting
Урок 17.02 · 24 мин
Продвинутый
deploymentdocker-composeminioconfiguration

Развёртывание кластера: координатор, воркеры, MinIO

Архитектура спроектирована. Теперь поднимаем её в работающем виде. В этом уроке мы разворачиваем платформу RetailScope через docker-compose: object storage (MinIO), REST catalog для Iceberg, координатор и воркеры Trino, и связываем всё конфигурацией каталогов.

Docker Compose: многосервисные стенды — именно то, что здесь делаем

Почему docker-compose: он даёт воспроизводимую среду — вся топология описана одним файлом, любой член команды поднимает идентичную систему одной командой. И ровно эта топология — координатор, воркеры, каталоги — масштабируется в продакшен; меняется оркестратор (на Kubernetes), но не структура.


Состав развёртывания

Платформе нужно пять видов компонентов. Распишем, что за что отвечает.

Компоненты развёртывания RetailScope
MinIOS3-совместимое object storage. Хранит файлы данных и метаданных Iceberg-таблиц. В проде заменяется на S3 или другое S3-совместимое хранилище.
REST catalogСервис каталога Iceberg. Хранит указатель на текущую версию каждой таблицы. Trino спрашивает у него, где метаданные таблицы.
CoordinatorКоординатор Trino: точка входа для клиентов, парсинг и планирование запросов, управление воркерами, Web UI. Данные сам не обрабатывает.
WorkersВоркеры Trino: исполняют задачи, читают данные из MinIO и PostgreSQL, обмениваются промежуточными результатами.
PostgreSQLОперационная база со справочниками. Подключается в уроке 4 как federated-источник.

В этом уроке поднимаем первые четыре (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.

NOTE

Один и тот же образ 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 аналогичен координаторскому.

Старт кластера: воркеры регистрируются через discovery
Воркеры стартуютКаждый воркер при запуске читает discovery.uri из config.properties — адрес координатора.
register по discovery.uri
Discovery serviceВстроен в координатор. Принимает регистрацию воркеров — так координатор узнаёт состав кластера.
кластер готов
Координатор раздаёт работуЗная живых воркеров, координатор может планировать запросы и назначать им задачи.

Каталоги: подключение источников

Каталоги — это файлы .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-движок, и параллелизм между несколькими нодами — его смысл существования. Два воркера в учебной среде позволяют наблюдать, как стадия распараллеливается на задачи на разных нодах, как данные обмениваются между ними. В продакшене воркеров десятки, но модель та же.

Поток одного запроса через развёрнутый кластер
Клиент шлёт SQLCLI, JDBC или BI-инструмент отправляет SQL-запрос на координатор по HTTP.
парсинг и планирование
Координатор планируетКоординатор парсит SQL, строит распределённый план, обращается к REST catalog за метаданными таблиц.
задачи на воркеры
Воркеры исполняютВоркеры получают задачи, читают файлы данных из MinIO, обрабатывают их и обмениваются промежуточными результатами.
результат
Результат клиентуКоординатор собирает результат с воркеров и отдаёт его клиенту порциями через protocol API.

Этот поток — то, ради чего разворачивался кластер. Координатор не трогает данные, он только планирует; воркеры читают из 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. Платформа развёрнута и готова принимать данные.

WARNING

В этом проекте креды (minio_secret_pw, ключи S3) записаны в конфигурацию открытым текстом — это допустимо только для локальной обучающей среды. В продакшене секреты не хранят в .properties-файлах: Trino поддерживает механизм secrets и подстановку чувствительных значений из переменных окружения. Безопасности развёртывания посвящён урок 6 этого модуля.


Попробуй сам

Разверните платформу RetailScope у себя.

  1. Создайте docker-compose.yml и каталоги etc-coordinator и etc-worker с файлами конфигурации из урока. Запустите docker compose up -d.
  2. Откройте Web UI на http://localhost:8080 и убедитесь, что Active Workers равно 2. Если воркеров не видно — проверьте discovery.uri и node.environment (он обязан совпадать у всех нод).
  3. Подключитесь CLI и выполните SHOW CATALOGS. Видны ли iceberg и tpch?
  4. Выполните CREATE SCHEMA iceberg.retail, затем загляните в консоль MinIO на http://localhost:9001 — появилась ли структура в bucket retailscope?
  5. Эксперимент на масштабирование: добавьте в docker-compose.yml сервис trino-worker-3 с той же воркерской конфигурацией, выполните docker compose up -d и проверьте в Web UI, что воркеров стало 3. Что вы для этого изменили, кроме добавления контейнера?

Цель — получить работающий кластер и на практике убедиться, что добавление воркера — это только добавление контейнера с воркерской конфигурацией.


Проверка знанийKnowledge check
В развёртывании RetailScope координатор и оба воркера запускаются из одного и того же docker-образа trinodb/trino:481. Что именно делает одну ноду координатором, а другую воркером, и почему из этого следует, что масштабирование кластера тривиально?
ОтветAnswer
Одну ноду от другой отличает не образ, а конфигурация — конкретно файл config.properties в смонтированном каталоге etc. Координатор и воркер в Trino — это две роли одного бинарника, и роль выбирается строкой coordinator=true или coordinator=false. У координатора стоит coordinator=true, обычно вместе с node-scheduler.include-coordinator=false, чтобы он только парсил и планировал запросы и управлял воркерами, не обрабатывая данные сам; у него же работает встроенный discovery service. У воркера стоит coordinator=false, и его задача — исполнять задачи и обрабатывать данные. Связывает их параметр discovery.uri: он одинаков у всех нод и указывает на координатор, поэтому каждый воркер при старте находит координатор и регистрируется в его discovery service — так координатор узнаёт актуальный состав кластера. Ещё одно общее обязательное условие — node.environment должен совпадать у всех нод, иначе они не считаются одним кластером, при этом node.id у каждой ноды уникален. Из этого устройства прямо следует, что масштабирование тривиально: чтобы добавить мощности, достаточно запустить ещё один контейнер из того же образа с воркерской конфигурацией — тем же config.properties с coordinator=false и тем же discovery.uri. Новый воркер сам зарегистрируется в discovery service, и координатор немедленно начнёт раздавать ему задачи. Не нужно ни менять образ, ни перенастраивать координатор, ни перезапускать кластер — добавление воркера сводится к добавлению одного одинаково сконфигурированного контейнера, и ровно эта же модель переносится в продакшен на Kubernetes, где воркеры масштабируются как реплики одного деплоймента.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. В развёртывании RetailScope координатор и воркеры используют один docker-образ trinodb/trino:481. Что делает ноду координатором, а не воркером?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6