# Отказоустойчивость на двух нодах Схема: две машины с локальным инстансом, общая внешняя PostgreSQL и балансировщик заказчика перед HTTP. Каждая нода — самостоятельный стек Docker Compose; общее у них только состояние в базе. Порядок такой: сначала [внешняя PostgreSQL](external-db.md), затем первая нода как обычная установка, затем вторая — с теми же секретами и адресами. ## Состав схемы | Компонент | Роль в схеме | | --- | --- | | Внешняя PostgreSQL | Единственное общее состояние: пользователи, ресурсы, сессии панели, аренда лидера RADIUS | | Балансировщик заказчика | HTTP к панели и API, TLS, публичный адрес | | Нода A и нода B | Одинаковые стеки с разными `INSTANCE_ID` | | RADIUS-клиенты (NAS) | Обращаются к нодам напрямую: primary — host-A, secondary — host-B | Балансировать UDP RADIUS обычно нечем, поэтому отказоустойчивость для него делается на стороне NAS: в настройках RADIUS-клиента прописывают два сервера — `host-A:1812` первым и `host-B:1812` вторым. ## Установка второй ноды 1. Скопируйте каталог дистрибутива того же релиза на вторую машину. 2. Перенесите с первой ноды совпадающие секреты и адреса — таблица ниже. Новые `SECRET_KEY` и `API_KEY_*` **не генерировать**. 3. Задайте уникальный `INSTANCE_ID`, например `node-b`. 4. Укажите тот же режим внешней БД и те же реквизиты подключения. 5. Поставьте `RADIUS_UDP_BINDING=all`. 6. Если HTTP идёт только через балансировщик — ставьте без nginx (`--tls-mode none`) и публикуйте панель и API портами; см. [Внешний reverse proxy](reverse-proxy.md). 7. Убедитесь, что на каждой ноде свой `COMPOSE_FILE` с оверлеем публикации RADIUS. 8. Запустите `./install.sh` (или подготовьте `.env` вручную и выполните `./update.sh`). Миграции на второй ноде выполнять не нужно: схема уже применена с первой, Flyway отработает вхолостую. ### Переменные, которые должны совпадать | Переменная | Зачем | | --- | --- | | `API_KEY_ENCRYPTION_KEY`, `API_KEY_PEPPER` | Расшифровка ключей ресурсов и HMAC. С разными значениями вторая нода не прочитает то, что записала первая | | `SECRET_KEY` | Сессии и CSRF панели. Сессии хранятся в базе, поэтому «липкие» сессии на балансировщике не нужны | | `POSTGRES_*` или `DATABASE_URL`, `POSTGRES_SSL_*` | Одна база на обе ноды | | `LOCAL_PANEL_URL`, `CSRF_TRUSTED_ORIGINS`, `ALLOWED_HOSTS` | Публичный адрес панели за балансировщиком | | `LOCAL_URL` | Обычно `http://local-backend:8080` — внутри стека своей ноды | | `RADIUS_UDP_BINDING`, `RADIUS_LEADER_*` | Одинаковая политика UDP на обеих нодах | | `DISTRIBUTED_STATE` | Задан в compose значением `postgres`. На `memory` не менять: ноды перестанут видеть общее состояние | | Теги образов | Желательно одинаковые версии | Различаются: `INSTANCE_ID`, при необходимости — порты на хосте и имена compose-проектов. !!! warning "Новые секреты шифрования ломают вторую ноду" `SECRET_KEY` и `API_KEY_*` переносятся с первой ноды как есть. Если сгенерировать их заново, ключи ресурсов, записанные первой нодой, второй нодой не расшифруются. ## RADIUS на двух нодах Режим слушателя задаётся переменной `RADIUS_UDP_BINDING`: - **`leader`** — UDP слушает только владелец аренды. Значение по умолчанию, рассчитанное на один хост. - **`all`** — слушают обе ноды. Этот вариант и нужен для схемы с primary и secondary на стороне NAS. Аренду лидера настраивают `RADIUS_LEADER_LEASE_TTL_SECONDS` (по умолчанию 30) и `RADIUS_LEADER_RENEW_INTERVAL_MS` (по умолчанию 10000). На каждой ноде RADIUS публикуется своим оверлеем `docker-compose.publish-radius.yml` — он должен быть в `COMPOSE_FILE` обеих нод. Отдельные compose-проекты на разных машинах между собой не конфликтуют. !!! note "Не путать с репликами" Ограничение «не публиковать RADIUS при нескольких репликах» относится к `--scale` внутри одного хоста, а не к двум машинам. См. [Несколько реплик local-backend](replicas.md). ## Обновление Обновляйте ноды по одной: ```bash # на ноде A ./update.sh # убедиться, что нода поднялась, затем на ноде B ./update.sh ``` Миграции применяет первая обновлённая нода, вторая получает готовую схему. Одновременный запуск на пустой базе безопасен за счёт таблицы блокировок Flyway, но при обновлении рабочего контура последовательность даёт предсказуемый результат. ## Проверка `./doctor.sh` запускается на каждой ноде отдельно и проверяет её собственный стек: контейнеры, связь с базой, доступ к облаку, публикацию RADIUS. Отчёт с обеих нод можно прислать в поддержку целиком. Дополнительно проверьте на своей стороне: - панель открывается через балансировщик и вход проходит независимо от того, на какую ноду попал запрос; - вход по RADIUS проходит при остановленной ноде A — NAS должен уйти на secondary; - `INSTANCE_ID` у нод разные: их видно в журналах и в [«Истории ошибок»](../../admin/error-history.md). ## Резервное копирование База общая и копируется один раз — по правилам вашей СУБД. Файлы настроек сохраняются с каждой ноды отдельно: у нод разный `INSTANCE_ID`. Что входит в копию и как её восстановить — [«Резервное копирование и восстановление»](backup.md). ## Смотрите также - [Внешняя PostgreSQL](external-db.md) - [Резервное копирование и восстановление](backup.md) - [Внешний reverse proxy](reverse-proxy.md) - [Установка локального инстанса](../local-instance.md)