Осталось реализовать
Содержание
Десять дней назад я завершил разбор схемы доставки конфигурации клиентам и поставил точку в характерном стиле: «схема зафиксирована в документации. Осталось дело за малым: реализовать». Такие фразы в дневнике проекта — опасный знак. Звучат оптимистично, будущее кажется близким — а через месяц смотришь на них с тихой укоризной.
На этот раз пронесло. Сегодня сервис запущен.
Мехинизм подписки #
Напомню суть проблемы — в том посте я объяснял, почему статичная VLESS-ссылка — это ахиллесова пята. Мы научились менять gRPC-пути между Entry- и Core-нодами автоматически, по расписанию. Но клиент об этом ничего не знает: у него на руках ссылка, скопированная при регистрации, и она рано или поздно устаревает.
Решение — механизм подписки. Вместо статичной ссылки пользователь получает один адрес:
https://<домен>/api/<UUID>
Клиентское приложение периодически обращается к нему и само забирает актуальную конфигурацию. Поменялся serviceName — следующий запрос вернет обновленную ссылку. Заменили Entry-ноду — перенаправили DNS, URL подписки остался тем же. Пользователю не нужно ничего делать — система сама обновляет конфигурацию, без лишних телодвижений.
Архитектура микросервиса #
Теперь об устройстве изнутри.
Микросервис небольшой — около 270 строк Python. FastAPI, четыре слоя с однонаправленными зависимостями, функциональный стиль. Никаких баз данных: данные берутся из локального клона репозитория с данными, который уже живет на Core-ноде и синхронизируется каждые 15 минут.
Логика одного запроса: UUID из пути → проверить устройство → проверить пользователя → найти активные маршруты для этой Core-ноды → для каждого маршрута подобрать домен Entry-ноды → собрать VLESS-ссылку → упаковать в base64 и вернуть. Один ответ может содержать несколько ссылок — если у пользователя есть доступ через несколько Entry-нод. Если устройство заблокировано, пользователь неактивен или маршрутов нет — просто 404, без подробностей.
Сервис упакован в Docker, запущен на Core-ноде. Nginx проксирует /api/ на localhost, снаружи сервис недоступен напрямую. CI/CD по знакомой схеме: пуш в main → GitHub Actions → SSH → git pull + docker compose up --build. Развернулось само, без единого ручного действия на сервере.
FastAPI #
Чуть раньше я писал о том, что этот микросервис — хороший повод наконец попробовать Go в деле. Язык уже присутствует в проекте повсюду: Hugo, тема, будущий etcd — всё Go. Было бы логично вписать в эту картину и сервис подписки.
Но в том же посте я честно оговорился: первый рабочий вариант — на FastAPI. Привычно, без сюрпризов, быстро до результата. Так и вышло. Прототип написан на Python, работает, задачу решает.
Go никуда не делся — просто это следующий шаг. Когда-нибудь перепишем.
Итоги #
Что это меняет на практике.
Раньше при любом изменении — смене ноды, обновлении пути, плановой ротации — каждому пользователю нужно было как-то доставить новую ссылку. Сейчас инфраструктура может меняться независимо: клиенты получат обновленную конфигурацию при следующей синхронизации сами.
«Осталось дело за малым: реализовать». Ну и вот. Реализовали.