Группа central на нескольких серверах¶
Эта страница описывает резервирование управляющего контура Agora: два и более инстанса central над одной базой PostgreSQL и отдельные стримеры. Она содержит конфигурацию инстансов, настройку адресов группы и проверки после развёртывания.
Здесь описан парк из независимых серверов — по машине на инстанс. Попробовать то же самое на одном ноутбуке, не арендуя серверов, можно по практикуму в Docker: там та же группа собирается из контейнеров за несколько минут.
Что именно разворачивается¶
Инсталляция с резервом состоит из четырёх частей, и только одна из них — сама Agora:
- Один кластер PostgreSQL, доступный всем инстансам по одному адресу. Это единственное хранилище состояния и единственная часть, отказоустойчивость которой Agora на себя не берёт.
- Два и более инстанса central — отдельные машины с одинаковым конфигом, отличающиеся ролями и объявленным адресом.
- Стримеры — отдельные машины, которым задан не адрес одного инстанса, а список адресов группы.
- Точка входа оператора — один адрес, за которым живой инстанс: DNS-имя с переключением или балансировщик.
Правило, с которого начинается такая инсталляция: central и вещание живут на разных машинах. Локальный стример внутри процесса central здесь не годится — он разделяет судьбу процесса, и его стримы падают вместе с управлением.
Сколько чего нужно¶
| Часть | Сколько | Замечание |
|---|---|---|
| PostgreSQL | Один логический кластер | Два независимых сервера базы — это две разные инсталляции, а не резерв |
| Инстансы central | Два и больше | Третий добавляется тем же конфигом; переписывать существующие не нужно |
| Стримеры | Сколько требует эфир | Резервируются не здесь, а размещением |
| Точка входа оператора | Один адрес | Стримеры через него не ходят |
Конфиг инстанса central¶
Сохраните конфигурацию каждого инстанса в /etc/agora/agora.yaml.d/60-central-group.conf. Для управляющего процесса добавьте в этот файл секцию agora: {}. Адрес общей базы в DATABASE_URL файла /etc/agora/secrets.env, если он там задан, должен совпадать с central.database_url. На инстансах central не задавайте CENTRAL_URL и JOIN_TOKEN: эти переменные предназначены для стримеров.
Инстансы группы отличаются друг от друга ровно двумя строками — объявленным адресом и набором ролей. Всё остальное у них одинаково, включая строку подключения к базе.
Первый инстанс:
listeners:
http:
- port: 80
central:
database_url: postgres://central:пароль@db.example.com:5432/central
admin_keys:
- ключ-управления
roles: [run, migrate, layouter, stats]
advertise_url: http://central-a.example.com
agora: {}
api_auth:
login: admin
password: пароль-оператора
Второй — то же самое без роли migrate и со своим адресом:
central:
database_url: postgres://central:пароль@db.example.com:5432/central
admin_keys:
- ключ-управления
roles: [run, layouter, stats]
advertise_url: http://central-b.example.com
Роли — это притязания, а не назначения¶
Роль в конфиге означает «эта машина готова делать такую работу», а не «эта машина её делает». Кто из инстансов действительно исполняет работу, решает PostgreSQL, а не конфиг, — поэтому layouter и stats заявлены у обоих, и это не ошибка и не двойной запуск.
run— обслуживать запросы: консоль, management API, приём синхронизации со стримеров, выдачу метрик. Нужна каждому инстансу.migrate— накатить миграции схемы при старте.layouter— исполнять прогоны размещения.stats— держать картину «прямо сейчас» по всему кластеру.
Единственная роль, которую нельзя оставлять у всех, — migrate. Она не защищена мандатом: два процесса, стартовавшие одновременно, погонят миграции наперегонки. Поэтому схему накатывает один инстанс, а остальные стартуют после него.
Это же — причина не оставлять roles пустым на втором инстансе: пустой список означает не «никаких ролей», а все четыре, включая migrate.
Объявленный адрес¶
advertise_url — адрес, по которому до этой машины достучатся соседи по группе. Он нужен потому, что картину «прямо сейчас» держит один инстанс, а остальные ходят к ней по сети: держатель записывает свой адрес, соседи читают его оттуда.
Адрес балансировщика здесь не годится — он привёл бы соседа к самому себе. Адрес обязан быть адресом конкретной машины.
Настройку можно и не задавать: инстанс без объявленного адреса полноценно работает и даже может держать картину — но соседи до неё не дойдут и скажут об этом в лог. В консоли такая строка помечена отдельно, см. проверку после развёртывания.
Лицензия¶
Лицензия нужна каждому процессу central, а не одному на группу: без неё процесс не стартует вовсе. Ключ и объявленный продукт у инстансов одинаковые.
Кто что исполняет: мандаты¶
Работу, дубль которой вреден, в группе исполняет один инстанс — тот, который взял на неё мандат. Мандат живёт в PostgreSQL и выдаётся на отдельном соединении, поэтому гибель процесса освобождает его сама, без разбора зависших исполнителей по таймаутам: сессия умерла — мандат свободен.
Мандатов несколько, и они независимы. Единого лидера у central нет: layouter и stats могут оказаться на разных машинах, и это штатное состояние, а не перекос.
У каждой выдачи есть номер — эпоха. Она растёт при смене держателя и едет вместе с каждой записью: инстанс, очнувшийся после паузы уже не держателем, получает отказ на записи, а не портит размещение задним числом. Поэтому переход мандата безопасен и не требует ручного подтверждения.
Смена держателя занимает секунды — столько, сколько нужно PostgreSQL, чтобы заметить смерть сессии, а соседу — чтобы прийти за освободившимся мандатом.
Инстанс, не держащий ничего, — не запасной и не спящий: он полноценно обслуживает консоль, API и синхронизацию стримеров. Мандат касается только фоновой работы.
Конфиг стримера¶
Стример обращается к группе, а не к процессу, поэтому в его конфиге список адресов:
managed_by:
name: streamer-01
url:
- http://central-a.example.com
- http://central-b.example.com
join_token: токен-присоединения
Порядок в списке — предпочтение оператора. Стример ходит на первый адрес, пока тот отвечает, переходит к следующему после неудавшегося запроса и раз в пять минут пробует первый снова: как только тот ожил, машина возвращается на него. Проба — обычный запрос синхронизации, отдельной проверки доступности нет: адрес годен тогда, когда по нему работает синхронизация.
Тот же список принимает переменная окружения CENTRAL_URL — адреса через запятую.
Стример узнаёт группу сам¶
Конфиг стримера — не единственный источник адресов. Каждый ответ на синхронизацию несёт состав группы: адреса живых инстансов, которые обслуживают стримеры. Стример дописывает их к адресам из конфига — те остаются впереди, в заданном порядке, — и хранит выученное на диске рядом с остальным состоянием машины, так что оно переживает перезапуск и работает уже при регистрации.
Отсюда следствия для эксплуатации:
- Инстанс добавляется без обхода парка. Поднятый над той же базой инстанс с
advertise_urlпопадает в состав группы за секунды, и каждый стример узнаёт о нём при следующей синхронизации. - Стример, поставленный с одним адресом, получает резерв при первом же ответе. Так к группе присоединяется существующий парк: конфиги на машинах не переписываются.
- Инстанс, переставший отмечаться в группе, выпадает из анонса через двадцать секунд, и стримеры вычёркивают его из выученных. Адреса из конфига анонс не трогает никогда — ни добавить к ним, ни убрать из них он не может.
Второй адрес в конфиге всё равно нужен машинам, которыми парк поднимается с нуля. До первого ответа стример знает о группе ровно то, что написано в его конфиге, а разрыв петли синхронизации — единственный признак живости стримера, который есть у central. Стример, потерявший свой единственный адрес до первой синхронизации, через полторы минуты числится offline, после чего лейаутер уводит его стримы на другую машину — с живой и вещающей.
Список удобно поделить по половинам парка: одной половине первым адресом задать первый инстанс, другой — второй. Тогда в спокойном состоянии нагрузка приёма синхронизации распределена, а при гибели любого инстанса на второй адрес уходит только его половина — и возвращается, когда инстанс оживёт.
Точка входа оператора¶
Оператору нужен один адрес, который сам находит живой инстанс: DNS-имя с переключением или балансировщик перед группой. Состояния у обработчика запросов своего нет, поэтому на запрос оператора отвечает любой инстанс.
Стримеры через эту точку ходить не должны: у них свой список адресов, а лишний посредник на пути синхронизации только добавит точку отказа.
Порядок развёртывания¶
Чтобы central работал отдельным управляющим процессом, без локального стримера, задайте команду запуска через override systemd. Создайте /etc/systemd/system/agora.service.d/20-central-only.conf:
[Service]
ExecStart=
ExecStart=/usr/sbin/agora central run
systemctl daemon-reload
systemctl restart agora
- Поднять PostgreSQL и создать базу.
- Запустить первый инстанс — тот, у которого есть роль
migrate. Дождаться, пока он ответит на запросы: схема накатывается до готовности. - Запустить остальные инстансы.
- Открыть окно присоединения и завести стримеры, задав каждому список адресов группы.
- Направить точку входа оператора на группу.
Пакет на машинах инстансов — agora-base, тот же, что и на стримерах: на вопрос установки о существующем PostgreSQL называется адрес общей базы, а роли и объявленный адрес задаются конфигом выше. Пакет agora здесь не нужен — он заводит базу на своей же машине, а группе она нужна отдельно от инстансов.
Порядок важен только на первом шаге: остальные инстансы не поднимутся раньше схемы, а стримеры не присоединятся раньше живого central.
Что проверить после развёртывания¶

Всё, что нужно посмотреть, лежит в консоли в разделе Кластер → Central.
- В таблице столько строк, сколько инстансов. Строка появляется через секунды после старта. Не появилась — инстанс не дошёл до базы.
- У каждой строки заполнен «Объявленный адрес». Пометка «не объявлен» означает, что соседи до этого инстанса не дойдут: ему не задан
advertise_url. - Столбец «Притязания» показывает заявленные роли. Убедитесь, что
migrateзаявлен ровно у одного. - Столбец «Держит мандаты» непустой хотя бы у одного инстанса. Пусто у всех — значит, ни один не заявил ролей
layouterиstats, и ни размещения, ни картины «прямо сейчас» в группе нет. - Столбец «Сборка» одинаков у всех строк. Разные версии в группе — штатное состояние только на время обновления.
- Раздел «Стримеры» показывает весь парк с любого инстанса. Половина парка, видимая с одного адреса, и другая половина — с другого, означают, что инстансы не видят картину друг друга: проверьте
advertise_url. - На стримере появился файл
group.jsonв каталоге состояния (/var/lib/agoraна пакетной машине) с адресами инстансов, которых нет в его конфиге. Файла нет — стример ещё не получил ни одного ответа, или у соседей не заданadvertise_url.
Проверять переключение руками не нужно и не стоит на работающей инсталляции — для этого есть практикум на одноразовом стенде.
Чего эта схема не делает¶
- Не резервирует базу. Отказоустойчивость PostgreSQL — задача самой базы: реплика с переключением или управляемый инстанс провайдера.
- Не заменяет бэкап. Резерв защищает от потери машины, но не от ошибочного удаления — см. Бэкап и восстановление.
- Не касается эфира. Стримы вещают и пишут архив без central; резервируется только управление, см. Автономия стримера.