Skip to content

Latest commit

 

History

History
169 lines (125 loc) · 13.7 KB

multi_instance.md

File metadata and controls

169 lines (125 loc) · 13.7 KB
title Несколько экземпляров панели
description Запуск нескольких экземпляров панели за балансировщиком: общая база, кэш, файлы и обмен событиями, сертификаты и типовые ошибки настройки.
layout default
lang ru
category Администрирование
order 338

Панель можно запустить в нескольких экземплярах за балансировщиком — ради отказоустойчивости или чтобы распределить нагрузку. Для этого экземпляры должны разделять состояние: базу данных, кэш, файлы и обмен событиями.

Для большинства установок это не нужно. Одна панель справляется с сотнями игровых серверов — тяжёлую работу выполняют демоны на выделенных серверах, а не панель.

Что должно быть общим

Компонент Настройка Что будет иначе
База данных PostgreSQL или MySQL SQLite не рассчитан на несколько подключённых экземпляров
Кэш CACHE_DRIVER=redis При memory сессии, ключи установки и билеты входа SSO видит только один экземпляр
Обмен событиями PUBSUB_DRIVER=redis или postgres При memory экземпляры не узнают о событиях друг друга
Файлы FILES_DRIVER=s3 При local у каждого экземпляра свои файлы, сертификаты и файлы плагинов

Нагляднее всего это видно на билетах входа SSO. POST /api/auth/sso/tickets кладёт одноразовый билет в кэш, а POST /api/auth/sso/exchange забирает его оттуда — как правило, уже на другом экземпляре, на который балансировщик направил браузер. При CACHE_DRIVER=memory обмен удастся, только если попадёт на экземпляр, выпустивший билет, поэтому SSO будет работать через раз. Билет живёт AUTH_SSO_TICKET_TTL (по умолчанию 60 секунд, не больше 120) — слишком мало, чтобы выручали повторы. См. Безопасность.

Пример конфигурации:

DATABASE_DRIVER=postgres
DATABASE_URL=postgres://gameap:пароль@db.example.com:5432/gameap?sslmode=require

CACHE_DRIVER=redis
CACHE_REDIS_ADDR=redis.example.com:6379

PUBSUB_DRIVER=redis
PUBSUB_REDIS_ADDR=redis.example.com:6379
PUBSUB_INSTANCE_ID=panel-1

FILES_DRIVER=s3
FILES_S3_ENDPOINT=https://s3.example.com
FILES_S3_BUCKET=gameap
FILES_S3_ACCESS_KEY_ID=...
FILES_S3_SECRET_ACCESS_KEY=...

AUTH_SECRET и ENCRYPTION_KEY должны быть одинаковыми на всех экземплярах: иначе токен, выданный одним, не примет другой, а зашифрованные данные окажутся нечитаемыми. С версии 4.5 это касается и секретов плагинов: таблица plugin_secrets шифруется ключом ENCRYPTION_KEY (AES-256-GCM), поэтому экземпляр с другим ключом не прочитает секрет, записанный соседом, а при PLUGINS_SECRETS_REQUIRE_ENCRYPTION=true (по умолчанию) экземпляр без ключа вообще откажется записывать секреты.

Идентификатор экземпляра

PUBSUB_INSTANCE_ID=panel-1

Значение должно быть уникальным для каждого экземпляра. По нему экземпляры отличают собственные сообщения от чужих и адресуют ответы друг другу.

Если переменную не задать, используется значение default — одинаковое у всех, и обмен между экземплярами работать не будет. Задавайте её явно.

Как это работает

Демон подключается к одному из экземпляров — к тому, на который его направил балансировщик — и держит с ним постоянное соединение. Все остальные экземпляры к этому демону обращаться напрямую не могут.

Поэтому команда, пришедшая на экземпляр, который не владеет соединением с нужным демоном, передаётся через общий обмен событиями тому экземпляру, который им владеет; ответ возвращается тем же путём. Так же работают файловые операции, консоль и запросы метрик.

Из этого следует, что общий обмен событиями обязателен: без него панель будет управлять только теми демонами, которые подключились именно к этому экземпляру.

Задачи, появившиеся в базе, пока демон был отключён или пока его соединением владел другой экземпляр, доставляются сразу при подключении, а не при следующем переподключении.

Балансировщик и порты

Разделите два потока:

  • HTTP (8025) — можно балансировать обычным образом, привязка сессии к экземпляру не нужна: токены проверяются по общему ключу, а сессионное состояние лежит в общем кэше.
  • gRPC (31718) — соединения долгоживущие. Балансировщик должен уметь их держать и не рвать по таймауту простоя: собственного механизма keepalive в канале нет, живость держится на heartbeat раз в 30 секунд.

Задайте GRPC_EXTERNAL_HOST с адресом, по которому демоны видят панель через балансировщик, и GRPC_EXTERNAL_PORT, если порт наружу опубликован под другим номером.

Сертификаты

Сертификаты gRPC должны быть общими. Они хранятся в файловом хранилище панели, поэтому при FILES_DRIVER=s3 автоматически оказываются общими для всех экземпляров.

При FILES_DRIVER=local каждый экземпляр сгенерирует собственный центр сертификации, и демон, зарегистрированный через один экземпляр, не сможет подключиться к другому.

HTTPS

Если сертификат панели выпускается через ACME, есть две особенности.

Хранилище ACME тоже лежит в файловом хранилище — при S3 оно общее.

Блокировка, не дающая нескольким экземплярам одновременно запрашивать сертификат, работает через Redis. Она включается только при CACHE_DRIVER=redis; при кэше в памяти блокировка локальная, и экземпляры будут мешать друг другу.

Подтверждение по http-01 требует, чтобы запрос центра сертификации попал на тот же экземпляр, который начал выпуск. Проще использовать dns-01 либо завершать TLS на балансировщике. См. HTTPS и сертификаты.

Ограничения

Счётчики попыток входа живут в кэше. При CACHE_DRIVER=redis они общие, при memory — свои у каждого экземпляра, то есть фактический предел умножается на число экземпляров.

Журнал аудита пишется в вывод каждого экземпляра отдельно. Сводить их нужно внешним сборщиком журналов, см. Безопасность.

Задачи плагинов дедуплицируются между экземплярами распределёнными блокировками, поэтому периодическая задача плагина выполнится один раз, а не на каждом экземпляре.

Состояние плагинов синхронизируется. Желаемое состояние — таблица plugins, и каждый экземпляр приводит себя в соответствие с ней: установка, обновление, удаление, изменение прав или перезагрузка плагина на одном экземпляре объявляется остальным через общий обмен событиями (сообщение gameap:plugin:sync) и подхватывается сразу; периодический проход раз в PLUGINS_SYNC_REFRESH_INTERVAL (по умолчанию 60 секунд) — страховка на случай потерянного сообщения. Перезагрузка увеличивает счётчик generation в записи, поэтому кнопка «Перезагрузить», нажатая на одном экземпляре, перезапускает плагин везде.

Плагин, файла которого на экземпляре нет, скачивается из каталога заново и сверяется с сохранённой контрольной суммой. Плагин, установленный из локального файла, так не восстановить — другим экземплярам негде взять файл, — поэтому таким плагинам нужно общее файловое хранилище (FILES_DRIVER=s3). Плагин, который не удалось загрузить на экземпляре, повторно загружается там с задержкой, растущей от PLUGINS_SYNC_MIN_BACKOFF (15 секунд) до PLUGINS_SYNC_MAX_BACKOFF (15 минут); плагин, отключённый средой выполнения, перезапускается по правилам PLUGINS_RECOVERY_*. PLUGINS_SYNC_DISABLED=true отключает синхронизацию — тогда изменения доходят до экземпляра только при его перезапуске. См. Справочник config.env.

Локальными для экземпляра остаются две вещи:

  • SSH-соединения, открытые плагинами через хост-библиотеку gameap-ssh, живут в памяти открывшего их экземпляра и другим не видны (PLUGINS_SSH_ENABLED по умолчанию false).
  • Кэш среды выполнения плагинов — скомпилированные модули WebAssembly — по умолчанию держится в памяти; если задан PLUGINS_RUNTIME_CACHE_DIR, это локальный путь на данном экземпляре, а не общее состояние. У каждого экземпляра должен быть свой каталог.

Проверка

После запуска убедитесь, что:

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

Если что-то из этого не работает, почти всегда причина в том, что один из четырёх общих компонентов остался локальным.