Skip to main content

WebRTC 全景实战 (14):TURN 集群部署与多区域扩展

Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡
Rainy
13 MIN READ... VIEWS

"TURN 是 WebRTC 基础设施中带宽成本最高的组件——relay 流量约占 10–30%。" — 生产运维共识

Ch5 ICE/STUN/TURN 讲了 TURN 原理。本章落地生产级 coturn 集群——从单机 Docker 到多区域 GeoDNS 部署,覆盖凭证安全、带宽成本建模与容量规划。

Serge Lachapelle 在 Curious 历史访谈 中回忆:Marratech 时代企业网内 ICE 几乎总是成功,但扩展到公网后 TURN relay 成为必需品——Today Meet/LiveKit 全球部署中,TURN 集群的运维复杂度不亚于 SFU 本身。

配套 Lab:examples/webrtc-lab/docker/coturn/docker-compose.yml


本篇术语表

术语英文解释
TURNTraversal Using Relays around NATNAT 穿透失败时的中继服务器
STUNSession Traversal Utilities for NAT获取公网 IP 的轻量服务
coturn最流行的开源 TURN/STUN 服务器
Allocate分配TURN 客户端请求 relay 地址的操作
Relay Address中继地址TURN 分配给客户端的流量转发地址
TURN REST APIRFC 8656 定义的短期凭证机制
lt-cred-mechLong-term Credentialcoturn 长期凭证模式(开发用)
use-auth-secretcoturn 短期 HMAC 凭证模式(生产用)
GeoDNS地理 DNS根据客户端位置解析到最近 TURN 节点
Anycast任播同一 IP 多区域广播(高级部署)
Relay Ratio中继占比走 TURN relay 的连接占总连接的比例
ICE-lite简化 ICE 实现,服务端不主动连通性检查
ChannelBind通道绑定TURN 优化模式,减少协议开销
Permission权限TURN 允许 relay 的目标 IP 白名单

一、生产拓扑

组件部署策略说明
SFU区域亲和 + Redis Mesh同 Room 参与者尽量同区域
TURN每区域独立集群客户端连接最近 TURN
STUN可与 TURN 共用 coturn轻量,可全局单点
DNSGeoDNS 或 Anycast客户端无感知选最近节点

Marratech 在瑞典企业网时代几乎不需要 TURN;Google 收购后在 Meet 全球部署中,TURN 成为带宽成本最高的基础设施组件之一——relay 流量是 SFU 转发的 2 倍(入站 + 出站各计一次)。


二、coturn 生产配置要点

2.1 开发环境(Lab 用)

examples/webrtc-lab/docker/coturn/docker-compose.yml

services:
coturn:
image: coturn/coturn:latest
ports:
- "3478:3478/udp"
- "3478:3478/tcp"
- "49152-49200:49152-49200/udp"
command: >
-n --log-file=stdout
--lt-cred-mech
--user=test:test123
--realm=webrtc.lab
--min-port=49152
--max-port=49200

2.2 生产环境配置

# /etc/turnserver.conf 核心配置

listening-port=3478
tls-listening-port=443
listening-ip=0.0.0.0
relay-ip=<SERVER_PUBLIC_IP>
external-ip=<SERVER_PUBLIC_IP>

use-auth-secret
static-auth-secret=<YOUR_HMAC_SECRET>
realm=turn.example.com

no-multicast-peers
no-cli
stale-nonce=600

max-bps=3000000
user-quota=10
total-quota=5000

min-port=49152
max-port=65535

log-file=/var/log/turnserver.log
verbose
prometheus
配置项开发生产说明
认证--lt-cred-mech --user=test:test123use-auth-secret生产禁止明文密码
TLS可选必须 443穿透企业防火墙
--max-bps不限3M–5M防止单用户占满带宽
端口范围49152-4920049152-65535每个 relay 会话占用 1 端口
--no-multicast-peers必须WebRTC 不需要多播
开放 49152-65535 UDP 是必须的

TURN relay 为每个会话分配一个 UDP 端口。安全组/防火墙必须开放此范围,否则 Allocate 成功但 media relay 失败——这是最常见的生产部署错误。

2.3 NAT 后的 coturn 部署

云服务器部署时务必设置 --external-ip=<公网IP>/<内网IP>,否则 relay 地址可能是内网 IP,客户端无法连接。


三、TURN REST API 短期凭证

RFC 8656 定义 REST API 短期凭证机制:

3.1 凭证生成(Node.js)

// examples/webrtc-lab/signaling/ 扩展
import crypto from "crypto";

function generateTurnCredentials(secret, identity, ttlSeconds = 86400) {
const timestamp = Math.floor(Date.now() / 1000) + ttlSeconds;
const username = `${timestamp}:${identity}`;
const hmac = crypto.createHmac("sha1", secret);
hmac.update(username);
const credential = hmac.digest("base64");

return {
username,
credential,
ttl: ttlSeconds,
iceServers: [
{
urls: [
"turn:turn.example.com:443?transport=tcp",
"turn:turn.example.com:443?transport=udp",
"turns:turn.example.com:443?transport=tcp",
],
username,
credential,
},
{
urls: "stun:stun.example.com:3478",
},
],
};
}

app.post("/rooms/join", (req, res) => {
const { roomId, identity } = req.body;
const turnCreds = generateTurnCredentials(process.env.TURN_SECRET, identity);
res.json({
roomId,
identity,
iceServers: turnCreds.iceServers,
});
});

3.2 凭证时效与刷新

参数推荐值说明
TTL24h(86400s)长会议足够,短于攻击窗口
刷新阈值剩余 10min客户端主动刷新
Secret 轮换每 90 天双 Secret 过渡期

四、带宽成本模型

4.1 TURN 带宽计算公式

单用户 TURN 消耗 = 媒体码率 × 2(入站 relay + 出站 relay)
1000 用户 TURN 总消耗 = 1000 × relay_ratio × 码率 × 2

示例:
1000 用户,25% relay,720p 1.5Mbps
= 1000 × 0.25 × 1.5M × 2
= 750 Mbps TURN 带宽
场景码率relay 占比1000 用户 TURN 带宽
音频 only50 kbps20%20 Mbps
360p 视频500 kbps25%250 Mbps
720p 视频1.5 Mbps25%750 Mbps
1080p 视频3 Mbps30%1.8 Gbps

4.2 单节点容量规划

实例规格网卡带宽720p relay 会话数(估算)
4 vCPU / 8GB1 Gbps~300 并发 relay
8 vCPU / 16GB5 Gbps~1500 并发 relay
16 vCPU / 32GB10 Gbps~3000 并发 relay

估算假设:每路 relay 720p ≈ 1.5Mbps × 2 = 3Mbps TURN 消耗。

4.3 成本优化策略


五、多区域部署策略

5.1 GeoDNS

策略复杂度效果适用
GeoDNS按地理位置解析大多数场景
客户端测速并行 STUN 多个区域,选 RTT 最低对延迟敏感
Anycast同一 IP 多区域广播大规模全球部署
SFU 内置 TURNLiveKit 自动管理LiveKit Cloud/自托管

5.2 客户端并行测速选 TURN

async function selectBestTurn(turnServers) {
const results = await Promise.all(
turnServers.map(async (server) => {
const start = performance.now();
try {
const pc = new RTCPeerConnection({ iceServers: [server] });
pc.createDataChannel("probe");
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
await new Promise((resolve) => {
pc.onicecandidate = (e) => {
if (e.candidate?.type === "relay") resolve();
if (!e.candidate) resolve();
};
setTimeout(resolve, 3000);
});
pc.close();
return { server, rtt: performance.now() - start };
} catch {
return { server, rtt: Infinity };
}
})
);
return results.sort((a, b) => a.rtt - b.rtt)[0].server;
}

5.3 LiveKit 内置 TURN

LiveKit Cloud 自动管理 TURN 凭证和区域选择。自托管配置:

# livekit.yaml
turn:
enabled: true
domain: turn.example.com
tls_port: 443
udp_port: 443
external_tls: true

详见 LiveKit TURN 文档LiveKit 介绍


六、高可用与扩容

指标告警阈值扩容动作
活跃会话数> 单节点 80% 容量加 coturn 实例
出站带宽> 节点网卡 70%加节点或升级带宽
Allocate 失败率> 1%查端口范围/Secret
凭证验证失败> 0.1%查 Secret 同步/时钟偏移

6.1 coturn Prometheus 指标

# turnserver.conf
prometheus
# 默认 :9641/metrics

# 关键指标
# turn_total_allocations
# turn_total_traffic_bytes
# turn_total_traffic_packets

6.2 coturn 集群限制

coturn 无原生集群状态同步

coturn 实例之间不共享 relay 会话状态。LB 应使用 session affinity(源 IP 粘性),或让每个客户端独立 Allocate。Secret 必须在所有实例间同步。


七、安全加固

措施配置说明
短期凭证use-auth-secret禁止长期密码
带宽配额--max-bps=3000000单用户上限
会话配额--user-quota=10防滥用
TLS 必须--tls-listening-port=443加密 + 防火墙穿透
禁用 CLI--no-cli防远程管理攻击
日志审计--verbose + 集中采集追踪异常 Allocate

八、防火墙与企业网络

端口协议用途必须
3478UDP/TCPSTUN/TURN
443TCP/TLSTURNS 穿透防火墙生产必须
49152-65535UDPTURN relay 媒体是(UDP relay)

九、常见陷阱

#陷阱现象修复
1未开放 UDP 49152-65535Allocate 成功但无 media安全组开放端口范围
2external-ip 未配置relay 地址是内网 IP设置 --external-ip
3长期凭证泄露带宽被滥用切换 REST API 短期凭证
4单 TURN 节点跨区 RTT 高多区域 GeoDNS
5忽略 relay 占比成本失控Prometheus 监控 + 告警
6STUN/TURN 混用同一域名DNS 解析混乱STUN 和 TURN 分开域名
7时钟偏移凭证验证失败NTP 同步所有节点
8LB 无 session affinityrelay 会话中断源 IP 粘性或客户端重 Allocate
9仅 TCP TURN 无 UDP媒体延迟高优先 UDP relay + TCP fallback
10Secret 不一致部分节点验证失败配置管理同步 Secret

十、实战 Lab

Lab 1:Docker coturn 启动与验证

cd examples/webrtc-lab/docker/coturn
docker compose up -d

turnutils_stunclient localhost
turnutils_uclient -T -u test -w test123 localhost

Lab 2:浏览器 TURN 验证

const pc = new RTCPeerConnection({
iceServers: [{
urls: "turn:localhost:3478",
username: "test",
credential: "test123",
}],
});
pc.createDataChannel("test");
pc.createOffer().then((o) => pc.setLocalDescription(o));
pc.onicecandidate = (e) => {
if (e.candidate) console.log(e.candidate.type, e.candidate.address);
};

Lab 3:REST API 凭证

  1. 部署带 use-auth-secret 的 coturn
  2. generateTurnCredentials() 生成凭证
  3. 浏览器连接,确认 relay 成功

Lab 4:带宽限制验证

配置 --max-bps=500000,720p 通话观察画质限制在 ~500kbps。

Lab 5:relay 占比统计

const stats = await pc.getStats();
stats.forEach((r) => {
if (r.type === "candidate-pair" && r.state === "succeeded" && r.nominated) {
console.log("Local:", r.localCandidateType, "Remote:", r.remoteCandidateType);
}
});

Lab 6:TLS 443 穿透

配置 tls-listening-port=443 + 证书,验证 turns: 连接成功。

Lab 7:跨区 TURN 延迟对比

  1. 部署两个区域 coturn(或模拟 DNS 解析)
  2. selectBestTurn() 测 RTT
  3. 对比选优前后 ICE 连接时间

十一、本章小结

概念要点
coturn最流行的 TURN 服务器,生产必配 TLS 443
REST API短期 HMAC 凭证,禁止长期密码
带宽成本relay = 码率 × 2 × relay 占比
多区域GeoDNS + 区域 TURN 集群
监控relay 占比 + 带宽 + Allocate 失败率
安全max-bps + user-quota + use-auth-secret
历史Marratech 企业网 → 公网 TURN 必需品

下一篇(Ch15)Capstone 视频会议系统


系列导航

章节主题状态
0架构全景与协议栈地图✅ 已发布
1浏览器媒体 API 与设备管理✅ 已发布
2第一个 P2P 视频通话✅ 已发布
3信令服务器设计与会话状态机✅ 已发布
4SDP 解剖与媒体协商✅ 已发布
5ICE、STUN、TURN 与 NAT 穿透✅ 已发布
6Data Channel 与 SCTP over DTLS✅ 已发布
7DTLS 握手与 SRTP 加密体系✅ 已发布
8RTP/RTCP 媒体传输与 QoS✅ 已发布
9音视频编解码与 Simulcast 入门✅ 已发布
10带宽估计与拥塞控制 GCC✅ 已发布
11Simulcast、SVC 与选择性订阅✅ 已发布
12SFU/MCU/Mesh 架构与 Pion 实战✅ 已发布
13调试工具链与可观测性✅ 已发布
14TURN 集群部署与多区域扩展✅ 已发布
15Capstone 生产级视频会议系统✅ 已发布

References

Logo
RainLib

Exploring the frontiers of technology, design, and distributed systems. Building tools for the future developers.

Suggestions & Feedback

© 2026 RainLib. Built for the Future.
All rights reserved.
System Normal