diff --git a/deploy/livekit/livekit.yaml.template b/deploy/livekit/livekit.yaml.template index eafd99e..cbbee32 100644 --- a/deploy/livekit/livekit.yaml.template +++ b/deploy/livekit/livekit.yaml.template @@ -32,6 +32,40 @@ rtc: use_external_ip: ${LIVEKIT_USE_EXTERNAL_IP} node_ip: ${LIVEKIT_NODE_IP} + # Внешний TURN (сервис coturn, профиль `media`) — АНОНС КЛИЕНТАМ. + # Сам SFU через эти серверы не ходит: LiveKit лишь отдаёт их браузеру в + # списке ICE-серверов при подключении (см. iceServersForParticipant в + # pkg/service/roommanager.go), а клиент уже решает, нужен ли ему relay. + # + # Зачем. До 28.07.2026 coturn работал, но КЛИЕНТЫ О НЁМ НЕ ЗНАЛИ: секция + # `turn` ниже выключена (встроенный TURN не поднимаем), внешний в конфиге + # объявлен не был, а фронтенд `iceServers` не задаёт. За всё время работы + # в логах coturn — ноль ALLOCATE. Итог: у клиентов из сетей с жёстким NAT + # не было relay-фолбэка вообще, только прямой UDP и TCP 7881. Именно так + # объясняются `PEER_CONNECTION_DISCONNECTED` на нагрузочном тесте — все + # у внешних участников, ни одного у офисных (.forcc/LOAD-FINDINGS.md, + # причина C). + # + # `secret` обязан совпадать с `static-auth-secret` в turnserver.conf — + # оба рендерятся из одного TURN_STATIC_AUTH_SECRET (deploy/render-templates.sh). + # Логин/пароль LiveKit генерирует сам по механизму TURN REST API. + # + # UDP и TCP на 3478 — оба порта уже открыты в ufw. TLS (5349) намеренно не + # объявляем: в turnserver.conf сертификаты не смонтированы, и анонс + # неработающего `turns:` заставил бы клиента впустую ждать таймаута, + # прежде чем перейти к рабочему кандидату. + turn_servers: + - host: ${TURN_EXTERNAL_IP} + port: 3478 + protocol: udp + secret: ${TURN_STATIC_AUTH_SECRET} + ttl: 14400 + - host: ${TURN_EXTERNAL_IP} + port: 3478 + protocol: tcp + secret: ${TURN_STATIC_AUTH_SECRET} + ttl: 14400 + # Redis обязателен для сервиса egress (см. deploy/egress/) — он использует # его как pub/sub и key-value хранилище состояния запущенных записей; # без него egress не может получать room/track-события от LiveKit diff --git a/deploy/render-templates.sh b/deploy/render-templates.sh index 0108b97..067a96b 100755 --- a/deploy/render-templates.sh +++ b/deploy/render-templates.sh @@ -49,7 +49,10 @@ envsubst '${TURN_STATIC_AUTH_SECRET} ${TURN_REALM} ${TURN_EXTERNAL_IP}' \ < "$SCRIPT_DIR/coturn/turnserver.conf.template" > "$SCRIPT_DIR/coturn/turnserver.conf" echo "[render] deploy/coturn/turnserver.conf готов" -envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY} ${REDIS_PASSWORD}' \ +# TURN_EXTERNAL_IP и TURN_STATIC_AUTH_SECRET нужны и здесь: с 0.0.14 LiveKit +# анонсирует клиентам внешний coturn (секция `rtc.turn_servers`), и секрет +# обязан совпадать с `static-auth-secret` в turnserver.conf выше. +envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY} ${REDIS_PASSWORD} ${TURN_EXTERNAL_IP} ${TURN_STATIC_AUTH_SECRET}' \ < "$SCRIPT_DIR/livekit/livekit.yaml.template" > "$SCRIPT_DIR/livekit/livekit.yaml" echo "[render] deploy/livekit/livekit.yaml готов" diff --git a/docs/deploy/DEPLOYMENT.md b/docs/deploy/DEPLOYMENT.md index a53a3be..24736f2 100644 --- a/docs/deploy/DEPLOYMENT.md +++ b/docs/deploy/DEPLOYMENT.md @@ -82,9 +82,14 @@ ufw allow 80/tcp # HTTP (редирект на HTTPS + ACME-challenge) ufw allow 443/tcp # HTTPS ufw allow 7881/tcp # LiveKit RTC TCP fallback (профиль media) ufw allow 54000:54100/udp # LiveKit WebRTC media (ICE), см. docker-compose.yml -# TURN (coturn) — только если включаете раздел 8: +# TURN (coturn) — только если включаете раздел 8. Нужны ОБА пункта: +# сигнальные порты И диапазон relay-аллокаций (min-port/max-port из +# deploy/coturn/turnserver.conf). Без второго TURN отвечает на запросы, но +# сам релей не работает — клиент получает кандидата и не может им +# воспользоваться, а в логах coturn при этом тишина. # ufw allow 3478/tcp # ufw allow 3478/udp +# ufw allow 49160:49200/udp # Мониторинг (профиль `monitoring`): node-exporter работает в host-сети — # иначе он отдаёт сетевые метрики собственного контейнера вместо метрик @@ -320,29 +325,48 @@ firewall) хватает для подавляющего большинства (характерный симптом — конференция подключается по signaling, `connection state: connected`, но собеседник не видит видео/не слышит звук). -По умолчанию `deploy/livekit/livekit.yaml.template` содержит -`turn.enabled: false`, и `rtc.turn_servers` не задан — standalone coturn -поднимается (профиль `media`), но LiveKit не раздаёт его клиентам как -ICE-фолбэк. +**С версии 0.0.14 LiveKit анонсирует coturn клиентам** — секция +`rtc.turn_servers` в `deploy/livekit/livekit.yaml.template` (UDP и TCP на +3478, credentials по механизму TURN REST API из общего +`TURN_STATIC_AUTH_SECRET`). Встроенный TURN LiveKit при этом остаётся +выключенным (`turn.enabled: false`), чтобы не поднимать два TURN-сервера. -Включение (правки шаблона `deploy/livekit/livekit.yaml.template` + -редеплой; **код-фикс не входит в это руководство без запроса** — обсудите -с командой перед изменением): +⚠️ **Чем это было до 0.0.14, если вы обновляетесь со старой версии.** coturn +поднимался и был healthy, но клиенты о нём не знали: в конфиге LiveKit +внешний TURN объявлен не был, а фронтенд `iceServers` не задаёт. За всё +время работы в логах coturn не было ни одного ALLOCATE — то есть relay не +использовался никогда, и участники из сетей с жёстким NAT просто теряли +соединение (`PEER_CONNECTION_DISCONNECTED`). -1. Открыть 443 для TURN/TLS (наиболее надёжный фолбэк — TURN через тот же - порт, что и остальной HTTPS-трафик, редко блокируется firewall'ами): - потребует отдельного TLS-сертификата для coturn (`cert-file`/`pkey-file` - в `deploy/coturn/turnserver.conf.template`) — можно переиспользовать тот - же Let's Encrypt сертификат, что и nginx (тот же `/etc/letsencrypt`, уже - смонтированный в nginx — coturn сейчас его не монтирует, потребуется - доп. volume). -2. В `livekit.yaml.template` включить `turn.enabled: true` и/или явно - прописать `rtc.turn_servers` со статическими credentials - (`TURN_STATIC_AUTH_SECRET` уже есть в `.env`). -3. `ufw allow 3478/tcp` + `ufw allow 3478/udp` (шаг 1, закомментированные - строки) — сейчас coturn поднят, но порт не проверялся как обязательный. -4. Передеплой (`docker compose ... up -d --force-recreate livekit coturn`) - и повторный кросс-сетевой тест именно с проблемной сетью. +Что нужно проверить на своей инсталляции: + +1. **Порты в ufw — оба пункта** (см. шаг 1): `3478/tcp` + `3478/udp` для + сигнализации и `49160:49200/udp` для relay-аллокаций. Диапазон должен + совпадать с `min-port`/`max-port` в + `deploy/coturn/turnserver.conf.template`. Без него TURN отвечает на + запросы, но релей не работает — самый неприятный вариант, потому что в + логах coturn при этом тишина. +2. **`TURN_EXTERNAL_IP` в `.env`** — реальный внешний IP или домен сервера. + Именно это значение уезжает клиентам как адрес TURN-сервера, поэтому + `127.0.0.1` из dev-дефолта сделает анонс бесполезным. +3. После правок — `./deploy/render-templates.sh` (перерендерит конфиги из + шаблонов), затем `docker compose ... up -d --force-recreate livekit`. + ⚠️ Перезапуск LiveKit **разрывает все активные конференции** — выбирайте + окно. +4. Проверка, что релей заработал: провести звонок из проблемной сети и + убедиться, что в логах появились аллокации: + `docker logs vidconf-coturn-1 --since 10m 2>&1 | grep -ci allocate`. + Ноль при живом звонке из-за NAT означает, что до coturn не дошли — + смотрите ufw и `TURN_EXTERNAL_IP`. + +**TURN over TLS (порт 5349 или 443) — не настроен.** Это самый надёжный +фолбэк (проходит там, где режут UDP и нестандартные порты), но требует +смонтировать в coturn TLS-сертификат: раскомментировать `cert`/`pkey` в +`deploy/coturn/turnserver.conf.template`, добавить volume с +`/etc/letsencrypt` (nginx его уже монтирует, coturn — нет), открыть порт и +не забыть про перезапуск coturn при обновлении сертификата. Пока этого нет, +`turns:` намеренно не анонсируется: анонс неработающего адреса заставил бы +клиента ждать таймаута перед переходом к рабочему кандидату. ---