Jitsi Meet自部署实战:从WebRTC原理到TURN服务器与端口映射
团队成员分散在几个城市,每次项目周会都要临时借第三方视频会议软件:免费版限时40分钟,企业版按年订阅,而且会议数据总是要绕一圈别人的服务器。时间长了,还是觉得不踏实。后来我在公司的一台闲置服务器上自己部署了 Jitsi Meet,完全开源,数据只走自己的机器,访问权限自己控制。整个过程从安装到把服务开放到公网,前前后后调试了两天。这篇博文就把完整的本地部署过程和"外网能访问"的配置细节写清楚,包括 TURN 服务器、路由器端口映射、ICE 连接排查这类网上资料常常语焉不详的部分,适合有基本 Linux 操作基础、想自己掌控视频会议系统的读者参考。
1. 为什么放弃第三方会议服务,自己搭一套
1.1 第三方视频会议的隐性成本
很多人觉得直接用 Zoom、腾讯会议、钉钉会议很方便,确实,注册账号就能开会。但对于一个长期有内部会议需求的团队来说,痛点会逐渐冒出来。
第一是费用和人数限制。免费版大多限制会议时长、会议人数,想开一场超过 50 人的全员培训,或者想开一场不被打断的长会议,基本就得开会员。第二是数据隐私问题。内部项目讨论、产品方案评审、客户沟通,这些内容都会经过第三方服务器,虽然多数平台宣称加密传输,但"加密"和"不出自家服务器"是两回事。第三是定制能力。品牌、Logo、会议页面的措辞、录制文件的存储位置,都不归你管。第四是稳定性依赖对方的服务状态,一旦对方 CDN 或者机房出问题,会议说断就断。
这些问题的本质是:你把自己的会议基础设施托管给了别人。对于偶尔开会的人来说这不是问题,但对于每周都有固定例会、月度有全员会、偶尔还有公开分享活动的团队来说,自部署一套会议系统是更可控的选择。
1.2 Jitsi Meet 适合谁,又不适合谁
Jitsi Meet 是一套基于 WebRTC 的开源视频会议系统,Apache 2.0 协议,可以免费商用。它最大的特点是免客户端:开会的人在浏览器里打开会议链接就能进,不用装任何插件和 App,这一点对参会者非常友好。
它特别适合以下几类场景:
- 企业内部例会和技术评审,参会人数 20~100 人,需要数据不出内网。
- 开源社区、技术社群做线上分享,演讲者用屏幕共享,听众用浏览器加入。
- 学校或培训机构的在线答疑、小型公开课,需要自己控制会议室入口。
- 对会议内容敏感,希望所有媒体流都走自己服务器的用户。
但也要说清楚它不适合的场景:如果会议动辄几百人、上千人,或者参会者分布在全国各地且网络链路复杂,自部署 Jitsi 的成本会很高,因为你必须有足够带宽和良好的跨网优化能力。另外,如果服务器没有公网 IP,外网接入会非常依赖中继服务器,体验会打折扣。大多数中小团队和企业内网环境,自部署是划算的。
2. 部署前先想明白:架构与网络规划
2.1 Jitsi Meet 到底由哪几个组件组成
很多人以为 Jitsi Meet 是一个单一程序,装完就完事。实际上它是一套组件协同工作。理解这套架构,后面排查问题才能有的放矢。
我用一个表格说明核心组件的作用:
| 组件 | 进程名 | 作用 |
|---|---|---|
| Nginx | nginx | 负责静态页面、反向代理、HTTPS 终结和 WebSocket 转发 |
| Prosody | prosody | XMPP 信令服务器,管理聊天室、用户在线状态、房间权限 |
| Jicofo | jicofo | 会议聚焦组件,负责开会时把媒体流调度给 JVB |
| JVB | jvb | 视频桥接器,所有参会者的音视频流都在这里混合和转发 |
| coturn / turnserver | coturn | TURN 中继服务器,负责 NAT 打洞失败后的流量中转 |
打个比方:Prosody 像是会议室的"前台",它负责接待和登记进出的人;Jicofo 像是"主持人调度员",决定谁什么时候发言;JVB 像是会议室里的"分线器",把每个人的声音画面送到其他人那里;coturn 则像是会议室所在大楼的"门卫",当参会者所在网络不允许直接连线时,由它帮忙中转。
在安装 Jitsi Meet 时,Nginx、Prosody、Jicofo、JVB 会被自动装上,但 coturn 需要单独处理,这也是外网访问最容易出问题的地方。
2.2 域名、DNS 与服务器规格
先说域名。 强烈建议给 Jitsi 配一个域名,比如 meet.example.com,而不是直接用 IP 访问。原因是多方面的:
- Let's Encrypt 免费证书只能签给域名,没有证书,浏览器的音视频权限会被严格限制,很多浏览器的 getUserMedia 接口在非 HTTPS 环境下根本不可用。
- 后续使用中,会议室链接要发给其他人,没有域名的链接又长又难记。
- 域名方便以后做负载均衡、CDN 和邮箱等其它服务的规划。
DNS 解析上,建议将 meet.example.com 的 A 记录直接指向你服务器的公网 IP。如果你有多个公网 IP,可以只解析其中一个。
再说服务器规格。 我个人的经验是:4 核 CPU、8GB 内存、40GB 以上 SSD,带宽至少 10Mbps 上行。Jitsi 对 CPU 和带宽都比较敏感,尤其是 JVB 转发视频流时。如果同时开会的人数不多(10 人以内),2 核 4G 也能跑,但要关闭一些吃资源的转码功能。操作系统推荐 Ubuntu 22.04 LTS,网上资料多,踩坑容易搜到答案。
如果服务器本身没有公网 IP,而是位于内网(比如公司机房),那就要靠路由器的端口映射或光猫的 NAT 来暴露服务,这部分我在第 4 节详细讲。
2.3 端口清单:哪些端口必须对外开放
Jitsi 对外通信的端口不算多,但每一个都有明确用途。部署之前先列个清单,方便后面配置防火墙和路由器。
| 端口 | 协议 | 用途 |
|---|---|---|
| 80 | TCP | Let's Encrypt 证书验证、HTTP 自动跳转 HTTPS |
| 443 | TCP | HTTPS 网页和 WebSocket 信令 |
| 10000 | UDP | RTP 媒体流,即音视频数据 |
| 4443 | TCP | JVB 的 TCP 回退通道,UDP 不通时会用到 |
| 3478 | TCP/UDP | TURN 服务器监听端口 |
| 5349 | TCP | TURN over TLS 加密通道 |
这里面 80 和 443 很容易理解,做 Web 服务必须开。绝大多数人忽略的是 10000/UDP:这是音视频媒体流的主通道,如果这个端口不通,外网用户能打开会议页面、能看到别人的名字,但听不到声音、看不到画面,或者画面严重卡顿。
3. 一步一步搭起来:Ubuntu 22.04 上的安装配置
3.1 基础环境准备
我习惯从头开始操作,确保环境干净。用 Ubuntu 22.04 的服务器,先更新系统:
sudo apt update && sudo apt upgrade -y
然后设置主机名。这里要注意,Jitsi 官方安装脚本会读取系统主机名来判断域名,虽然可以通过安装过程中的交互输入覆盖,但提前把主机名设置正确可以减少不必要的坑:
sudo hostnamectl set-hostname meet.example.com
echo "127.0.0.1 meet.example.com" | sudo tee -a /etc/hosts
接着确认 80 和 443 端口没有被占用,检查服务器时间和时区是否正确。时间偏差过大会导致 TLS 证书验证失败:
timedatectl set-ntp true
如果服务器在防火墙后面,先临时放行 80、443、10000/UDP 和 4443,后面部署完再加固规则。
3.2 添加 Jitsi 官方仓库并安装
Jitsi 官方提供了 apt 仓库,安装很方便。先添加仓库和密钥:
echo 'deb https://download.jitsi.org stable/' | sudo tee /etc/apt/sources.list.d/jitsi-stable.list
curl -sL https://download.jitsi.org/jitsi-key.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/jitsi-keyring.gpg
添加 Keyring 时要注意写法,旧版本的
apt-key add
在新系统上会报警告。正确做法是用
gpg --dearmor
转换成文件后,在 source 列表里指定 Keyring 路径。所以 source 条目应该是这样:
echo 'deb [signed-by=/usr/share/keyrings/jitsi-keyring.gpg] https://download.jitsi.org stable/' | sudo tee /etc/apt/sources.list.d/jitsi-stable.list
然后更新并安装:
sudo apt update
sudo apt install -y jitsi-meet
安装过程中会弹出一个蓝色交互界面,提示输入主机名,这里输入 meet.example.com。接着会问你要不要生成自签名证书。如果你还没有域名的 TLS 证书,可以先选生成自签名,安装完成后再用官方脚本申请 Let's Encrypt 证书。
安装过程一般会自动启动 Nginx、Prosody、Jicofo、JVB 四个服务。安装完后检查一下服务状态:
systemctl status prosody jicofo jvb nginx
正常应该是
active (running)
状态。看到界面上显示在上传日志之类的信息没关系,那是 Jitsi 的可选崩溃上报功能,后面可以关掉。
3.3 申请 Let's Encrypt 免费证书
自签名证书会让浏览器报警告,而且麦克风和摄像头权限可能受限,所以必须换成可信任的证书。Jitsi 提供了一个现成脚本:
sudo /usr/share/jitsi-meet/scripts/install-letsencrypt-cert.sh
脚本会先安装 certbot 插件,然后提示你输入邮箱地址。证书签发成功后,会自动配置一个 cron 任务做续期。我试过多次,只要 80 端口能从公网访问,这个脚本基本一次通过。签发成功后不需要额外操作,Nginx 会自动使用新证书。
有两点提醒:一是证书签发前,必须保证域名 A 记录已经解析到当前服务器的公网 IP,并且 80 端口从外网可达;二是如果你的服务部署在公司内网的机器上,需要先把 80 端口映射到这台机器,否则验证请求到不了 Nginx。
3.4 配置会议认证和用户管理
默认情况下,Jitsi 会议室谁都能开,只要知道你服务器地址,输入任意房间名就能开会。如果你想把服务器限制为内部使用,或者希望每个同事使用独立账号登录,可以启用内部认证。
Jitsi 的认证配置在 Prosody 里。编辑
/etc/prosody/conf.avail/meet.example.com.cfg.lua
,找到下面的配置:
authentication = "internal_plain"
如果你只希望内部人员有权限创建会议室,但允许访客以匿名身份加入会议,使用下面的配置:
authentication = "internal_plain"
app_id = "jitsi"
app_secret = "你的随机字符串"
还需要在
/etc/jitsi/meet/meet.example.com-config.js
中设置:
anonymousdomain: 'guest.meet.example.com',
然后创建用户:
sudo prosodyctl register zhangsan meet.example.com '一个强密码'
执行后没有任何输出就是成功了。重启 Prosody 和 Jicofo 让配置生效:
sudo systemctl restart prosody jicofo
启用认证后,虽然多了一道登录步骤,但能有效防止陌生人占用服务器资源。对于企业内网场景,这一步值得做。
4. 外网访问的真正难点:TURN 服务器与端口映射
4.1 WebRTC 为什么会卡在 NAT 后面
Jitsi 的音视频传输使用 WebRTC 技术。正常情况下,参会者的浏览器会尝试直接向同一会议中的其他参会者发送媒体流,称为 P2P 模式。但在现实网络环境中,大多数终端都位于 NAT 网关后面,IP 地址是私有的,外部设备无法直接找到它。
WebRTC 用 ICE 机制解决这个问题:它收集本机的所有网络候选地址,包括局域网 IP、公网 IP、以及 STUN 服务器返回的映射地址,然后在通话双方之间尝试建立链路。如果网络环境是开放的,链路很容易建立;但如果双方都处于严格的对称 NAT 后面,打洞就会失败。
这时候就需要 TURN 服务器兜底。TURN 协议允许客户端通过中继服务器转发媒体流,虽然会消耗服务器带宽和 CPU,但能保证在几乎所有网络环境下都能建立连接。Jitsi 默认依赖一个公共 STUN 服务器,但如果没有正确配置 TURN 服务器,外网用户一旦遇到对称 NAT,会议就直接卡死在连接中。
4.2 coturn 的安装与配置
Jitsi 官方仓库中附带 coturn 包,但默认不会自动配置。安装命令:
sudo apt install -y coturn
coturn 的主配置在
/etc/turnserver.conf
。我通常会直接写入一套比较健壮的配置:
listening-ip=0.0.0.0
listening-port=3478
tls-listening-port=5349
relay-ip=你的服务器内网IP
fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=请换成一段很长的随机字符串
realm=meet.example.com
total-quota=100
stale-nonce
no-cli
no-multicast-peers
no-tlsv1
no-tlsv1_1
其中
listening-ip
设置为 0.0.0.0 表示监听所有网卡;
relay-ip
要填服务器在局域网内的地址,如果你只有一台公网服务器,也可以填公网 IP,但大多数情况下这台机器在内网,填内网地址即可;
static-auth-secret
是 Jitsi 用来签发 TURN 临时凭证的密钥,必须和 Jitsi 配置里的密钥保持一致,否则外网用户拿到凭证去连接 TURN 服务器会被拒绝。
启动 coturn 并设置开机自启:
sudo systemctl enable coturn
sudo systemctl restart coturn
有个小坑:coturn 默认监听 IPv6 和 IPv4,有些服务器 IPv6 配置混乱会导致启动失败。如果启动失败,可以在配置里加上
listening-ip
的那行之后,再补一行
listening-ip=::1
来禁用 IPv6,或者干脆用
ipv6-black-list
避免监听 IPv6。
4.3 把 coturn 接入 Jitsi 配置
TURN 服务器跑起来后,还需要让 Jitsi 给每个会议参与者签发临时 TURN 凭证。Jitsi 提供了一种
turncredentials
模块,会动态生成带时效的凭证给客户端。
检查 Jitsi 的 Prosody 配置,确认相关模块已经启用。在
/etc/prosody/conf.avail/meet.example.com.cfg.lua
中,应该有类似这样的几行:
ExternalServiceType = "turn"
ExternalServiceName = "meet.example.com"
ExternalServicePort = 3478
ExternalServiceSecret = "与turnserver.conf中一致的随机字符串"
通常会有一个
turncreds
模块配置区域,如果你用的是官方安装脚本,它应该会自动生成
turn
相关配置。但默认生成的 secret 可能与你在 turnserver.conf 里手写的 secret 不一致,所以要检查一下,确保两边相同。
修改后重启 Prosody:
sudo systemctl restart prosody coturn
你也可以用下面的命令测试 TURN 凭证是否正常发放:
prosodyctl mod_turncredentials
4.4 路由器端口映射与 DDNS 动态域名
如果 Jitsi 服务器部署在公司内网,外网流量要到达服务器,必须经过路由器。需要在路由器上做"端口映射"(也叫虚拟服务器、端口转发),把公网 IP 的特定端口转发到内网服务器的对应端口。
以常见的企业路由器为例,一般需要添加以下几组映射规则:
| 公网端口 | 内网 IP 端口 | 协议 |
|---|---|---|
| 80 | 192.168.x.x:80 | TCP |
| 443 | 192.168.x.x:443 | TCP |
| 10000 | 192.168.x.x:10000 | UDP |
| 4443 | 192.168.x.x:4443 | TCP |
| 3478 | 192.168.x.x:3478 | TCP/UDP |
| 5349 | 192.168.x.x:5349 | TCP |
这里有一个容易忽略的点: 10000 端口必须映射 UDP 协议 。很多人做端口映射时只填了 TCP,结果页面能打开但音视频完全不通。
如果运营商分配给你的是动态公网 IP,那么每次重拨 IP 都会变,DDNS 可以解决这个问题。在路由器上配置 DDNS,绑定一个域名,路由器会自动把最新的公网 IP 更新到域名解析记录里。很多民用路由器都内置了 DDNS 客户端,选择对应服务商填入账号和密码就行。如果你用的是云服务商的 DNS 解析,也可以通过脚本定期调用 API 更新 A 记录。
端口映射完成后,从外网访问
https://meet.example.com
应该能打开会议页面。但此时还不能高兴太早,这只是 Web 层通了,音视频是否正常,还需要做一轮深入的连接测试。
5. 从浏览器到媒体流:外网会议连接链路与排查
5.1 我遇到的第一个问题:页面能打开,别人却听不到我的声音
第一次部署完成,我在内网机器上测试非常顺利,但在外网的同事反馈:页面正常打开,能看到我的屏幕共享,但听不到声音。我第一反应是麦克风权限问题,排查了一圈发现不是。后来打开浏览器的 WebRTC 内部工具(Chrome 地址栏输入
chrome://webrtc-internals
),才发现媒体流根本没建立起来。
问题出在 10000/UDP 端口没通。虽然我在路由器上映射了 10000 UDP,但服务器本机的 ufw 防火墙没有放行这个端口。Jitsi 安装脚本默认不会修改系统防火墙策略,如果你的 Ubuntu 服务器开启了 ufw,必须手动放行:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 10000/udp
sudo ufw allow 4443/tcp
sudo ufw allow 3478/tcp
sudo ufw allow 3478/udp
sudo ufw allow 5349/tcp
放行之后,外网同事再测试,声音和画面就正常了。
5.2 排查 ICE 候选地址,判断 TURN 是否生效
如果你想确认 TURN 中继是否真的在工作,可以在浏览器里访问:
chrome://webrtc-internals
进入一个会议后,在 ICE Candidate 相关的日志里,候选地址的类型有三种:
-
host:本机网卡地址,仅在局域网内可用 -
srflx:STUN 反射地址,NAT 打洞成功后的公网映射地址 -
relay:TURN 中继地址,表示媒体流经过 TURN 服务器转发
如果你在外网加入会议时,候选列表里始终没有
relay
类型的地址,说明 TURN 凭证或 coturn 配置有问题。这时候检查以下几点:
-
coturn 是否在运行:
systemctl status coturn - turnserver.conf 的 secret 是否和 Prosody 配置里的 secret 一致
- 防火墙是否放行 3478 和 5349 端口
-
用
turnutils_stunclient手动测试 TURN 服务是否可达
5.3 常见外网访问问题对照表
为了方便快速定位,我把常见的几种症状和排查方向整理成了一个表:
| 症状 | 可能原因 | 排查要点 |
|---|---|---|
| 页面打不开 | 443 未映射 / DNS 没生效 / 证书问题 | 路由器映射、DNS 解析、浏览器地址栏锁标 |
| 页面能开但加入会议失败 | WebSocket 信令不通 | Nginx 配置、Prosody 日志、443 端口是否完整转发 |
| 别人看不到我的画面 | 10000/UDP 未通 | 检查 ufw、路由器 UDP 映射 |
| 外网画面卡得厉害 | TURN 未生效 / 中继带宽不足 | webrtc-internals 检查 relay 候选 |
| 手机 4G/5G 网络下无法加入 | 运营商封禁非标端口 | 改用 443 TCP 作为媒体流备选通道 |
| 加入会议黑屏但能听到声音 | 视频编码器协商异常 / 显卡或编码器资源不足 | JVB 日志、显存/CPU 占用 |
手机上比较容易出现的问题是运营商对大流量 UDP 的限制,Jitsi 默认的 10000/UDP 是随机高位端口,部分运营商对非标准端口的 UDP 优先丢弃。遇到这种情况,可以在 Jitsi 配置里开启
rtcsetportrange
,或者依赖 TURN over TLS(5349)来加密中转,后者通常能够绕过这类限制。
5.4 用日志定位问题
Jitsi 的日志分散在多个地方:
-
Prosody 日志:
/var/log/prosody/prosody.log -
Jicofo 日志:
/var/log/jitsi/jicofo.log -
JVB 日志:
/var/log/jitsi/jvb.log -
Nginx 错误日志:
/var/log/nginx/error.log
排查问题时的基本顺序是:先看 Nginx 是否正常反代 → 再看 Prosody 是否正常处理 XMPP 连接 → 再看 Jicofo 是否成功调度 → 最后看 JVB 是否分配媒体端口。绝大多数问题出在最后一步,也就是媒体流没有建立起来。
6. 让服务长期稳定跑下去:调优、安全和维护
6.1 性能调优的几条实用经验
Jitsi 默认配置比较激进,可能会在你的服务器上消耗大量资源。我使用过程中调整过几个参数,效果很明显。
一是限制会议人数。在
/etc/jitsi/meet/meet.example.com-config.js
中设置:
maxParticipants: 50
超过人数后新用户无法加入,避免服务器资源被耗尽。
二是降低视频码率。在同一个配置文件中:
const config = {
constraints: {
video: {
height: {
ideal: 720,
max: 720
}
}
},
channelLastN: 6
}
channelLastN
的含义是:如果你在一个 20 人的会议室里,只接收画面最大的 6 个人的视频,其他参会者默认只保留音频。这能大幅减少 JVB 的转码压力和带宽消耗。对于小型团队 10 人左右的会议,这个值设置为 4~6 比较合适。
三是调整 JVB 的带宽上限。编辑
/etc/jitsi/videobridge/sip-communicator.properties
:
org.jitsi.videobridge.TCP_HARVESTER_PORT=4443
org.jitsi.videobridge.UDP_HARVESTER_PORT=10000
如果你希望在带宽不足时优先保音频,可以配置 JVB 的拥塞控制参数,但一般情况下默认就够了。
6.2 安全加固回到位
自部署服务暴露到公网后,安全是绕不开的话题。我的做法包括:
-
启用房间锁。开会时主持人点击界面上的"添加密码",参会者需要输入密码才能进入。对于临时会议很实用,不需要改服务器配置。
-
启用 Prosody 内部认证。第 3 节提到的账号体系能有效防止陌生人"蹭会议"。
-
安装 fail2ban,对 Nginx 和 SSH 的暴力破解做自动封禁:
sudo apt install -y fail2ban
默认配置对 SSH 有效,再套一层对 Nginx 的前端防护,基本够用。
-
关闭 Jitsi 的可选崩溃上报。编辑
/etc/jitsi/meet/meet.example.com-config.js:
analytics: {
disabled: true
}
-
Nginx 层面开启安全响应头,例如
X-Frame-Options: DENY能防止页面被恶意网站嵌套。
6.3 备份与升级的小习惯
Jitsi 的配置分散在几个目录,重要的资产是 Prosody 的用户数据库、房间历史记录、以及所有配置文件。我每周会用 cron 打包一次:
tar czf /backup/jitsi-backup-$(date +%Y%m%d).tar.gz \
/etc/jitsi \
/etc/prosody \
/var/lib/prosody
升级时直接:
sudo apt update && sudo apt upgrade -y
Jitsi 的升级一般不会破坏配置,但我总是先备份再执行。曾经有一次升级 JVB 后,10000 端口被防火墙配置重置,导致会议中断,重启防火墙后恢复。从那以后,每次升级完我都要例行检查一遍服务状态和端口监听情况。
我个人实际跑这套系统已经有大半年,期间经历过最多的不是功能问题,而是网络环境差异带来的兼容性问题。如果你也打算自己部署,建议把第 4 节和第 5 节的内容多读两遍,尤其是 TURN 和端口映射这两块。它们不像装包和改配置那样立竿见影,但恰恰是决定外网用户能不能顺利开会的关键。希望这篇记录能帮你少走一些弯路。
更多推荐
所有评论(0)