简介:本资源为开源视频会议系统 Docker-Jitsi-Meet 的完整源代码包,面向 DevOps 工程师、音视频系统部署人员及开源项目二次开发者,解决轻量级、可定制化远程会议平台的本地快速搭建与深度定制需求,适用于在线教育、远程办公及私有化协作场景。压缩包共128个文件,涵盖16个YAML/Compose配置文件(如docker-compose.yml定义服务拓扑)、10个Shell脚本(含gen-passwords.sh密码生成与环境初始化)、8个Dockerfile(各组件镜像构建)、12个Conf/Properties配置模板(媒体、Nginx、Jicofo等模块参数),以及Lua、JSON、MD文档等辅助文件,整体仅386KB,结构紧凑、模块职责清晰。目前已有212人学习下载。读者可直接获取生产就绪的容器化部署体系,包含安全密码生成机制、多环境变量配置范例、时区与APT优化脚本(如01-set-timezone、apt-cleanup),并基于源码理解Jitsi核心组件(Web、JVB、Prosody)协同逻辑,为后续功能扩展、插件开发或私有化加固提供坚实基础。
1. docker-jitsi-meet 的源代码:不是“一键部署包”,而是可调试、可定制、可审计的实时音视频协作系统底座
你点开 GitHub 上jitsi/docker-jitsi-meet仓库,看到docker-compose.yml和一堆.env文件,第一反应可能是:“哦,这是个 Docker 部署脚本集合”。但真正打开docker-jitsi-meet的源代码目录树——web/,prosody/,jicofo/,jvb/下那些带Dockerfile、entrypoint.sh、conf/和resources/的子目录,你才意识到:这根本不是封装好的黑匣子,而是一套完全解耦、分层清晰、配置驱动、支持本地热重载调试的 WebRTC 协作系统源码工程。它把 Jitsi Meet 的前端(React)、信令服务(Prosody XMPP)、协调器(Jicofo)、媒体中继(JVB)全部用 Docker 容器化,但每个组件的构建逻辑、启动流程、配置注入方式都暴露在源码里。这意味着:你想改会议默认布局、加自定义水印、调低 JVB 的 ICE 候选超时、禁用某类 STUN 服务器、甚至替换掉默认的 JWT 鉴权逻辑——全都可以不碰镜像,只改源码、重建对应容器、重新docker-compose up。这不是运维手册,是给一线音视频工程师写的“可调试系统说明书”。适合正在搭建私有化会议平台、需要深度定制 UI/UX、或正被 NAT 穿透失败、音频卡顿、房间状态同步异常等问题卡住的开发者。
2. 源码结构拆解:从docker-compose.yml到每个组件的Dockerfile,看清“谁在启动谁、配置怎么进、日志怎么出”
docker-jitsi-meet的源码不是单体仓库,而是一个以 compose 编排为入口、以组件源码为纵深的分层架构。它的核心价值不在“能跑起来”,而在“每一层都可读、可断点、可替换”。下面带你一层层剥开,重点不是罗列文件名,而是说清每个目录的控制权归属和调试介入点。
2.1docker-compose.yml:编排中枢,也是第一个可修改的“开关面板”
这个文件表面是服务定义,实则是整个系统的拓扑图 + 启动策略 + 依赖契约。注意三个关键设计:
- 所有服务(
web,prosody,jicofo,jvb)都使用build:而非image:,意味着它们默认走本地构建,而非拉取预编译镜像; volumes:映射全部采用相对路径(如./web:/config:Z),且挂载目标是/config—— 这是所有组件统一的配置落盘目录,也是你做配置热更新的主战场;environment:中大量使用${VAR_NAME}占位符,这些变量全部来自.env文件,但**.env不是最终配置源,而是 compose 构建时的参数注入层**;真正的运行时配置由各组件的 entrypoint 脚本从/config目录加载并渲染。
# 示例:jvb 服务片段(来自官方 v8500+ 版本) jvb: image: jitsi/jvb build: context: . dockerfile: resources/jvb/Dockerfile # 注意这里:/config 是唯一配置挂载点,所有 conf 文件都从这读 volumes: - ${CONFIG}/jvb:/config:Z environment: - DOCKER_HOST_ADDRESS=${DOCKER_HOST_ADDRESS} - JVB_STUN_SERVERS=${JVB_STUN_SERVERS}提示:不要直接改
docker-compose.yml里的image:行去换镜像。想试新版 JVB?改build.context指向你 fork 的 jvb 仓库,或在Dockerfile里FROM jitsi/jvb:stable-8500改成你的 tag。image:字段只应在调试完成、需固化版本时才填。
2.2resources/目录:每个组件的“源码根”与“构建上下文”
resources/web/,resources/prosody/,resources/jicofo/,resources/jvb/—— 这四个目录才是真正的“源码心脏”。每个目录下必含三样东西:
Dockerfile:定义如何构建该组件镜像。例如resources/jvb/Dockerfile会COPYjvb.sh、start.sh和conf/到镜像内,最后CMD ["./start.sh"];entrypoint.sh:容器启动时第一个执行的 shell 脚本。它负责:读取/config下的模板文件(如jvb.conf.j2)、用环境变量渲染成真实jvb.conf、校验端口占用、设置 JVM 参数;conf/目录:存放Jinja2 模板文件(.j2后缀)。这是整个配置体系最精妙的设计——你改jvb.conf.j2,entrypoint.sh就自动渲染出jvb.conf;改prosody.cfg.lua.j2,就生成prosody.cfg.lua。所有模板里都用{{ ENV_VAR }}语法,和.env形成映射。
# resources/jvb/entrypoint.sh 关键片段(简化) # 渲染 jvb.conf.j2 → /config/jvb.conf j2 /defaults/conf/jvb.conf.j2 > /config/jvb.conf # 渲染 sip-communicator.properties.j2 → /config/sip-communicator.properties j2 /defaults/conf/sip-communicator.properties.j2 > /config/sip-communicator.properties逻辑说明:
j2是jinja2-cli工具,它把模板和当前环境变量($DOCKER_HOST_ADDRESS,$JVB_PORT等)合并,生成最终配置。这意味着:你不需要手动写jvb.conf,只需改.j2模板 + 调.env变量,就能控制所有底层行为。参数说明:j2命令默认读取所有export出的环境变量,所以.env里设的JVB_PORT=10000,模板里{{ JVB_PORT }}就自动替换成10000。
2.3web/目录:前端 React 应用的完整源码,支持本地热重载调试
很多人以为web/只是静态资源挂载点,其实它是Jitsi Meet Web App 的完整 Git 子模块(submodule)。docker-jitsi-meet仓库的web/目录默认指向https://github.com/jitsi/jitsi-meet的某个 commit。你可以:
cd web && git checkout main切到最新开发分支;npm install && make编译前端;cp -r ./build/* /path/to/docker-jitsi-meet/web/手动覆盖(不推荐);- 更推荐:在
docker-compose.yml中将web服务的volumes改为./web/build:/usr/share/jitsi-meet:Z,然后本地npm start启动 webpack dev server,前端请求自动代理到后端。
# 在 web/ 目录下启用本地热重载(无需 rebuild 容器) cd web npm install # 修改 .env 文件,确保 APP_BASE_URL=http://localhost:8080 npm start # 此时浏览器访问 http://localhost:8080,所有 JS/CSS 实时更新参数说明:
APP_BASE_URL控制前端 API 请求的 base URL;DISABLE_AUDIO_LEVELS=1可关闭麦克风波形动画(降低 CPU);ENABLE_P2P=0强制走 JVB 中继(方便抓包分析)。这些变量既可在.env设,也可在web/interface_config.js里硬编码,后者优先级更高。
3. 本地构建与调试:绕过docker-compose up,用docker build+docker run精准定位组件问题
当你遇到“会议创建失败”、“用户进房黑屏”、“音频单通”这类问题,docker-compose up -d日志太泛,无法定位是jicofo选不到会议、jvb端口没暴露、还是prosodyXMPP 认证失败。此时必须脱离 compose 编排,对单个组件做隔离构建与调试。这是源码级调试的核心能力。
3.1 构建单个组件镜像:以jvb为例,看清构建全过程
不要依赖docker-compose build jvb,它隐藏了中间步骤。手动执行,才能看到哪一步失败:
# 进入 jvb 构建上下文 cd resources/jvb # 1. 查看基础镜像是否可拉取(常因网络导致构建卡住) docker pull openjdk:17-jre-slim # 2. 手动执行构建(关键:加 --progress=plain 看详细日志) docker build \ --progress=plain \ --build-arg JVB_VERSION=stable-8500 \ -t my-jvb:debug \ . # 3. 构建成功后,检查镜像内容(确认 conf/ 和 scripts 是否存在) docker run --rm -it my-jvb:debug ls -l /defaults/conf/逻辑说明:
--build-arg JVB_VERSION是构建时参数,用于指定下载哪个版本的 JVB 二进制包;-t my-jvb:debug打标签便于后续调试;--progress=plain输出原始日志,比默认的auto模式更能发现网络超时、curl 失败等细节。常见失败点:curl -L https://github.com/jitsi/jvb/releases/download...下载失败(需配代理或换国内镜像源);apt-get update超时(需在Dockerfile中加RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list)。
3.2 启动单个容器并挂载调试卷:让jvb容器输出完整 DEBUG 日志
docker-compose默认只输出INFO级别日志。要查 ICE 候选交换、DTLS 握手、RTP 流状态,必须进容器内部启DEBUG:
# 启动 jvb 容器,挂载本地 config 并强制 DEBUG docker run -it \ --rm \ --name debug-jvb \ -p 10000:10000/udp \ -p 10000:10000/tcp \ -v $(pwd)/../configs/jvb:/config:Z \ -e JVB_LOG_LEVEL=DEBUG \ -e JVB_ENABLE_APIS=rest,colibri \ my-jvb:debug参数说明:
-p 10000:10000/udp必须显式声明 UDP 端口(JVB 主要用 UDP 传 RTP);JVB_LOG_LEVEL=DEBUG是 JVB 内置环境变量,开启全量日志;JVB_ENABLE_APIS=rest,colibri启用 REST API(可 curlhttp://localhost:8080/colibri/stats查实时流统计);/config挂载确保你改的jvb.conf.j2已渲染生效。此时终端会刷出类似org.jitsi.videobridge.xmpp.XmppConnection: Connected to XMPP server的连接日志,若无此行,说明jicofo没连上prosody。
3.3 进容器内部调试:用jps、jstack、tcpdump直查 Java 进程与网络包
JVB 是 Java 进程,jicofo也是。当容器启动后卡住或 CPU 飙高,不能只看日志:
# 1. 进入正在运行的 jvb 容器 docker exec -it debug-jvb /bin/bash # 2. 查 Java 进程 PID(JVB 启动后只有一个 java 进程) jps -l # 3. 查线程堆栈(判断是否死锁) jstack <PID> # 4. 抓 UDP 包(验证 STUN/TURN 是否正常) tcpdump -i any -n udp port 10000 -w /tmp/jvb.pcap # 5. 查端口监听(确认 JVB 是否真在 listen) netstat -tulnp | grep :10000血泪经验:
jstack输出里若出现java.lang.Thread.State: BLOCKED (on object monitor)且多个线程 block 在同一对象,大概率是jicofo和jvb之间的心跳超时导致的锁竞争;tcpdump抓不到任何STUN Binding Request,说明客户端根本没发请求——问题在前端lib-jitsi-meet或浏览器 WebRTC 配置,而非 JVB。
4. 配置定制实战:从改会议标题、禁用录制,到自定义 JWT 鉴权逻辑
源码的价值,在于让你把“配置项”变成“可编程逻辑”。下面三个例子,覆盖 UI、功能、安全三类高频定制需求,全部基于修改源码中的.j2模板或entrypoint.sh,不依赖外部插件。
4.1 改会议标题与 Logo:修改web/的interface_config.js模板
web/目录下的interface_config.js是前端全局配置。但注意:docker-jitsi-meet的web/是 submodule,直接改会丢失 commit。正确做法是——在resources/web/下新建interface_config.js.j2模板,让entrypoint.sh渲染它:
# 创建模板(resources/web/interface_config.js.j2) { // ... 其他配置保持不变 DEFAULT_REMOTE_DISPLAY_NAME: '{{ DEFAULT_REMOTE_DISPLAY_NAME|default("参会者") }}', APP_NAME: '{{ APP_NAME|default("我的会议系统") }}', BRAND_WATERMARK_LINK: '{{ BRAND_WATERMARK_LINK|default("https://example.com") }}', SHOW_BRAND_WATERMARK: {{ SHOW_BRAND_WATERMARK|default(true) }}, // 新增:动态控制是否显示右上角“设置”按钮 SHOW_SETTINGS_BUTTON: {{ SHOW_SETTINGS_BUTTON|default(false) }} }然后改resources/web/entrypoint.sh,在渲染interface_config.js前加一行:
# resources/web/entrypoint.sh 中 j2 /defaults/interface_config.js.j2 > /config/interface_config.js逻辑说明:
j2渲染时,会读取.env中的APP_NAME="客户会议平台",然后生成interface_config.js里"APP_NAME": "客户会议平台"。SHOW_SETTINGS_BUTTON=false可彻底隐藏设置按钮,防止用户误调分辨率/降噪参数。参数说明:所有|default(...)是 Jinja2 的默认值语法,避免.env没设该变量时报错。
4.2 禁用录制功能:从prosody配置到jicofo启动参数全链路关闭
录制依赖jibri组件,但即使不部署jibri,前端仍显示“录制”按钮。要彻底禁用,需三处修改:
prosody配置:禁止conference.example.com域的recording模块加载
修改resources/prosody/conf/prosody.cfg.lua.j2,在Component "conference.example.com"块内删掉modules_enabled = { "recording" }行;jicofo启动参数:关掉自动发现jibri
修改resources/jicofo/entrypoint.sh,在exec java ...命令前加:# 禁用 jibri 自动注册 export JICOFO_DISABLE_AUTO_JIBRI=true前端隐藏按钮:改
web/interface_config.js.j2TOOLBAR_BUTTONS: [ // ... 其他按钮 // 删除 'recording' 字符串 ]
提示:改完
prosody.cfg.lua.j2后,docker-compose restart prosody即可生效;jicofo需docker-compose build jicofo && docker-compose up -d jicofo;前端改interface_config.js.j2需重建web镜像或重启web容器。
4.3 自定义 JWT 鉴权:替换prosody的auth_token插件逻辑
官方 JWT 验证只支持 HS256 算法和固定audience。若你用 RS256 + 多 audience,需改prosody插件。docker-jitsi-meet的resources/prosody/目录下有plugins/子目录,这就是你的扩展点:
# 在 resources/prosody/plugins/ 下新建 custom_auth.lua -- resources/prosody/plugins/custom_auth.lua local jwt = require "luajwt" local jwks = require "jwks" function authenticate(username, password) local token = password -- JWT token 传在密码字段 local jwks_url = "https://your-auth-server/.well-known/jwks.json" local key = jwks.fetch_key(jwks_url, token) local payload = jwt:verify(token, key, { algorithms = {"RS256"}, audience = {"jitsi", "meeting-app"} }) if payload and payload.sub then return payload.sub -- 返回用户 ID 作为 username end return nil end然后在prosody.cfg.lua.j2的VirtualHost "auth.meet.jitsi"块内,把authentication = "token"改成:
authentication = "custom_auth"注意:
luajwt和jwks需提前装进prosody镜像。在resources/prosody/Dockerfile的RUN apt-get update && ...后加:RUN luarocks install luajwt jwks
这样prosody启动时就能加载你的custom_auth.lua。翻车点:luarocks install可能因网络失败,建议先docker run -it prosody:latest luarocks install luajwt测试。
5. 避坑指南:5 个血泪换来的“为什么跑不起来”真相
源码级部署最大的陷阱,不是不会写代码,而是对 Docker 网络、Java 进程、XMPP 协议、WebRTC 信令的隐式依赖一无所知。下面 5 条,每一条都来自真实翻车现场,按“现象→原因→解决”给出可立即执行的方案。
5.1 现象:docker-compose up后jvb容器秒退,日志只有Killed
原因:JVB 是内存大户,默认 JVM 参数-Xmx3072m,宿主机物理内存不足 4GB,Linux OOM Killer 直接 kill 进程。
解决:
- 查宿主机内存:
free -h,确保available> 4G; - 降 JVM 内存:在
.env中加JVB_OPTS="-Xmx1024m -Xms512m"; - 或在
resources/jvb/entrypoint.sh的exec java命令前加:export JAVA_OPTS="${JAVA_OPTS} -Xmx1024m -Xms512m"
5.2 现象:用户能进房,但互相看不到视频,Chrome 控制台报Failed to set remote offer sdp: Called in wrong state: stable
原因:web/前端版本与jicofo/jvb后端版本不兼容。例如web用main分支(v2024.3),而后端用stable-8500(v2023.10),SDP 语义已变。
解决:
- 统一版本:进
web/目录,git checkout stable-8500; - 或升级后端:改
docker-compose.yml中jicofo的build.context指向https://github.com/jitsi/jicofo.git#stable-8500; - 验证:
docker-compose exec web cat /usr/share/jitsi-meet/package.json | grep version和docker-compose exec jicofo java -cp /usr/share/jicofo/jicofo.jar org.jitsi.jicofo.Version输出应一致。
5.3 现象:prosody日志反复刷Authentication failed for ...,但 JWT token 经jwt.io验证无误
原因:prosody的auth_token插件默认要求 token 的iss(issuer)字段必须是https://your-domain.com,而你的 token 是https://auth.example.com,插件硬校验失败。
解决:
- 在
resources/prosody/conf/prosody.cfg.lua.j2的VirtualHost "auth.meet.jitsi"块内,加:authentication = "token" token_secret = "your-secret-key" token_issuer = "https://auth.example.com" -- 允许的 issuer 列表 token_audience = "jitsi" - 注意:
token_issuer是字符串,不是数组;若需多 issuer,得改插件源码。
5.4 现象:docker-compose logs jvb显示org.jitsi.videobridge.xmpp.XmppConnection: Failed to connect to XMPP server
原因:jvb容器无法解析prosody服务名。docker-compose默认用 bridge 网络,服务名解析靠 Docker DNS,但jvb的Dockerfile基础镜像是openjdk:17-slim,它没有安装dnsutils,nslookup prosody会失败,导致jvb启动时 DNS 解析超时。
解决:
- 在
resources/jvb/Dockerfile的RUN apt-get update && ...行后加:RUN apt-get install -y dnsutils && rm -rf /var/lib/apt/lists/* - 或更优:在
resources/jvb/entrypoint.sh开头加 DNS 重试逻辑:until nslookup prosody &>/dev/null; do echo "Waiting for prosody DNS..." sleep 2 done
5.5 现象:会议中用户 A 能听到 B,但 B 听不到 A;Wireshark 抓包显示 A 发 RTP 包,B 收不到
原因:jvb的DOCKER_HOST_ADDRESS配置错误。该变量告诉 JVB:“对外宣称我的公网 IP 是多少”。若设成127.0.0.1或内网 IP(如192.168.1.100),客户端收到的 ICE candidate 就是错的,NAT 穿透失败。
解决:
- 查宿主机真实公网 IP:
curl ifconfig.me; - 在
.env中设:DOCKER_HOST_ADDRESS=203.0.113.45(你的公网 IP); - 若用域名,确保该域名 A 记录指向公网 IP,且
DOCKER_HOST_ADDRESS=meet.example.com; - 验证:
docker-compose exec jvb env | grep DOCKER_HOST_ADDRESS,再docker-compose exec jvb cat /config/jvb.conf | grep PUBLIC_ADDRESS,两者必须一致。
6. 进阶技巧:用docker-compose.override.yml实现多环境配置与灰度发布
源码级部署的终极价值,不是“一次跑通”,而是“一套代码,多套环境”。docker-jitsi-meet原生支持docker-compose.override.yml,这是实现开发/测试/生产环境分离、A/B 测试、灰度发布的最轻量方案——不用改任何源码,只靠 Compose 的配置叠加机制。
6.1 用override.yml隔离开发与生产配置
.env文件是全局的,但不同环境需要不同参数。比如开发环境用localhost,生产环境用域名;开发环境开 DEBUG 日志,生产环境关。这时,.env就不够用了。override.yml是答案:
# docker-compose.override.yml(开发环境) version: '3' services: web: environment: - ENABLE_HTTP_REDIRECT=0 - DISABLE_HTTPS=1 ports: - "8080:80" jvb: environment: - JVB_LOG_LEVEL=DEBUG - JVB_STUN_SERVERS=stun.l.google.com:19302 # 开发环境允许 UDP 端口映射(生产环境通常只开 443/3478) ports: - "10000:10000/udp"# docker-compose.prod.override.yml(生产环境) version: '3' services: web: environment: - ENABLE_HTTP_REDIRECT=1 - DISABLE_HTTPS=0 # 生产环境不暴露 8080,只走 Nginx 反代 ports: [] jvb: environment: - JVB_LOG_LEVEL=INFO - JVB_STUN_SERVERS=stun.example.com:3478 # 生产环境禁用端口映射,只靠 host network 或 firewall 开放 network_mode: "host"启动命令:
# 开发环境 docker-compose up -d # 生产环境(叠加 prod override) docker-compose -f docker-compose.yml -f docker-compose.prod.override.yml up -d逻辑说明:
docker-compose默认加载docker-compose.yml+docker-compose.override.yml;用-f可指定任意组合。override.yml中的配置会深度合并(不是覆盖):environment是追加,ports是合并列表,volumes是合并列表。所以web的ports在开发版是["8080:80"],在生产版是[],最终生效的是[]。
6.2 用override.yml实现灰度发布:让 10% 用户用新jvb
你想上线新版jvb(比如修复了某个音频抖动 bug),但不敢全量。docker-compose支持 service-level scale 和 environment 变量覆盖,可做流量切分:
# docker-compose.gray.override.yml version: '3' services: jvb-new: extends: file: docker-compose.yml service: jvb image: my-jvb:202405-patch environment: - JVB_LOG_LEVEL=DEBUG - JVB_VERSION=202405-patch # 新版 jvb 用不同端口,避免冲突 ports: - "10001:10001/udp" # 通过环境变量告诉 jicofo:这个 jvb 是“新”的 labels: - "traefik.enable=false" jicofo: environment: - JICOFO_JVB_MAPPINGS={"jvb-new":"10001","jvb":"10000"}然后改jicofo的entrypoint.sh,让它读JICOFO_JVB_MAPPINGS并动态注册多个 JVB。虽然官方不直接支持,但jicofo源码里JvbHarvester类可扩展——这才是源码级部署的真正自由:你不是在用产品,而是在改造产品。
我一般会在resources/jicofo/下新建jvb_mappings.patch,用git apply打补丁,再docker-compose build jicofo。每次 patch 都 commit 到自己仓库,回滚就是git checkout HEAD~1。这种掌控感,是任何 SaaS 会议平台给不了的。希望帮到你。
本文还有配套的精品资源,点击获取