Mirror Manager — Help

▤ Runs ◂ Mirrors

Оглавление

Что это

Mirror Manager — источник правды по зеркалам riobet.com: какие домены есть, доступны ли они, заблокированы ли РКН, и когда нужно поднять новое зеркало вместо заблокированного.

status/weight заводятся и меняются вручную операторами прямо здесь. is_blocked ставится либо вручную через кнопку RKN-BAN, либо автоматически через uwuaff check — но только в сторону блокировки: снять is_blocked может исключительно оператор кнопкой Un-ban, автоматика этого никогда не делает.

На основе is_blocked сервис сам решает, нужно ли выпускать новое зеркало (см. Groups), и запускает для этого пайплайн automirrors-crypt в GitLab.

Дальше по цепочке: отдельный проект seo-frontend-nginx раз в час опрашивает mirrors-service (GET /api/v1/get-mirrors) и сам переключает is_active/redirect_to в своём inventory.yml, разворачивая nginx-конфиги на реальных серверах. Mirror Manager ничего не деплоит сам — он только решает и хранит статус.

Статусы зеркала (таблица Mirrors)

КолонкаЧто значитКто проставляет
status available / unavailable — включено ли зеркало в принципе. Это главный переключатель "в ротации / нет". Только вручную — через Edit или кнопку Enable/Disable.
Active Реально ли зеркало сейчас может отдавать трафик = status = available И НЕ is_blocked. Это то, что практически совпадает с is_active в inventory.yml у seo-frontend-nginx. Пересчитывается сервисом при каждом RKN-BAN/ручном изменении статуса. Отдельно не редактируется.
Blocked Заблокирован ли домен по РКН. Кнопкой RKN-BAN (оператор), либо автоматически — uwuaff check сам жмёт RKN-BAN, если находит новую блокировку. Un-ban снимает блок независимо от того, кто его поставил.
📌 (значок у статуса) Manual override — статус зафиксирован вручную оператором. Снимается кнопкой Un-pin. Ставится автоматически, когда вы реально меняете status через Edit или кнопку Enable/Disable (просто пересохранение формы без смены статуса пин не ставит).
uwuaff Чисто информационная колонка — свой собственный статус блокировки домена по данным api.uwuaff.com (см. uwuaff check). Никак не влияет на Blocked/is_blocked и автоматику Groups — это делает только RKN-BAN. Автоматически, по расписанию (раз в uwuaff_check_interval_seconds, Settings), либо вручную кнопкой "Check now" в Settings.

Как вручную вывести зеркало из ротации

  1. Найти зеркало в таблице → кнопка Disable (или Edit → Status = unavailable → Save).
  2. У статуса появится 📌 — это просто фиксирует, что статус выставлен оператором вручную. Снимается кнопкой Un-pin.
  3. Дождаться следующего часового прогона seo-frontend-nginx (или запустить его пайплайн вручную) — он увидит status=unavailable, выберет живого соседа по группе (riobet40x/riobet_thousand) как redirect_to и задеплоит редирект.

Weight

Ручной приоритет (меньше — важнее). Заводится и меняется вручную, ничем автоматически не перезаписывается.

ЗначениеСмысл
-9Основное зеркало
-5В очереди / запасное
-1Fallback, последний вариант

⛓ Groups — пулы и цепочки

Group — это политика, по которой mirrors-service сам решает, когда запускать пайплайн automirrors-crypt и выпускать новый домен. Оценивается сразу после каждого RKN-BAN.

pool — держать N доступных одновременно

Пример: группа riobet40x, target_pool_size = 8. Если из доменов с тегом riobet40x доступно (available, не заблокировано, не disabled) меньше 8 — заказывается разница (deficit), с учётом уже выпускаемых (provisioning, чтобы не заказать дважды). Новые домены берут следующий свободный номер после максимального когда-либо использованного в диапазоне number_minnumber_max — номера не переиспользуются.

chain — одно текущее зеркало, замена при блокировке

Пример: цепочка riobet2000. "Головой" считается домен с максимальным номером среди немаркированных как provisioning. Пока голова не заблокирована — ничего не происходит. Как только голову блокируют — заказывается следующий по номеру домен, а голова становится predecessor (на неё ставится redirect_to в inventory.yml при онбординге).

range_exhausted — если number_max исчерпан, а зеркало всё ещё нужно, автоматика останавливается и шлёт алерт в Slack; поднимите number_max в Groups.

Кнопка Preview plan в Groups показывает точно то же решение, что приняла бы автоматика прямо сейчас — без побочных эффектов (ничего не создаёт, пайплайн не запускает). Полезно проверить перед тем, как включать enabled или прописывать GitLab-токен.

🚫 RKN-BAN — форсировать пересборку цепочки/пула вручную

Кнопка в таблице Mirrors (напротив каждого зеркала). В отличие от Disable/Enable, она трогает не status, а is_blocked — а редирект на стороне seo-frontend-nginx ориентируется только на status. Поэтому зеркало продолжает отдаваться в выдаче как обычно, а Groups оцениваются немедленно — если зеркало было головой цепочки, преемник закажется прямо сейчас.

Тот же самый эффект (is_blocked=true + немедленная оценка Groups) может сработать автоматически — см. uwuaff check ниже, который жмёт эту кнопку сам, когда находит новую блокировку. Разница видна только в block_type (manual vs uwuaff) и в колонке Trigger на странице Runs (manual vs scheduled).

Требования те же, что и для автовыпуска: зеркало должно быть протегировано mirror_tag нужной Group, а сама Group — существовать и быть enabled. Если группы нет или тега нет — кнопка отработает, но в Runs будет issued: none.

uwuaff check — статус блокировки от api.uwuaff.com + авто-RKN-BAN

Раз в uwuaff_check_interval_seconds (по умолчанию 900с = 15 минут) сервис опрашивает GET api.uwuaff.com/v1/domains — это тот же uwuaff, куда seo-frontend-nginx регистрирует каждый домен (register-mirror-domain.yml/trigger-frontend-deploy.yml) и который сам гоняет собственные проверки на блокировку (RKN, прокси и т.д.). Результат по каждому известному домену сохраняется в колонку uwuaff.

Затем, если uwuaff нашёл новую блокировку (is_active: false) у зеркала, чьи теги пересекаются с mirror_tag какой-либо включённой Group, и которое ещё не is_blocked здесь — сервис сам жмёт RKN-BAN за оператора (block_type = 'uwuaff'): выставляет is_blocked=true и сразу оценивает Groups, заказывая преемника, если нужно. Зеркала вне какой-либо включённой Group авто-баном не трогаются — это касается и зеркал вообще без тегов, и зеркал с тегами, не связанными ни с одной Group (например, тег api у API/LB-хостов) — им просто некому заказать преемника, колонка uwuaff у них остаётся чисто информационной.

Важно про направление: это одностороннее действие — только на блокировку, никогда обратно. Ничего в uwuaff-чеке само не снимает is_blocked; это по-прежнему делает только оператор кнопкой Un-ban. Так сделано намеренно: раньше в проекте уже был похожий автоматический чек (по другому сервису, dump.allury.tech), который писал is_blocked в обе стороны — и молча снимал ручные RKN-BAN'ы, если сам не находил блокировку. Именно эта реверсия и была проблемой, от неё отказались; новый uwuaff-чек её не повторяет — он может только поставить блок, никогда не снять.

Побочный эффект одностороннего дизайна: если оператор нажмёт Un-ban на зеркале, которое uwuaff всё ещё считает заблокированным, следующий плановый чек (до 15 минут) снова поставит is_blocked=true. Чтобы бан не возвращался — либо дождитесь, пока uwuaff сам перестанет считать домен заблокированным, либо временно выключите чек в Settings.

Каждый авто-бан пишет отдельную запись в job rkn_ban (trigger = scheduled) на странице Runs — как и ручной RKN-BAN, только с другим trigger. Сам опрос api.uwuaff.com пишется в job uwuaff_check.

Включается/выключается и настраивается интервал в Settings, там же кнопка Check now для ручного запуска и статус последнего прогона. Учётные данные (UWUAFF_TOKEN_URL/UWUAFF_CLIENT_ID/UWUAFF_CLIENT_SECRET/UWUAFF_API) задаются переменными окружения контейнера — теми же GitLab CI/CD переменными, что уже использует seo-frontend-nginx, в UI не вводятся.

Set Active — отдельно от Enable/Disable

Enable/Disable и Set Active — два разных, независимых переключателя, специально разделены:

Дальше это конвертируется в реальный редирект как обычно: sync_mirrors.py в seo-frontend-nginx видит, что "неактивные" зеркала группы теперь unavailable, и сам подставляет им redirect_to на активное — никаких изменений в seo-frontend-nginx для этого не потребовалось.

⚙ Settings — что где включается

РазделЗа что отвечает
Slack alertsКуда слать: успешный/неуспешный запуск automirrors-crypt, исчерпание диапазона номеров группы.
automirrors-cryptGitLab base URL / Project ID / Pipeline trigger token — которыми mirrors-service стучится в GitLab API, чтобы запустить пайплайн выпуска нового зеркала. Пока Project ID или токен не заполнены — автоматика не запускает пайплайны, только шлёт Slack-алерт вида "нужно перевыпустить вручную".
uwuaff domain-block checkВключить/выключить автоматический опрос api.uwuaff.com и задать интервал (см. выше), плюс статус/ошибка последнего прогона и кнопка Check now.

Как всё это запускается

Webhook — мгновенная блокировка

Если внешняя система узнала о блокировке, можно дёрнуть эндпоинт напрямую — статус обновится сразу, не дожидаясь ручного RKN-BAN:

curl -X POST https://mirrors-service.rioorg.com/webhook/blocked \
  -H "Content-Type: application/json" \
  -d '{"domain": "riobet403.com"}'

Это не ставит 📌 manual override.

▤ Runs — статус выполнения

Полная история запусков — не только "последний раз", а каждый прогон отдельной строкой:

JobЧто это
rkn_banНажатие кнопки RKN-BAN — форсированный is_blocked=true и последующая оценка Groups.
automirrors_triggerОдна попытка запустить пайплайн automirrors-crypt для конкретного домена/группы — сюда же попадает ошибка "не достучались до GitLab CI" (сеть/таймаут) и ошибка неверного статуса ответа GitLab (например, протухший токен).
seo_sync_triggerРучной запуск ТОЛЬКО sync_mirrors-джобы в seo-frontend-nginx (см. ниже).
uwuaff_checkОпрос api.uwuaff.comscheduled (по интервалу) или manual (кнопка "Check now" в Settings). Информационный, ничего не выпускает.

Для каждой строки: время старта, длительность, ok/error, деталь (для automirrors_trigger — какой домен/группа), колонка Pipeline (ссылка на пайплайн в GitLab + его статус сразу после создания — created/pending) и полный текст ошибки по наведению.

Важно про статус пайплайна: Pipeline Trigger Token в GitLab умеет только запускать пайплайн, но не может потом прочитать его финальный результат (success/failed) — для этого нужен отдельный токен с правом чтения API, которого здесь нет. Поэтому колонка Pipeline показывает только начальный статус создания; реальный результат (прошёл ли деплой) смотрите по ссылке в самом GitLab.

▶ Trigger seo-frontend-nginx sync — только GitLab, без sync/check здесь

Кнопка на странице Runs запускает исключительно джобу sync_mirrors в проекте seo-frontend-nginx через её собственный Pipeline Trigger Token. Данные берутся ровно те, что прямо сейчас лежат в mirrors-service (наш status/is_active — источник правды), включая любые ручные 📌-изменения.

Что делает эта джоба на стороне seo-frontend-nginx: читает GET /api/v1/get-mirrors отсюда, для каждого известного хоста сравнивает с текущим inventory.yml, и если статус разошёлся — проставляет is_active: true/falseredirect_to для заблокированных, подбирая живого соседа по группе riobet40x/riobet_thousandтолько среди хостов, которые сам знает, см. ниже), коммитит и деплоит nginx-конфиги на изменившихся хостах.

На странице Runs — две отдельные кнопки, специально разделённые, чтобы не катить вслепую:

Чтобы кнопки заработали, нужно:

  1. В Settings → seo-frontend-nginx указать Project ID и Pipeline trigger token (GitLab → тот проект → Settings → CI/CD → Pipeline trigger tokens).
  2. В seo-frontend-nginx/.gitlab-ci.yml у джобы sync_mirrors должно быть правило if: '$CI_PIPELINE_SOURCE == "trigger"' — без него пайплайн от Trigger Token запустится, но выполнит ноль джоб.

Важно про выбор redirect_to: цель для редиректа выбирается только среди хостов группы (riobet40x/riobet_thousand), которые реально зарегистрированы в mirrors-service и сейчас доступны. Хост, которого mirrors-service вообще не знает (например, добавлен в inventory.yml вручную и никогда не заводился здесь), никогда не будет выбран как цель — раньше это было не так, и именно из-за этого редиректы иногда улетали на несуществующее/неотслеживаемое зеркало вместо реально живого соседа.

Частые проблемы

СимптомЧто делать
automirrors_trigger: "could not reach GitLab CI at ..." Сеть/доступность GitLab с сервера mirrors-service, либо неверный automirrors_gitlab_base_url в Settings.
automirrors_trigger / seo_sync_trigger: "POST /trigger/pipeline -> 404" Почти всегда — неверный Project ID (сверьте с Settings → General → Project ID у нужного проекта в GitLab, вверху страницы) либо Pipeline trigger token принадлежит другому проекту. Project ID можно указать и числом, и полным путём вида devops/seo-frontend-nginx — оба варианта поддерживаются.
automirrors_trigger / seo_sync_trigger: "POST /trigger/pipeline -> 401/403" Токен отозван/просрочен — создайте новый в GitLab → тот проект → Settings → CI/CD → Pipeline trigger tokens (показывается только один раз при создании) и вставьте в Settings mirrors-service.
Group показывает range_exhausted Увеличьте Number max у этой группы в Groups.
Зеркало не появляется в seo-frontend-nginx/inventory.yml Это отдельный шаг — либо автоматический онбординг (при выпуске через automirrors-crypt с заданными MIRROR_GROUP/PREDECESSOR_DOMAIN), либо ручное добавление хоста в inventory.yml.
"Trigger seo-frontend-nginx sync" вернул 400 "not configured in Settings" Заполните Project ID и Pipeline trigger token в Settings → seo-frontend-nginx.
uwuaff_check: "UWUAFF_TOKEN_URL/UWUAFF_CLIENT_ID/UWUAFF_CLIENT_SECRET not configured" Эти переменные — env vars контейнера, не поля в Settings. Проверьте, что в GitLab CI/CD Variables проекта/группы mirrors-service заданы UWUAFF_API/UWUAFF_TOKEN_URL/UWUAFF_CLIENT_ID/UWUAFF_CLIENT_SECRET (те же, что уже использует seo-frontend-nginx), и передаются в docker-stack-prod.yml.
Колонка uwuaff всегда показывает n/a Домен не найден в списке api.uwuaff.com/v1/domains — либо он ещё не зарегистрирован там (см. register-mirror-domain.yml/trigger-frontend-deploy.yml в seo-frontend-nginx), либо url_mirror здесь не совпадает с доменом в uwuaff.
Кнопка отработала (Pipeline: created), но inventory.yml не поменялся Скорее всего в seo-frontend-nginx/.gitlab-ci.yml у джобы sync_mirrors нет правила if: '$CI_PIPELINE_SOURCE == "trigger"' — пайплайн создался, но выполнил ноль джоб. Проверьте по ссылке на пайплайн, была ли там вообще джоба sync_mirrors.