ubuntu@ec2:~$ ./deploy-webrtc.sh
[INFO] Loading deployment manual ...
[OK] signal + coturn ready to deploy
[INFO] Loading deployment manual ...
[OK] signal + coturn ready to deploy
AWS EC2 部署
WebRTC 信令和 TURN
Node.js WebSocket 信令服务 + coturn STUN/TURN · 外网可访问部署手册
server.js
coturn
EC2 Ubuntu
▌
01
架构总览
overview // architecture外网浏览器 / App
ws://EC2公网IP:8080/ws · turn:EC2公网IP:3478
↓
AWS EC2 Ubuntu
Node.js WebSocket 信令 · coturn STUN/TURN
局域网能通 ≠ 外网能通。外网 WebRTC 至少需要:
- 信令服务 — 交换 offer / answer / candidate(server.js)
- STUN / TURN — 建立媒体连接,外网与移动网络必须配置 TURN
- 公网 IP / 域名 — 外网设备访问不到 192.168.x.x / 10.x.x.x
02
准备信息
prerequisites- AWS 区域 — 建议选离用户近的区域
- EC2 系统 — Ubuntu 22.04 / 24.04 LTS
- 实例规格 — 测试 t3.micro / t3.small
- 公网地址 — 建议绑定 Elastic IP
- ⚠ 大陆用户:AWS 海外区域延迟与可达性可能不稳定
- 测试时用手机 4G/5G 与不同宽带一起验证
服务端口
8080/tcp # WebSocket signaling
3478/tcp # TURN TCP
3478/udp # STUN/TURN UDP
49152-49200/udp # TURN relay UDP
03
新建 EC2 实例
console // launch instance- 登录 AWS 控制台 → 搜索进入 EC2
- 右上角选区域:新加坡 / 东京 / 俄勒冈
- EC2、Elastic IP、安全组须在同一区域
- 点击 Launch instances
- Name:webrtc-signal
- Key pair:新建 → RSA → .pem
- .pem 只能下载一次,丢失不可重下
镜像与规格
image = Ubuntu Server 22.04/24.04 LTS
type = t3.micro # 多人通话用 t3.small
key = webrtc-signal-key.pem
✗ 不要选:SQL Server / Windows / DL / ECS Optimized
04
网络与安全组
security group // inbound rules| Type | Protocol | Port | Source | Description |
|---|---|---|---|---|
| SSH | TCP | 22 | My IP | SSH 登录 |
| Custom TCP | TCP | 8080 | 0.0.0.0/0 | WebSocket signaling |
| Custom TCP | TCP | 3478 | 0.0.0.0/0 | TURN TCP |
| Custom UDP | UDP | 3478 | 0.0.0.0/0 | STUN/TURN UDP |
| Custom UDP | UDP | 49152-49200 | 0.0.0.0/0 | TURN relay UDP |
- Auto-assign public IP → Enable
- 安全组名建议 webrtc-signal-sg · 存储建议 20 GB gp3
- IPv6 需求才加 ::/0 规则,纯 IPv4 测试可先不加
- 启动后等待 Running + 2/2 checks passed
05
绑定 Elastic IP
elastic ip // associate- 左侧菜单 → Elastic IPs → Allocate Elastic IP address
- 保持默认网络边界 → Allocate
- 选中新 IP → Actions → Associate Elastic IP address
- Resource type = Instance → 选 webrtc-signal → Associate
记录公网地址
EC2_PUBLIC_IP=你的ElasticIP
⚠ 已绑定的 Elastic IP 一般不产生空闲费用;分配后未绑定则可能计费
06
SSH 登录
ssh // remote access
本地终端
chmod 400 ~/Downloads/webrtc-signal-key.pem
ssh -i ~/Downloads/webrtc-signal-key.pem ubuntu@你的ElasticIP
# 登录成功提示
ubuntu@ip-xxx-xxx-xxx-xxx:~$
- Windows:PowerShell / Windows Terminal 可直接用 ssh,私钥路径改成实际 .pem
- 权限报错时把 .pem 放到用户目录下再试
- 默认用户名是 ubuntu(不是 root / ec2-user)
07
初始化系统 + Docker
provision // docker系统初始化
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl git ufw
sudo apt install -y docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker ubuntu
- 装完执行 exit 重新登录,让 docker 用户组生效
- 验证:docker --version / docker compose version
- docker compose(空格)是新版命令
- docker-compose(连字符)是旧版,Ubuntu 26.04 可能没有
- 新版可用就用 docker compose,无需装旧版
08
上传 / 拉取项目代码
deploy // source code方式 A:Git 拉取
git clone 你的Git仓库地址 webrtc-signal
cd webrtc-signal
方式 B:本地上传
scp -i ~/Downloads/webrtc-signal-key.pem -r /home/q/webrtc-signal ubuntu@你的ElasticIP:/home/ubuntu/webrtc-signal
cd /home/ubuntu/webrtc-signal
· 带 node_modules 上传不影响构建但会变慢 · 正式建议用 Git 或排除 node_modules
09
配置环境变量
config // .env.env 示例
HOST=0.0.0.0
PORT=8080
WS_PATH=/ws
MAX_ROOM_SIZE=8
PUBLIC_HOST=你的ElasticIP
TURN_EXTERNAL_IP=你的ElasticIP
TURN_USER=webrtc
TURN_PASSWORD=换成至少16位的随机密码
TURN_REALM=你的ElasticIP
- cp .env.example .env 然后 nano .env
- PUBLIC_HOST 填公网 IP 或域名
- TURN_EXTERNAL_IP 建议填 Elastic IP 数字地址
- TURN_PASSWORD 不要用示例密码
- 绑域名后 TURN_REALM 可改成域名
openssl rand -base64 24 # 生成随机密码
10
启动 WebRTC 服务
docker compose // updocker-compose.yml 会启动两个服务
docker compose up -d --build
docker compose ps
docker compose logs -f signal
docker compose logs -f turn
signal
Node.js WebSocket 信令服务 · 监听
8080/tcpturn
coturn · 监听
3478/tcp + 3478/udp + 49152-49200/udp
11
验证信令服务
verify // healthz本机 / 公网测试
curl http://127.0.0.1:8080/healthz
{"ok":true}
curl http://你的ElasticIP:8080/healthz
公网不通,按顺序检查:
- 安全组是否放行 8080/tcp
- ufw 是否放行 8080/tcp
- docker compose ps 中 signal 是否为 Up
- .env 中 PORT 是否仍是 8080
12
验证 TURN 服务
verify // trickle ice① 浏览器 Trickle ICE 测试
- 打开 webrtc.github.io/samples/.../trickle-ice
- STUN:stun:你的ElasticIP:3478
- TURN:turn:你的ElasticIP:3478?transport=udp
- 点 Gather candidates
- 出现 typ relay → TURN 可用
- 只有 host/srflx → TURN 未正常工作
② 命令行测试
sudo apt install -y coturn
turnutils_uclient -u webrtc -w 你的TURN_PASSWORD 你的ElasticIP
# 能看到连接与收发统计 → TURN 基本可用
13
配置客户端
client // iceServersapp-webrtc-config.example.js
const PUBLIC_HOST = "你的ElasticIP";
const webrtcConfig = {
signalingUrl: `ws://${PUBLIC_HOST}:8080/ws`,
iceServers: [
{ urls: [`stun:${PUBLIC_HOST}:3478`] },
{ urls: [`turn:${PUBLIC_HOST}:3478?transport=udp`, `turn:${PUBLIC_HOST}:3478?transport=tcp`],
username: "webrtc", credential: "你的TURN_PASSWORD" },
],
};
- 不要再用 127.0.0.1
- 不要再用 192.168.x.x / 10.x.x.x / 172.16.x.x
- 外网设备必须能访问 ws://你的ElasticIP:8080/ws
- 移动网络 / 公司网络 / 跨运营商 → 强制配置 TURN
14
HTTPS / WSS
tls // reverse proxyHTTPS 页面 → 必须用 wss://
Caddy — /etc/caddy/Caddyfile
webrtc.example.com {
reverse_proxy /ws 127.0.0.1:8080
reverse_proxy /healthz 127.0.0.1:8080
}
需要额外放行 80/tcp、443/tcp
无域名:Cloudflare Quick Tunnel
- 适合测试,不适合长期生产
- 无需域名、无需在 EC2 配 TLS
- trycloudflare.com 地址临时,重启可能变化
cloudflared tunnel --url http://127.0.0.1:8080
# → wss://xxx.trycloudflare.com/ws
15
常用运维命令
operations // cheatsheetcd /home/ubuntu/webrtc-signal
docker compose up -d # 启动
docker compose down # 停止
docker compose restart # 重启
docker compose up -d --build # 更新镜像并重建
docker compose ps # 查看状态
docker compose logs -f signal|turn # 查看日志
sudo ss -lntup # 查看端口监听
16
常见问题 · 连接 / 媒体
faq // 15.1–15.615.1 healthz 不通
查
docker compose ps、ufw status、安全组 8080/tcp 是否放行 0.0.0.0/0。15.2 连上了但没画面
通常是 TURN 问题:检查 iceServers、TURN_PASSWORD 一致、放行
3478/udp 与 49152-49200/udp、Trickle ICE 出现 relay。15.3 局域网通、外网不通
客户端是否还写着 192.168/10./172.16/localhost/127.0.0.1,须改成 Elastic IP 或域名。
15.4 手机 4G/5G 不通
移动网络常需 TURN 中继,保证
turn:...:3478?transport=udp 可用且能产生 relay。15.5 HTTPS 但用了 ws
HTTPS 页面不能用 ws://,须改用 wss://(域名 + TLS 反向代理)。
15.6 多人 / 多路卡顿
升级实例规格、扩大 relay UDP 范围、降分辨率/码率、减少同房人数、用更近的区域。
17
常见问题 · 部署 / SSH
faq // 15.7–15.1215.7 报 SQL Server 不支持
AMI 选错了,换成普通 Ubuntu Server 22.04/24.04,不要选 SQL Server / Windows。
15.8 turnutils Connection refused
TURN 没在监听或容器没起来:查
docker compose ps、logs turn、ss -lntup | grep 3478,检查 .env 的 TURN 配置;日志报 no-cli deprecated 就删掉 --no-cli 参数。15.9 SSH 连接超时
非私钥问题:确认实例 Running、IP 正确、安全组放行
22/tcp;建议只放行自己的公网出口 IP(curl ifconfig.me)。15.10 私钥权限错误
UNPROTECTED PRIVATE KEY → 本地
chmod 400 xxx.pem 再连;用户名是 ubuntu。15.11 SSH 空闲断线
加保活参数
-o ServerAliveInterval=30 -o ServerAliveCountMax=3,或写进 ~/.ssh/config;长任务用 tmux。15.12 断开后服务会停吗
不会:
up -d 后台运行 + restart: unless-stopped + docker 开机自启。退出日志用 Ctrl+C 不停服务。
18
AWS 成本提醒
cost // billing可能产生费用的资源:
- EC2 实例 — 运行时间计费
- EBS 磁盘 — 存储容量计费
- Elastic IP — 未绑定或实例停止时可能计费
- 公网流量 — 出站流量计费
不用时:停止 EC2(停止后服务不可访问)
长期不用:释放 Elastic IP、删除实例与磁盘,避免继续计费
19
最小可用检查清单
checklist // go-live- EC2 状态是 Running
- Elastic IP 已绑定到 EC2
- 安全组放行 8080/tcp
- 安全组放行 3478/tcp
- 安全组放行 3478/udp
- 安全组放行 49152-49200/udp
- Ubuntu ufw 放行同样端口
- docker compose ps 显示 signal / turn 都是 Up
- curl healthz 返回 {"ok":true}
- Trickle ICE 测试出现 typ relay
- 客户端 signalingUrl 用公网 IP 或域名
- 客户端 iceServers 用公网 IP 或域名
20
参考
references- AWS EC2 启动实例 — docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html
- AWS 安全组入站规则 — docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules.html
- AWS Elastic IP — docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html
- WebRTC Trickle ICE 测试页 — webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
ubuntu@ec2:~$ systemctl status webrtc ● active (running)
Deployment complete. Happy WebRTC!