ubuntu@ec2:~$ ./deploy-webrtc.sh
[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
TypeProtocolPortSourceDescription
SSHTCP22My IPSSH 登录
Custom TCPTCP80800.0.0.0/0WebSocket signaling
Custom TCPTCP34780.0.0.0/0TURN TCP
Custom UDPUDP34780.0.0.0/0STUN/TURN UDP
Custom UDPUDP49152-492000.0.0.0/0TURN 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 IPsAllocate Elastic IP address
  • 保持默认网络边界 → Allocate
  • 选中新 IP → ActionsAssociate Elastic IP address
  • Resource type = Instance → 选 webrtc-signalAssociate
记录公网地址
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 // up
docker-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/tcp
turn
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 // iceServers
app-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 proxy

HTTPS 页面 → 必须用 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/tcp443/tcp

无域名:Cloudflare Quick Tunnel

  • 适合测试,不适合长期生产
  • 无需域名、无需在 EC2 配 TLS
  • trycloudflare.com 地址临时,重启可能变化
cloudflared tunnel --url http://127.0.0.1:8080 # → wss://xxx.trycloudflare.com/ws
15

常用运维命令

operations // cheatsheet
cd /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.6
15.1 healthz 不通
docker compose psufw status、安全组 8080/tcp 是否放行 0.0.0.0/0。
15.2 连上了但没画面
通常是 TURN 问题:检查 iceServers、TURN_PASSWORD 一致、放行 3478/udp49152-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.12
15.7 报 SQL Server 不支持
AMI 选错了,换成普通 Ubuntu Server 22.04/24.04,不要选 SQL Server / Windows。
15.8 turnutils Connection refused
TURN 没在监听或容器没起来:查 docker compose pslogs turnss -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!
01 / 21