Configure self-hosted STUN for remote WebRTC
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
# docker compose 变量(复制为项目根 .env)。公网 WebRTC 需配合 --profile remote。
|
# docker compose 变量(复制为项目根 .env)。公网 WebRTC 需配合 --profile remote。
|
||||||
PUBLIC_IP=182.92.86.220
|
# PRIVATE_IP 是 `ip -4 route get 1.1.1.1` 输出中 src 后的地址。
|
||||||
|
PUBLIC_IP=118.89.89.89
|
||||||
|
PRIVATE_IP=10.0.0.8
|
||||||
TURN_SECRET=change-me-to-a-long-random-string
|
TURN_SECRET=change-me-to-a-long-random-string
|
||||||
TURN_URLS=turn:182.92.86.220:3478?transport=udp,turn:182.92.86.220:3478?transport=tcp
|
STUN_URL=stun:118.89.89.89:3478
|
||||||
|
TURN_URLS=turn:118.89.89.89:3478?transport=udp,turn:118.89.89.89:3478?transport=tcp
|
||||||
S3_ACCESS_KEY=rustfsadmin
|
S3_ACCESS_KEY=rustfsadmin
|
||||||
S3_SECRET_KEY=rustfsadmin
|
S3_SECRET_KEY=rustfsadmin
|
||||||
S3_BUCKET=ai-video
|
S3_BUCKET=ai-video
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ KNOWLEDGE_TOP_K=5
|
|||||||
|
|
||||||
# ---- WebRTC TURN(公网跨网语音预览;本地开发留空) ----
|
# ---- WebRTC TURN(公网跨网语音预览;本地开发留空) ----
|
||||||
# 与 docker compose --profile remote 的 coturn 配套。云安全组需放行 UDP 3478 与 49152-49200。
|
# 与 docker compose --profile remote 的 coturn 配套。云安全组需放行 UDP 3478 与 49152-49200。
|
||||||
# PUBLIC_IP 填云主机公网 IP(compose 里给 coturn --external-ip 用,见项目根 .env)。
|
# Docker Compose 部署请在项目根 .env 设置这些变量;此处适用于宿主机直接运行 uvicorn。
|
||||||
# TURN_URLS=turn:182.92.86.220:3478?transport=udp,turn:182.92.86.220:3478?transport=tcp
|
# STUN_URL=stun:118.89.89.89:3478
|
||||||
|
# TURN_URLS=turn:118.89.89.89:3478?transport=udp,turn:118.89.89.89:3478?transport=tcp
|
||||||
# TURN_SECRET=your-turn-secret
|
# TURN_SECRET=your-turn-secret
|
||||||
# TURN_CREDENTIAL_TTL=86400
|
# TURN_CREDENTIAL_TTL=86400
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import time
|
|||||||
|
|
||||||
import settings
|
import settings
|
||||||
|
|
||||||
STUN_URL = "stun:stun.l.google.com:19302"
|
|
||||||
|
|
||||||
|
|
||||||
def _turn_credentials() -> tuple[str, str] | None:
|
def _turn_credentials() -> tuple[str, str] | None:
|
||||||
"""Return (username, credential) for TURN, or None when TURN is not configured."""
|
"""Return (username, credential) for TURN, or None when TURN is not configured."""
|
||||||
@@ -34,7 +32,7 @@ def _turn_credentials() -> tuple[str, str] | None:
|
|||||||
|
|
||||||
def client_ice_servers() -> list[dict]:
|
def client_ice_servers() -> list[dict]:
|
||||||
"""ICE servers for browser RTCPeerConnection (JSON-serializable)."""
|
"""ICE servers for browser RTCPeerConnection (JSON-serializable)."""
|
||||||
servers: list[dict] = [{"urls": STUN_URL}]
|
servers: list[dict] = [{"urls": settings.STUN_URL}]
|
||||||
creds = _turn_credentials()
|
creds = _turn_credentials()
|
||||||
if not creds:
|
if not creds:
|
||||||
return servers
|
return servers
|
||||||
@@ -48,7 +46,7 @@ def aiortc_ice_servers() -> list:
|
|||||||
"""ICE servers for backend SmallWebRTCConnection / aiortc."""
|
"""ICE servers for backend SmallWebRTCConnection / aiortc."""
|
||||||
from aiortc import RTCIceServer
|
from aiortc import RTCIceServer
|
||||||
|
|
||||||
servers = [RTCIceServer(urls=STUN_URL)]
|
servers = [RTCIceServer(urls=settings.STUN_URL)]
|
||||||
creds = _turn_credentials()
|
creds = _turn_credentials()
|
||||||
if not creds:
|
if not creds:
|
||||||
return servers
|
return servers
|
||||||
|
|||||||
@@ -38,9 +38,13 @@ AUTH_TOKEN_EXPIRE_MINUTES = int(os.getenv("AUTH_TOKEN_EXPIRE_MINUTES", "1440"))
|
|||||||
AUTH_COOKIE_SECURE = os.getenv("AUTH_COOKIE_SECURE", "false").lower() == "true"
|
AUTH_COOKIE_SECURE = os.getenv("AUTH_COOKIE_SECURE", "false").lower() == "true"
|
||||||
AUTH_COOKIE_SAMESITE = os.getenv("AUTH_COOKIE_SAMESITE", "lax")
|
AUTH_COOKIE_SAMESITE = os.getenv("AUTH_COOKIE_SAMESITE", "lax")
|
||||||
|
|
||||||
# ---- WebRTC TURN ----
|
# ---- WebRTC STUN / TURN ----
|
||||||
|
# Override STUN_URL in remote deployments to use the colocated coturn server
|
||||||
|
# instead of waiting for a public STUN service that may be unreachable.
|
||||||
|
STUN_URL = os.getenv("STUN_URL", "stun:stun.l.google.com:19302")
|
||||||
|
|
||||||
# TURN_URLS example:
|
# TURN_URLS example:
|
||||||
# turn:182.92.86.220:3478?transport=udp,turn:182.92.86.220:3478?transport=tcp
|
# turn:118.89.89.89:3478?transport=udp,turn:118.89.89.89:3478?transport=tcp
|
||||||
TURN_URLS = _split(os.getenv("TURN_URLS", ""))
|
TURN_URLS = _split(os.getenv("TURN_URLS", ""))
|
||||||
TURN_SECRET = os.getenv("TURN_SECRET", "")
|
TURN_SECRET = os.getenv("TURN_SECRET", "")
|
||||||
TURN_USERNAME = os.getenv("TURN_USERNAME", "")
|
TURN_USERNAME = os.getenv("TURN_USERNAME", "")
|
||||||
|
|||||||
@@ -170,44 +170,53 @@ https://<远程机器IP或域名>:18189
|
|||||||
如果浏览器和后端不在同一内网,WebRTC 媒体链路通常需要 TURN。项目里的
|
如果浏览器和后端不在同一内网,WebRTC 媒体链路通常需要 TURN。项目里的
|
||||||
`docker-compose.yaml` 已经带了 coturn,用 `remote` profile 启动即可。
|
`docker-compose.yaml` 已经带了 coturn,用 `remote` profile 启动即可。
|
||||||
|
|
||||||
#### 6.1 配项目根目录 `.env`
|
#### 6.1 确认公网/私网 IP
|
||||||
|
|
||||||
项目根目录的 `.env` 给 coturn 容器用:
|
查询云主机默认路由使用的私网 IP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip -4 route get 1.1.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
输出中 `src` 后的地址是 `PRIVATE_IP`。例如:
|
||||||
|
|
||||||
|
```text
|
||||||
|
1.1.1.1 via 10.0.0.1 dev eth0 src 10.0.0.8
|
||||||
|
```
|
||||||
|
|
||||||
|
云控制台中绑定到该主机、用于 SSH 的公网地址是 `PUBLIC_IP`。
|
||||||
|
|
||||||
|
#### 6.2 配项目根目录 `.env`
|
||||||
|
|
||||||
|
Docker Compose 会把项目根 `.env` 中的 ICE 配置同时传给 API 和 coturn:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
PUBLIC_IP=<远程机器公网IP>
|
PUBLIC_IP=<远程机器公网IP>
|
||||||
|
PRIVATE_IP=<远程机器私网IP>
|
||||||
TURN_SECRET=<一串足够长的随机密钥>
|
TURN_SECRET=<一串足够长的随机密钥>
|
||||||
|
STUN_URL=stun:<远程机器公网IP>:3478
|
||||||
|
TURN_URLS=turn:<远程机器公网IP>:3478?transport=udp,turn:<远程机器公网IP>:3478?transport=tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
PUBLIC_IP=118.89.89.89
|
PUBLIC_IP=118.89.89.89
|
||||||
|
PRIVATE_IP=10.0.0.8
|
||||||
TURN_SECRET=change-me-to-a-long-random-string
|
TURN_SECRET=change-me-to-a-long-random-string
|
||||||
```
|
STUN_URL=stun:118.89.89.89:3478
|
||||||
|
|
||||||
#### 6.2 配后端 `backend/.env`
|
|
||||||
|
|
||||||
后端用同一个 `TURN_SECRET` 签发短时 TURN 凭证:
|
|
||||||
|
|
||||||
```text
|
|
||||||
TURN_URLS=turn:<远程机器公网IP>:3478?transport=udp,turn:<远程机器公网IP>:3478?transport=tcp
|
|
||||||
TURN_SECRET=<和项目根 .env 一致的密钥>
|
|
||||||
```
|
|
||||||
|
|
||||||
示例:
|
|
||||||
|
|
||||||
```text
|
|
||||||
TURN_URLS=turn:118.89.89.89:3478?transport=udp,turn:118.89.89.89:3478?transport=tcp
|
TURN_URLS=turn:118.89.89.89:3478?transport=udp,turn:118.89.89.89:3478?transport=tcp
|
||||||
TURN_SECRET=change-me-to-a-long-random-string
|
|
||||||
```
|
```
|
||||||
|
|
||||||
改完 `backend/.env` 后,重启后端 `uvicorn`。
|
`STUN_URL` 指向同机 coturn,可避免后端等待不可达的公共 STUN。
|
||||||
|
Compose 的 `environment` 会覆盖 `backend/.env` 中的同名值,因此 Docker
|
||||||
|
部署必须在项目根 `.env` 设置上述变量。
|
||||||
|
|
||||||
#### 6.3 启动 coturn
|
#### 6.3 启动 coturn
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose --profile remote up -d coturn
|
docker compose config --quiet
|
||||||
|
docker compose --profile remote up -d --build --force-recreate api coturn
|
||||||
```
|
```
|
||||||
|
|
||||||
查看状态和日志:
|
查看状态和日志:
|
||||||
@@ -259,7 +268,7 @@ curl -k https://<远程机器IP或域名>:18189/api/webrtc/ice-servers
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"iceServers": [
|
"iceServers": [
|
||||||
{"urls": "stun:stun.l.google.com:19302"},
|
{"urls": "stun:118.89.89.89:3478"},
|
||||||
{
|
{
|
||||||
"urls": "turn:<远程机器公网IP>:3478?transport=udp",
|
"urls": "turn:<远程机器公网IP>:3478?transport=udp",
|
||||||
"username": "...",
|
"username": "...",
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ services:
|
|||||||
DATABASE_URL: "postgresql+asyncpg://postgres:${POSTGRES_PASSWORD:-postgres}@postgres:5432/postgres"
|
DATABASE_URL: "postgresql+asyncpg://postgres:${POSTGRES_PASSWORD:-postgres}@postgres:5432/postgres"
|
||||||
# 3030 = docker ui 宿主端口;3000 = 宿主机裸跑 npm run dev 时的端口
|
# 3030 = docker ui 宿主端口;3000 = 宿主机裸跑 npm run dev 时的端口
|
||||||
CORS_ORIGINS: "http://localhost:3030,http://127.0.0.1:3030,http://localhost:3000,http://127.0.0.1:3000"
|
CORS_ORIGINS: "http://localhost:3030,http://127.0.0.1:3030,http://localhost:3000,http://127.0.0.1:3000"
|
||||||
# WebRTC TURN(公网部署:设 PUBLIC_IP + TURN_SECRET,并 docker compose --profile remote up)
|
# WebRTC ICE(公网部署:STUN 指向同机 coturn,避免等待不可达的公共 STUN)
|
||||||
|
STUN_URL: "${STUN_URL:-stun:stun.l.google.com:19302}"
|
||||||
TURN_URLS: "${TURN_URLS:-}"
|
TURN_URLS: "${TURN_URLS:-}"
|
||||||
TURN_SECRET: "${TURN_SECRET:-}"
|
TURN_SECRET: "${TURN_SECRET:-}"
|
||||||
S3_ENDPOINT_URL: "http://rustfs:9000"
|
S3_ENDPOINT_URL: "http://rustfs:9000"
|
||||||
@@ -118,7 +119,7 @@ services:
|
|||||||
networks: [app-network]
|
networks: [app-network]
|
||||||
|
|
||||||
# ---- 可选(profile: remote):WebRTC 公网穿透 ----
|
# ---- 可选(profile: remote):WebRTC 公网穿透 ----
|
||||||
# 在项目根 .env 设置 PUBLIC_IP(云主机公网 IP)与 TURN_SECRET,与 backend TURN_SECRET 一致。
|
# 在项目根 .env 设置 PUBLIC_IP、PRIVATE_IP 与 TURN_SECRET。
|
||||||
# 云安全组放行:UDP/TCP 3478,UDP 49152-49200。
|
# 云安全组放行:UDP/TCP 3478,UDP 49152-49200。
|
||||||
coturn:
|
coturn:
|
||||||
image: coturn/coturn:4.8.0
|
image: coturn/coturn:4.8.0
|
||||||
@@ -128,11 +129,12 @@ services:
|
|||||||
- -n
|
- -n
|
||||||
- --log-file=stdout
|
- --log-file=stdout
|
||||||
- --listening-port=3478
|
- --listening-port=3478
|
||||||
- --listening-ip=0.0.0.0
|
- --listening-ip=${PRIVATE_IP:?set PRIVATE_IP in .env for coturn}
|
||||||
- --external-ip=${PUBLIC_IP:?set PUBLIC_IP in .env for coturn}
|
- --relay-ip=${PRIVATE_IP:?set PRIVATE_IP in .env for coturn}
|
||||||
|
- --external-ip=${PUBLIC_IP:?set PUBLIC_IP in .env for coturn}/${PRIVATE_IP:?set PRIVATE_IP in .env for coturn}
|
||||||
- --realm=ai-video
|
- --realm=ai-video
|
||||||
- --use-auth-secret
|
- --use-auth-secret
|
||||||
- --static-auth-secret=${TURN_SECRET:-changeme}
|
- --static-auth-secret=${TURN_SECRET:?set TURN_SECRET in .env for coturn}
|
||||||
- --min-port=49152
|
- --min-port=49152
|
||||||
- --max-port=49200
|
- --max-port=49200
|
||||||
- --no-cli
|
- --no-cli
|
||||||
|
|||||||
@@ -56,4 +56,4 @@ icon: plug
|
|||||||
{"assistant_id":"asst_xxx"}
|
{"assistant_id":"asst_xxx"}
|
||||||
```
|
```
|
||||||
|
|
||||||
WebRTC 在公网环境通常需要 TURN。设置 `TURN_URLS` 和 `TURN_SECRET` 后,可通过 `GET /api/webrtc/ice-servers` 获取浏览器 ICE 配置。
|
WebRTC 在公网环境通常需要 TURN。设置 `STUN_URL`、`TURN_URLS` 和 `TURN_SECRET` 后,可通过 `GET /api/webrtc/ice-servers` 获取浏览器 ICE 配置。远程部署建议让 `STUN_URL` 指向同机 coturn,避免等待不可达的公共 STUN。
|
||||||
|
|||||||
Reference in New Issue
Block a user