Продуктовый Telegram-трафик уходит через ретранслятор на Beget #3487

Merged
lekss361 merged 4 commits from feat/3471-telegram-relay-beget into main 2026-09-12 11:38:26 +00:00
Owner

Зачем

Путь Selectel → api.telegram.org теряет примерно каждый четвёртый короткий запрос. Тот же замер с Beget в те же минуты — чистый, значит дело в сети провайдера, а не в Telegram. Отсюда потери ответов оператора и алертов.

Ретранслятор ставится на Beget и проксирует Bot API целиком. Продуктовый клиент ходит через него, при недоступности ретранслятора один раз пробует прямой путь.

Что внутри

  • ops/metrics/tg-relay/app.py — ретранслятор на stdlib, проксирует /bot<TOKEN>/<method> с методом, телом и query. Таймаут к апстриму 75с, шире любого long-poll. Токен вырезается из лога, log_request переопределён целиком.
  • docker-compose.metrics.yml — сервис tg-relay рядом с alert-ack, сеть shared, лимит 128 МБ, healthcheck.
  • caddy/sites/infra.caddy — маршрут /tg-relay/* под log_skip (путь несёт токен, #3154), таймаут ответа 80с.
  • tradein-mvp/backend/app/services/tgbot/client.py — поход через ретранслятор с одноразовым откатом на прямой хост; секрет уходит только в ретранслятор.
  • Проводка настроек в shared.py и tgbot_main.py — оба места создания клиента.

Секрет — заголовок X-Relay-Secret. Без переменной TG_RELAY_SECRET сервис не стартует, без заголовка отвечает 401.

Переменные окружения

Beget: TG_RELAY_SECRET.
Selectel: TELEGRAM_RELAY_BASE_URL=https://metrics.gendsgn.ru/tg-relay, TELEGRAM_RELAY_SECRET с тем же значением. Пустое значение возвращает прежнее прямое поведение — это и есть откат.

Test plan

  • ops/metrics/tg-relay/test_app.py — 9/9
  • tradein-mvp/backend/tests/services/tgbot/test_client.py — 25/25
  • caddy validate на infra.caddy — Valid configuration
  • после деплоя: завести обе переменные, проверить доставку сообщения в поддержку

Refs #3471

🤖 Generated with Claude Code

https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG

## Зачем Путь Selectel → `api.telegram.org` теряет примерно каждый четвёртый короткий запрос. Тот же замер с Beget в те же минуты — чистый, значит дело в сети провайдера, а не в Telegram. Отсюда потери ответов оператора и алертов. Ретранслятор ставится на Beget и проксирует Bot API целиком. Продуктовый клиент ходит через него, при недоступности ретранслятора один раз пробует прямой путь. ## Что внутри - `ops/metrics/tg-relay/app.py` — ретранслятор на stdlib, проксирует `/bot<TOKEN>/<method>` с методом, телом и query. Таймаут к апстриму 75с, шире любого long-poll. Токен вырезается из лога, `log_request` переопределён целиком. - `docker-compose.metrics.yml` — сервис `tg-relay` рядом с `alert-ack`, сеть `shared`, лимит 128 МБ, healthcheck. - `caddy/sites/infra.caddy` — маршрут `/tg-relay/*` под `log_skip` (путь несёт токен, #3154), таймаут ответа 80с. - `tradein-mvp/backend/app/services/tgbot/client.py` — поход через ретранслятор с одноразовым откатом на прямой хост; секрет уходит только в ретранслятор. - Проводка настроек в `shared.py` и `tgbot_main.py` — оба места создания клиента. Секрет — заголовок `X-Relay-Secret`. Без переменной `TG_RELAY_SECRET` сервис не стартует, без заголовка отвечает 401. ## Переменные окружения Beget: `TG_RELAY_SECRET`. Selectel: `TELEGRAM_RELAY_BASE_URL=https://metrics.gendsgn.ru/tg-relay`, `TELEGRAM_RELAY_SECRET` с тем же значением. Пустое значение возвращает прежнее прямое поведение — это и есть откат. ## Test plan - [x] `ops/metrics/tg-relay/test_app.py` — 9/9 - [x] `tradein-mvp/backend/tests/services/tgbot/test_client.py` — 25/25 - [x] `caddy validate` на `infra.caddy` — Valid configuration - [ ] после деплоя: завести обе переменные, проверить доставку сообщения в поддержку Refs #3471 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
lekss361 added 3 commits 2026-09-12 11:20:27 +00:00
Замер 12.09.2026, оба хоста в одни и те же минуты: getMe из tradein-tgbot
на Selectel — 9 успешных из 12, три ConnectTimeout; TCP-443 до адреса,
резолвящегося на Selectel (149.154.167.220) — 5 из 6; TCP-443 до адреса,
резолвящегося на Beget (149.154.166.110) — 8 из 8. За сутки в логе бота
508 строк network error, за 30 дней 92 обрыва итерации poll loop. Значит:
путь до Telegram с Selectel лоссовый, с Beget чистый — Alertmanager (живёт
на Beget) шлёт в тот же чат без проблем, а бот поддержки на Selectel часть
отправок теряет.

Добавлен ops/metrics/tg-relay — stdlib-only HTTP-сервис (тот же принцип,
что у alert-ack: без зависимостей, поднимается даже когда всё остальное
сломано), проксирует Bot API целиком (метод, путь, тело — sendMessage,
copyMessage, getUpdates) на api.telegram.org. Токен из пути не логируется:
log_request переопределён полностью, путь редактируется до записи в лог.
Аутентификация — общий секрет в X-Relay-Secret, по образцу
X-Internal-Auth-Secret из этого же стека.

Клиент (tgbot/client.py) при транспортном отказе похода на ретранслятор
делает одну попытку напрямую к api.telegram.org — хуже прямого пути быть
не должно ни при каких условиях. Пустой TELEGRAM_RELAY_BASE_URL — прежнее
поведение без изменений, это и есть механизм отката.

Refs #3471
feat(ops): маршрут ретранслятора Telegram на metrics.gendsgn.ru
Some checks failed
CI Trade-In / changes (pull_request) Successful in 15s
CI / changes (pull_request) Successful in 20s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 1m39s
34c15ecf08
Без маршрута сервис tg-relay недостижим снаружи и настройка
TELEGRAM_RELAY_BASE_URL на продуктовом хосте ни к чему не приводит.

Путь несёт токен бота, поэтому помечен log_skip: иначе он осядет в
файловом логе сайта и в stdout-копии, которую читает Alloy (#3154).
Таймаут ответа поднят до 80с — getUpdates висит long-poll'ом до ~40с,
дефолтного не хватает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
bot-backend added 1 commit 2026-09-12 11:27:19 +00:00
style(tg): перенос длинной строки заголовков ретранслятора
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 15s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m58s
24c2052057
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
lekss361 merged commit ede6fd5974 into main 2026-09-12 11:38:26 +00:00
lekss361 deleted branch feat/3471-telegram-relay-beget 2026-09-12 11:38:26 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3487
No description provided.