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 ничего не деплоит сам — он только решает и хранит статус.
| Колонка | Что значит | Кто проставляет |
|---|---|---|
| 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. |
seo-frontend-nginx (или запустить его пайплайн вручную) — он увидит status=unavailable, выберет живого соседа по группе (riobet40x/riobet_thousand) как redirect_to и задеплоит редирект.Ручной приоритет (меньше — важнее). Заводится и меняется вручную, ничем автоматически не перезаписывается.
| Значение | Смысл |
|---|---|
-9 | Основное зеркало |
-5 | В очереди / запасное |
-1 | Fallback, последний вариант |
Group — это политика, по которой mirrors-service сам решает, когда запускать пайплайн automirrors-crypt и выпускать новый домен. Оценивается сразу после каждого RKN-BAN.
Пример: группа riobet40x, target_pool_size = 8. Если из доменов с тегом riobet40x доступно (available, не заблокировано, не disabled) меньше 8 — заказывается разница (deficit), с учётом уже выпускаемых (provisioning, чтобы не заказать дважды). Новые домены берут следующий свободный номер после максимального когда-либо использованного в диапазоне number_min–number_max — номера не переиспользуются.
Пример: цепочка riobet2000. "Головой" считается домен с максимальным номером среди немаркированных как provisioning. Пока голова не заблокирована — ничего не происходит. Как только голову блокируют — заказывается следующий по номеру домен, а голова становится predecessor (на неё ставится redirect_to в inventory.yml при онбординге).
range_exhausted — если number_max исчерпан, а зеркало всё ещё нужно, автоматика останавливается и шлёт алерт в Slack; поднимите number_max в Groups.
Кнопка Preview plan в Groups показывает точно то же решение, что приняла бы автоматика прямо сейчас — без побочных эффектов (ничего не создаёт, пайплайн не запускает). Полезно проверить перед тем, как включать enabled или прописывать GitLab-токен.
Кнопка в таблице 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).
issued: riobet1003.com), и пишет запись в job rkn_ban на странице Runs.Требования те же, что и для автовыпуска: зеркало должно быть протегировано mirror_tag нужной Group, а сама Group — существовать и быть enabled. Если группы нет или тега нет — кнопка отработает, но в Runs будет issued: none.
Раз в 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.
is_active: false в его ответе). Наведите курсор — во всплывающей подсказке будет время проверки и текст причины от uwuaff (например, номер решения РКН).Каждый авто-бан пишет отдельную запись в 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 не вводятся.
Enable/Disable и Set Active — два разных, независимых переключателя, специально разделены:
⛓ Groups, mode=chain). Жёстко фиксирует: это зеркало — available, а все остальные зеркала с тем же тегом (та же цепочка) — unavailable. Гарантирует инвариант "ровно один активный на цепочку" — все затронутые зеркала получают 📌 (manual override).Дальше это конвертируется в реальный редирект как обычно: sync_mirrors.py в seo-frontend-nginx видит, что "неактивные" зеркала группы теперь unavailable, и сам подставляет им redirect_to на активное — никаких изменений в seo-frontend-nginx для этого не потребовалось.
| Раздел | За что отвечает |
|---|---|
| Slack alerts | Куда слать: успешный/неуспешный запуск automirrors-crypt, исчерпание диапазона номеров группы. |
| automirrors-crypt | GitLab base URL / Project ID / Pipeline trigger token — которыми mirrors-service стучится в GitLab API, чтобы запустить пайплайн выпуска нового зеркала. Пока Project ID или токен не заполнены — автоматика не запускает пайплайны, только шлёт Slack-алерт вида "нужно перевыпустить вручную". |
| uwuaff domain-block check | Включить/выключить автоматический опрос api.uwuaff.com и задать интервал (см. выше), плюс статус/ошибка последнего прогона и кнопка Check now. |
is_blocked=true, немедленно оцениваются все включённые Groups — если что-то нужно, тут же уходит запрос в GitLab на запуск automirrors-crypt.uwuaff_check_interval_seconds он опрашивает uwuaff check, обновляет колонку uwuaff, и при новой блокировке сам жмёт RKN-BAN (см. выше) — но только в сторону блокировки, никогда не снимает её.Если внешняя система узнала о блокировке, можно дёрнуть эндпоинт напрямую — статус обновится сразу, не дожидаясь ручного RKN-BAN:
curl -X POST https://mirrors-service.rioorg.com/webhook/blocked \
-H "Content-Type: application/json" \
-d '{"domain": "riobet403.com"}'
Это не ставит 📌 manual override.
Полная история запусков — не только "последний раз", а каждый прогон отдельной строкой:
| 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.com — scheduled (по интервалу) или manual (кнопка "Check now" в Settings). Информационный, ничего не выпускает. |
Для каждой строки: время старта, длительность, ok/error, деталь (для automirrors_trigger — какой домен/группа), колонка Pipeline (ссылка на пайплайн в GitLab + его статус сразу после создания — created/pending) и полный текст ошибки по наведению.
Важно про статус пайплайна: Pipeline Trigger Token в GitLab умеет только запускать пайплайн, но не может потом прочитать его финальный результат (success/failed) — для этого нужен отдельный токен с правом чтения API, которого здесь нет. Поэтому колонка Pipeline показывает только начальный статус создания; реальный результат (прошёл ли деплой) смотрите по ссылке в самом GitLab.
Кнопка на странице 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/false (и redirect_to для заблокированных, подбирая живого соседа по группе riobet40x/riobet_thousand — только среди хостов, которые сам знает, см. ниже), коммитит и деплоит nginx-конфиги на изменившихся хостах.
На странице Runs — две отдельные кнопки, специально разделённые, чтобы не катить вслепую:
SYNC_MIRRORS_CHECK=true: джоба посчитает изменения и выведет их в лог + артефакт inventory.diff, но ничего не закоммитит/не запушит/не задеплоит. Диф смотрите по ссылке на пайплайн, в логе джобы sync_mirrors или в артефактах.inventory.yml, пушит и деплоит nginx-конфиги на изменившихся хостах.Чтобы кнопки заработали, нужно:
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. |