Войти

Кластер и отказоустойчивость

Несколько узлов SPZGate реплицируют друг другу историю трекера в реальном времени — без единой точки отказа и без промежуточного сервера.

Кластеризация — часть SPZGate On-Premise (см. Установка). Минимум для отказоустойчивости — два узла; при трёх и более новый узел достаточно один раз спарить с любым существующим — остальные пары построятся автоматически (см. «Автообнаружение» ниже).

Как устроена репликация

Каждый узел ведёт собственный WAL (write-ahead log) всех событий трекера. Узлы обмениваются данными напрямую друг с другом по собственному бинарному протоколу — без HTTP и без зависимости от веб-панели: сама панель лишь просит свой локальный движок выполнить операцию, а дозванивается до другого узла уже он сам.

Добавление узла в кластер

1

На первом узле: добавьте пару без кода

В панели первого узла откройте раздел ШлюзыДобавить шлюз, укажите IP второго узла и направление (обычно «оба»), поле «Код» оставьте пустым. Узел сгенерирует пару и код подтверждения — пара останется в статусе «Ожидает подтверждения», пока её не подтвердят со второй стороны.

Код подтверждения передайте администратору второго узла вне этой формы — голосом, в мессенджере — не тем же каналом, которым его можно перехватить автоматически.
2

На втором узле: добавьте пару с этим кодом

В панели второго узла — тот же раздел ШлюзыДобавить шлюз, IP первого узла и код, полученный на шаге 1. Второй узел дозванивается до первого напрямую по бинарному протоколу и подтверждает пару — оба узла переходят в статус «Активна» одновременно, без дополнительных действий на первом узле.

3

Автообнаружение остальных узлов кластера

Как только пара становится активной, оба узла «знакомят» новый узел со всеми своими остальными активными парами. Поэтому для кластера из трёх и более узлов достаточно одной ручной пары между новым узлом и любым узлом уже существующего кластера — остальные связи выстроятся сами, без похода по каждому узлу отдельно.

4

При необходимости — подтяните полную историю

Новая пара реплицирует новые события сразу, но не досылает историю, накопленную ДО момента пары. Если присоединяется узел с уже накопленными самостоятельно данными (например, поднятый из бэкапа) и историю нужно свести к одному состоянию — используйте кнопку «Синхронизировать» у нужной пары (см. ниже).

Важно: полная синхронизация переносит не только историю трекера, но и конфигурацию — домены, пользователей, списки. Она замещает данные на принимающей стороне данными узла-источника. Для двух узлов с независимо накопленной конфигурацией (а не для чистого/восстанавливаемого узла) полная синхронизация может быть разрушительной — прежде чем нажимать, убедитесь, что понимаете, какая сторона у вас источник, а какая — приёмник.

Управление репликацией и синхронизацией

Раздел Шлюзы показывает все пары этого узла, их статус и отставание от актуального состояния.

Статус
Значение
Ожидает подтверждения
Пара создана этой стороной, ждёт подтверждения с другой (см. шаг 2 выше)
Активна
Репликация идёт в обе стороны (или в одну — см. «Направление»)
Недоступен
Узел не отвечает на сетевом уровне — очередь копится, будет доставлена при восстановлении связи
Ошибка
Соединение есть, но пара отклонена (чаще всего — рассинхронизировался код подтверждения)
Отозвана
Пара помечена неактивной — см. предупреждение об «Отозвать» ниже

Рядом со статусом активной пары показано отставание — сколько записей ещё не подтверждено соседним узлом и сколько времени прошло с последнего подтверждения. Устойчивое ненулевое отставание, которое не сокращается, — повод проверить сетевую связность между узлами на порту репликации.

Синхронизировать / Синхронизировать весь кластер

Кнопка «Синхронизировать» у конкретной пары запускает полный перенос данных с узла-источника (см. предупреждение в шаге 4). «Синхронизировать весь кластер» — то же самое по очереди для каждой активной пары этого узла. Операция блокирующая и может занять продолжительное время на большой истории — не закрывайте вкладку до завершения.

Отозвать и Удалить

Обе кнопки останавливают пару, но не одинаково надёжно:

Если нужно гарантированно и окончательно остановить обмен данными с узлом — используйте «Удалить», а не «Отозвать».

SMTP-балансировщик между узлами

Отдельный компонент — балансировщик на базе HAProxy с Lua-модулем, слушающий один адрес и распределяющий входящие SMTP-сессии по нескольким узлам кластера в зависимости от домена получателя. Полезен, когда почта разных доменов физически обрабатывается разными узлами, а внешний мир должен видеть один адрес.

1

Соберите и запустите контейнер

Конфигурация балансировщика — в исходниках SPZGate, каталог docker/balancer/. Контейнер работает в режиме network_mode: host (слушает порт непосредственно на хосте, не через docker-сеть):

cd docker/balancer && docker compose up -d --build

По умолчанию слушает :2525 (порт настраивается в haproxy.conf) и разбирает SMTP-диалог до команды RCPT TO, чтобы узнать домен получателя ещё до выбора backend-узла.

2

Включите XCLIENT на принимающих узлах

Балансировщик ретранслирует сессию на выбранный узел командой XCLIENT, чтобы узел видел реальный IP отправителя, а не IP балансировщика — это нужно для корректной работы RBL-проверок, greylisting и репутационных механизмов. Backend-узлы должны принимать XCLIENT от IP балансировщика.

3

Опишите карту «домен → узлы»

Перед каждым решением балансировщик запрашивает HTTP-эндпойнт lookup (адрес задан в haproxy.conf, аргумент lua-load) и кэширует ответ на заданный TTL. Формат ответа — JSON-массив узлов с приоритетом; при недоступности узла с наивысшим приоритетом балансировщик пробует следующий:

GET /lookup?domain=example.com
[{"host":"10.0.0.11","port":12645,"priority":1},{"host":"10.0.0.12","port":12645,"priority":2}]
Текущий статус: в поставке docker/balancer/nginx-lookup.conf — статическая карта доменов для локального тестирования (несколько записей примера), а не готовый к продакшену сервис. Для реальной установки замените его на свой lookup-эндпойнт с тем же JSON-контрактом, читающий актуальную карту доменов из вашей БД или API — комментарий в самом файле прямо на это указывает.

Таймауты подключения, TTL кэша и путь к TLS-сертификату для STARTTLS передаются аргументами lua-load в haproxy.conf — там же документированы значения по умолчанию. Отдельной страницы статуса/мониторинга у балансировщика сейчас нет — состояние смотрите в логах контейнера (docker logs spz-smtp-balancer).