Discovery service: как ноды находят друг друга
В предыдущих уроках модуля мы разобрали обе роли: координатор дирижирует, воркеры обрабатывают данные. Но между ними есть пропущенный шаг, без которого ничего не работает. Координатор раздаёт работу воркерам — но откуда он вообще знает, что эти воркеры существуют? Какие воркеры сейчас в кластере, по каким адресам, живы ли они? Воркер при старте — это просто процесс на какой-то машине; он сам по себе никак не связан с координатором.
Связывает их discovery service — сервис обнаружения. Это механизм, благодаря которому узлы кластера находят друг друга. Урок короткий, но важный: discovery — это то, что превращает набор отдельных процессов на разных машинах в единый кластер. Без понимания discovery непонятно, как кластер вообще собирается воедино и почему добавить воркер так просто.
Проблема: узлы не знают друг о друге
Представьте момент запуска кластера. Вы стартуете процесс координатора на одной машине и процессы воркеров на других. На этом этапе это просто несколько независимых программ. Координатор не знает, сколько воркеров запущено и где они. Воркеры не знают, где координатор. Никакого «кластера» ещё нет — есть россыпь процессов.
Чтобы кластер заработал, нужно решить две связанные задачи. Во-первых, воркеры должны заявить о себе: «я воркер, я жив, я по такому-то адресу, дайте мне работу». Во-вторых, координатор должен иметь актуальный список всех живых воркеров, чтобы знать, кому раздавать задачи. И этот список должен обновляться: воркеры приходят и уходят, и координатор должен это замечать.
Именно эти две задачи — регистрацию узлов и поддержание актуального списка — решает discovery service.
Discovery service встроен в координатор
В Trino discovery service не отдельный сервер, который надо разворачивать и поддерживать. Он встроен в координатор. Координатор, помимо парсинга и планирования, выполняет ещё и роль точки регистрации для воркеров. Это упрощает архитектуру: не нужно поднимать отдельный компонент, координатор и так единственная центральная точка кластера, и логично, чтобы воркеры регистрировались именно у него.
Из-за этого discovery.uri в конфигурации каждого узла указывает на координатор. Вспомните файл etc/config.properties из прошлых уроков:
coordinator=false
http-server.http.port=8080
discovery.uri=http://trino-coordinator:8080
Строка discovery.uri=http://trino-coordinator:8080 сообщает воркеру: «сервис обнаружения — здесь». Поскольку discovery встроен в координатор, это адрес координатора. Тот же discovery.uri прописан и в конфиге самого координатора — он указывает сам на себя. Это и есть единственная привязка, которая нужна воркеру, чтобы найти кластер: один URL.
Раньше в экосистеме Presto discovery service можно было запускать как отдельный сервис. В современном Trino это поглощено координатором: отдельный discovery-сервер разворачивать не нужно. Для вас как инженера это значит, что настройка кластера предельно простая — всем узлам достаточно знать один адрес координатора.
Как воркер регистрируется
Разберём механику по шагам — что происходит, когда воркер присоединяется к кластеру.
Шаг 1. Воркер стартует и читает конфигурацию. Из etc/config.properties он узнаёт discovery.uri — адрес координатора, и http-server.http.port — порт, на котором он сам будет доступен.
Шаг 2. Воркер регистрируется на координаторе. Воркер обращается по discovery.uri к discovery service (то есть к координатору) и сообщает о себе: свой уникальный идентификатор, свой сетевой адрес, свою версию. По сути воркер говорит: «я существую, я по такому-то адресу, я готов к работе».
Шаг 3. Координатор добавляет воркер в список живых узлов. Получив регистрацию, координатор заносит воркер в свой реестр кластера. С этого момента координатор знает про воркер и может назначать ему задачи при планировании запросов.
Шаг 4. Воркер продолжает подтверждать, что он жив. Регистрация — не одноразовое событие. Воркер периодически даёт о себе знать, а координатор отслеживает, кто на связи. Если воркер перестаёт отвечать (упал, потерял сеть), координатор через некоторое время убирает его из списка живых узлов и больше не раздаёт ему работу. Так список воркеров всегда остаётся актуальным.
Почему это делает кластер эластичным
Discovery — это не просто служебная деталь, а механизм, который делает Trino-кластер эластичным. Понять это важно для эксплуатации.
Поскольку воркер сам регистрируется при старте, добавление воркера тривиально. Не нужно где-то прописывать список воркеров, не нужно перезапускать координатор, не нужно ничего перенастраивать. Вы просто запускаете новый процесс воркера с правильным discovery.uri — и он сам приходит, регистрируется, и координатор начинает давать ему работу. Кластер расширился без вмешательства в существующие узлы.
Симметрично работает и вывод воркера. Воркер остановили — он перестаёт подтверждать жизнь — координатор убирает его из списка живых узлов и больше не назначает ему задачи. Кластер сжался сам.
Именно discovery делает возможным автомасштабирование, о котором мы говорили в уроке про воркеров: в Kubernetes или в облаке оркестратор может поднимать и гасить воркеры под нагрузку, а discovery автоматически встраивает их в кластер или выводит из него. Без discovery каждое изменение размера кластера было бы ручной операцией с перенастройкой.
Два способа вывести воркер
Стоит различать два сценария исчезновения воркера, потому что discovery обрабатывает их по-разному, и для эксплуатации это важно.
Первый — внезапная потеря: воркер упал, машина выключилась, пропала сеть. Воркер ничего не успел сообщить, он просто перестал подтверждать жизнь. Discovery замечает это не мгновенно: координатору нужно какое-то время, чтобы убедиться, что узел действительно замолчал, а не задержался с ответом. Только после этого воркер убирается из списка живых узлов. И, как мы знаем из stateless-природы Trino, если на этом воркере исполнялись задачи, их промежуточные данные потеряны — а значит, по умолчанию пострадают запросы, которые на нём работали.
Второй — плановый вывод: воркер выключают намеренно, например при сокращении кластера или обновлении. Тут грубо «убивать» процесс нежелательно — это та же внезапная потеря со сломанными запросами. Поэтому у Trino есть механизм graceful shutdown: воркеру можно сообщить, что он выводится, и он перестанет брать новые задачи, доработает уже идущие до конца, и только потом остановится. Координатор за это время перестаёт назначать на него работу. Так узел уходит из кластера, не уронив ни одного запроса. Подробно graceful shutdown разбирается в модуле про деплой; здесь важно понять принцип: плановый вывод и аварийная потеря — разные вещи, и аккуратная эксплуатация использует именно плановый путь.
Практическое правило: автоскейлер, который сокращает кластер, должен выводить воркеры через graceful shutdown, а не просто гасить контейнеры. Иначе каждое сокращение кластера будет ронять часть выполняющихся запросов. В Helm-чартах и операторах Trino для Kubernetes graceful shutdown обычно уже учтён в логике остановки пода.
Самая частая ошибка при ручной сборке кластера — неверный или недоступный discovery.uri на воркере. Если воркер не может достучаться до координатора по этому адресу (опечатка, неправильный порт, сетевой барьер между контейнерами), он не зарегистрируется и просто не появится в кластере, хотя процесс при этом работает. Если запущенный воркер не виден в кластере — первым делом проверяйте discovery.uri и сетевую доступность координатора с машины воркера.
Как посмотреть, кто в кластере
Актуальный список узлов кластера — тот самый реестр, который ведёт discovery, — доступен через системный каталог. Запрос:
SELECT node_id, http_uri, node_version, coordinator, state
FROM system.runtime.nodes;
node_id | http_uri | node_version | coordinator | state
------------------------+-------------------------+--------------+-------------+--------
trino-coordinator | http://172.20.0.2:8080 | 481 | true | active
trino-worker-1 | http://172.20.0.3:8080 | 481 | false | active
trino-worker-2 | http://172.20.0.4:8080 | 481 | false | active
(3 rows)
Каждая строка — узел, который discovery знает как живой. Столбец coordinator отличает координатор от воркеров, state показывает состояние узла, node_version — версию Trino. Это и есть прямое окно в результат работы discovery: всё, что вы здесь видите, — узлы, успешно зарегистрировавшиеся в кластере. То же самое отражает и Web UI координатора, показывая число активных воркеров.
Попробуй сам
На своём локальном Trino (одиночный контейнер из модуля 0) выполните запрос к system.runtime.nodes и убедитесь, что видите один узел — он же координатор, он же discovery service.
Затем, если вы поднимали многоузловой кластер в задании к прошлому уроку, проделайте эксперимент. Выполните system.runtime.nodes — запомните число узлов. Остановите один контейнер-воркер (docker stop). Подождите немного и снова выполните system.runtime.nodes — узел должен исчезнуть из списка: discovery заметил, что воркер замолчал. Запустите контейнер обратно (docker start), подождите, снова сделайте запрос — узел вернулся в список сам, без всякой перенастройки координатора. Запишите своими словами, что именно вы пронаблюдали и как это связано с эластичностью кластера.