OvenMediaEngine 0.21.0 Docker Compose 部署指南
OvenMediaEngine 0.21.0 Docker Compose 部署指南:端口规划映射、Server.xml 三处关键修改、RTMP 推流与 LLHLS/HLS/WebRTC 分发全流程实测
OvenMediaEngine 0.21.0 Docker Compose 部署指南
目标:在一台 Linux 服务器上用 Docker Compose 部署 OvenMediaEngine(OME)v0.21.0,完成 RTMP 推流接入与 LLHLS / HLS / WebRTC 三路分发验证,并打通 REST API 管理面。本文所有命令与回显均在下述实测环境中逐条执行过;端口规划采用「官方默认端口 → 宿主高位端口」的映射方案(原因与对应关系见第三节)。
一、环境信息
| 项 | 值 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 24.04(x86_64,内核 7.0.0-38-generic) | 服务器为双栈(IPv4/IPv6) |
| Docker | 29.9.2 | docker --version 实测 |
| Docker Compose | v5.6.1 | docker compose version 实测 |
| OME 版本 | v0.21.0(官方最新稳定版,2026-08-13 发布) | docker image inspect 显示镜像构建于 2026-08-13,与 Release 同日 |
| 镜像 | airensoft/ovenmediaengine:v0.21.0 | 约 512 MB,基于 Ubuntu 22.04,内嵌 FFmpeg 7.1.5、libaom v3.12.0、SRT 1.5.2、libSRTP 2.4.2 |
| 部署目录 | /opt/ovenmediaengine-compose/ | 存放 docker-compose.yml 与 Server.xml |
| 网络 | 专用桥接网络 ovenmediaengine-0.21.0-net(子网 10.89.64.0/24) | 与宿主上其他服务隔离 |
| 验证时间 | 2026-10-29 | 全部命令实测当日 |
💡 镜像命名空间说明:OME 官方镜像同时发布在
airensoft/ovenmediaengine与ovenmedialabs/ovenmediaengine两个命名空间,v0.21 起官方文档站推荐后者;实测两者 v0.21.0 为同一构建。tag 规律为v前缀(v0.21.0),不存在不带前缀的0.21.0tag,写错会报 manifest unknown。
二、前置条件
- 已安装 Docker 与 Docker Compose v2+(
docker compose version有输出即可); - 服务器可达公网(首次启动 OME 会通过 STUN 服务器
stun.ovenmediaengine.com:13478解析公网 IP,用于 WebRTC 候选地址;不通则需显式设置OME_HOST_IP); - 已规划端口并确认空闲(本机复核命令:
ss -ltn与ss -lun)。
三、端口规划与官方默认值的映射关系
OME 各协议的官方默认端口如下(取自默认配置 Server.xml 中的 ${env:OME_*} 缺省值):
| 协议/用途 | 官方默认 | 环境变量 | 传输层 |
|---|---|---|---|
| RTMP 推流 | 1935 | OME_RTMP_PROV_PORT | TCP |
| SRT 推流 | 9999 | OME_SRT_PROV_PORT | UDP(SRT) |
| WebRTC 信令 + HLS/LLHLS | 3333 | OME_WEBRTC_SIGNALLING_PORT / OME_LLHLS_STREAM_PORT | TCP(LLHLS 兼容 HTTP/1.1) |
| WebRTC 信令/LLHLS TLS | 3334 | OME_WEBRTC_SIGNALLING_TLS_PORT / OME_LLHLS_STREAM_TLS_PORT | TCP(需证书) |
| REST API | 8081 | OME_API_PORT | TCP |
| OVT(Origin-Edge) | 9000 | OME_ORIGIN_PORT | TCP |
| WebRTC ICE(默认候选段) | 10000-10003/udp | OME_WEBRTC_CANDIDATE_PORT | UDP |
| WebRTC ICE TCP 回退 | 10000/tcp | OME_WEBRTC_TCP_ICE_PORT | TCP(RFC 6544) |
| WebRTC TURN(内嵌中继) | 3478 | OME_WEBRTC_TCP_RELAY_PORT | TCP |
| MPEG-2 TS 推流 | 4000-4005/udp | OME_MPEGTS_PROV_PORT | UDP |
本服务器 1935/9999/3333/8081 等标准端口已被其他业务占用,因此全部映射到宿主空闲高位段(UDP 端口同样映射),映射关系如下——这是本服务器端口规划与官方默认值的对应关系,非官方默认配置:
| OME 功能 | 官方默认 | 本服务器宿主端口 | 映射到容器内 |
|---|---|---|---|
| RTMP 推流 | 1935/TCP | 8160 | 1935 |
| SRT 推流 | 9999/UDP | 8161 | 9999 |
| WebRTC 信令 + HLS/LLHLS | 3333/TCP | 8162 | 3333 |
| REST API | 8081/TCP | 8163 | 8081 |
| WebRTC ICE(UDP) | 10000/UDP | 8164 | 10000 |
| WebRTC ICE(TCP 回退) | 10000/TCP | 8165 | 10000 |
💡 说明:为把 ICE 端口段收敛为单端口,实测通过环境变量
OME_WEBRTC_CANDIDATE_PORT=10000/udp把默认的 10000-10003 四端口候选段改为单端口 10000;TURN 3478 仅在内网穿透受限时才需要映射,本方案未暴露。若宿主 8160-8165 不可用,请顺延选择空闲端口并同步修改 compose 文件,其余不变。
四、部署步骤
1. 建目录与专用网络
mkdir -p /opt/ovenmediaengine-compose
docker network create --subnet 10.89.64.0/24 ovenmediaengine-0.21.0-net
⚠️
--subnet建议显式指定并先确认与宿主上既有 docker 网络不冲突(docker network inspect逐个核对);不指定时 Docker 会从默认地址池自动分配,可能与并行部署的服务撞池。
2. 提取默认配置文件
官方镜像内自带默认配置,直接用一次性命令提取到宿主(实测输出为 Server.xml version="8" 结构):
cd /opt/ovenmediaengine-compose
docker run --rm airensoft/ovenmediaengine:v0.21.0 \
cat /opt/ovenmediaengine/bin/origin_conf/Server.xml > Server.xml
默认配置即可接收 RTMP 推流并输出 LLHLS(3333 端口同端口复用信令与 HLS),本方案在其基础上做三处修改(见下节)。
3. 修改 Server.xml(三处)
第一处:启用 REST API。 v0.21 的 API 配置分居两处——监听端口在 Bind/Managers/API(默认被注释,含 <Port> 与 <WorkerCount>),访问令牌在 Server/Managers/API(默认整段缺失,需手工新增 <AccessToken>)。两处都配好 API 才能启动:
<!-- ① 在 <Bind> 下(与 <Providers> 平级)取消注释/新增: -->
<Bind>
<Managers>
<API>
<Port>${env:OME_API_PORT:8081}</Port>
<WorkerCount>1</WorkerCount>
</API>
</Managers>
<Providers>
...
</Providers>
</Bind>
<!-- ② 在 <Server> 下(与 <Bind> 平级)新增: -->
<Managers>
<API>
<AccessToken>q1w2e3r4</AccessToken>
</API>
</Managers>
⚠️ 两处放错位置会直接启动失败:
<AccessToken>写进Bind/Managers/API会报Unknown item found: Server.bind.managers.api.AccessToken;<AccessToken>缺失则 API 模块拒绝启动并退出进程(Empty <AccessToken> is not allowed)。排障实录见运维篇。
第二处(可选):启用 HLSv3(传统 TS 打包)。 默认配置的 Bind/Publishers 中没有 <HLS> 绑定,对应 Publisher 处于禁用状态(启动日志明示 HLSv3 Publisher is disabled by configuration)。需要向不支持 fMP4 的传统设备分发时,在 Bind/Publishers 下新增:
<Publishers>
...
<HLS>
<Port>3333</Port>
<WorkerCount>1</WorkerCount>
</HLS>
</Publishers>
第三处(可选):自定义 ABR 档位。 默认 OutputProfiles 使用 RenditionTemplate 自动匹配档位;要显式控制档位组合,改写 Applications/Application/OutputProfiles/OutputProfile/Playlist(v0.21 中 <Rendition> 是单数元素直接挂在 <Playlist> 下,可重复出现,没有 <Renditions> 包装层——写成包装层会报 Unknown item 拒绝启动):
<Playlist>
<Name>default</Name>
<FileName>master</FileName>
<Options>
<WebRtcAutoAbr>true</WebRtcAutoAbr>
<HLSChunklistPathDepth>0</HLSChunklistPathDepth>
<EnableTsPackaging>true</EnableTsPackaging>
</Options>
<Rendition>
<Name>bypass</Name>
<Video>bypass_video</Video>
<Audio>aac_audio</Audio>
</Rendition>
<Rendition>
<Name>720p</Name>
<Video>video_720</Video>
<Audio>aac_audio</Audio>
</Rendition>
</Playlist>
同时在 Encodes 中保留(或补上)与 Rendition 引用名一致的编码器定义,例如 video_720:
<Video>
<Name>video_720</Name>
<Codec>h264</Codec>
<Bitrate>2024000</Bitrate>
<Framerate>30</Framerate>
<Width>1280</Width>
<Height>720</Height>
<Preset>faster</Preset>
</Video>
💡
<FileName>master</FileName>决定该播放列表的访问文件名(master.m3u8);默认还内置一个自动生成的llhls.m3u8(仅含单档位),两者并存,自定义 ABR 验证时注意区分(详见运维篇)。配置项逐段精讲见《OvenMediaEngine 0.21.0 配置参数详解》。
4. 编写 docker-compose.yml
name: ovenmediaengine-0.21.0-stack
services:
ovenmediaengine:
image: airensoft/ovenmediaengine:v0.21.0
container_name: ovenmediaengine-0.21.0-app
hostname: ovenmediaengine-0.21.0-app
restart: "no"
environment:
OME_HOST_IP: 192.168.146.128 # 改为客户端可达的本机 IP(公网或 LAN)
OME_WEBRTC_CANDIDATE_PORT: "10000/udp" # 候选端口段收敛为单端口
networks:
- ome
ports:
- "8160:1935/tcp" # RTMP Provider
- "8161:9999/udp" # SRT Provider
- "8162:3333/tcp" # WebRTC Signaling + HLS/LLHLS Publisher
- "8163:8081/tcp" # REST API Server
- "8164:10000/udp" # WebRTC ICE (UDP)
- "8165:10000/tcp" # WebRTC ICE (TCP, RFC 6544)
volumes:
- ./Server.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml:ro
networks:
ome:
name: ovenmediaengine-0.21.0-net
external: true
要点:
OME_HOST_IP必须是客户端可达的 IP;WebRTC offer 中下发的 ICE 候选地址取自它(实测设为内网地址时,日志打印ICE candidate found: 192.168.146.128:10000);- UDP 端口必须按
/udp显式映射,漏写会导致 WebRTC 播放黑屏; - 配置文件以只读方式挂载覆盖容器内默认配置,改配置只需改宿主文件后重启容器。
5. 启动与启动日志验证
cd /opt/ovenmediaengine-compose
docker compose up -d
docker logs -f ovenmediaengine-0.21.0-app
启动成功时日志横幅与关键行(实测原文节选,时间戳与内存地址已省略):
▄██████▀███▄
█████▀ ▄██████ OvenMediaEngine Launcher v0.1
███▄▄▄▄▀▀▀▀███
██████▀ ▄█████ https://github.com/OvenMediaLabs/OvenMediaEngine
▀███▄██████▀
...
OvenMediaEngine | banner.cpp:23 | OvenMediaEngine v0.21.0 (v0.21.0) is started on [ovenmediaengine-0.21.0-app] (Linux x86_64 - 7.0.0-38-generic, ...)
OvenMediaEngine | banner.cpp:28 | FFmpeg 7.1.5
OvenMediaEngine | banner.cpp:36 | libaom: v3.12.0
OvenMediaEngine | banner.cpp:37 | SRT: 1.5.2
OvenMediaEngine | main.cpp:99 | Resolved public IP address (182.54.113.x) from stun server (stun.ovenmediaengine.com:13478)
ICE | ice_port_manager.cpp:342 | ICE candidate found: 192.168.146.128:10000
ICE | ice_port.cpp:123 | ICE ports are listening on *:10000/TCP (...), *:10000/UDP (...) (for WebRTC Publisher)
ICE | ice_port_manager.cpp:227 | TURN port is listening on *:3478/TCP (for WebRTC Publisher)
OVT | ovt_publisher.cpp:90 | OVTPublisher is listening on *:9000/TCP...
SrtProvider | srt_provider.cpp:125 | SrtProvider is listening on *:9999/SRT
RTMPProvider | rtmp_provider.cpp:169 | RTMPProvider is listening on *:1935
默认被禁用的组件会在日志文件(容器内 /var/log/ovenmediaengine/ovenmediaengine.log)中以 W 级别明示,例如 HLSv3 Publisher is disabled by configuration、SRTPublisher is disabled by configuration、ThumbnailPublisher is disabled by configuration——按第三节的修改补上绑定后,对应组件即随启动注册(docker logs 中出现 HLS Publisher ... HLS Stream ... All playlists are ready to play 等运行期行)。
6. 推流与分发验证
推流(本方案在专用网络内以 ffmpeg 长驻推流;宿主机装有 ffmpeg 时可直接对宿主端口推):
docker run -d --name ovenmediaengine-0.21.0-publisher \
--network ovenmediaengine-0.21.0-net --restart unless-stopped \
jrottenberg/ffmpeg:5-alpine \
-re -f lavfi -i "testsrc2=size=1280x720:rate=30" \
-f lavfi -i "sine=frequency=1000:sample_rate=48000" \
-c:v libx264 -preset veryfast -tune zerolatency -profile:v main -pix_fmt yuv420p \
-b:v 2000k -g 60 -c:a aac -b:a 128k -ac 2 -ar 48000 \
-f flv rtmp://ovenmediaengine-0.21.0-app:1935/app/stream
推流建立后 OME 日志出现(实测原文):
RTMPProvider | rtmp_provider.cpp:228 | A RTMP client has connected from <ClientSocket: ..., Connected, TCP, Nonblocking, <推流端地址>>
MediaRouter | mediarouter_application.cpp:467 | [#default#app/stream(100)] Stream has been created
WebRTC Publisher | rtc_stream.cpp:283 | WebRTC Stream has been created : stream/775775263
分发验证(逐项实测回显):
# ① LLHLS(自动默认列表,单档位)
curl -s http://127.0.0.1:8162/app/stream/llhls.m3u8 | head -6
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac_audio",NAME="Audio_1",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="/app/stream/chunklist_2_audio_7930408358254892257_llhls.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2158032,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.4d401f,mp4a.40.2",AUDIO="aac_audio"
/app/stream/chunklist_0_video_7930408358254892257_llhls.m3u8
# ② ABR 双档列表(自定义 Playlist,文件名 master)
curl -s http://127.0.0.1:8162/app/stream/master.m3u8
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac_audio",NAME="Audio_1",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="chunklist_2_audio_7930408358254892257_llhls.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2158032,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.4d401f,mp4a.40.2",AUDIO="aac_audio"
chunklist_0_video_7930408358254892257_llhls.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2149000,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.42c01f,mp4a.40.2",AUDIO="aac_audio"
chunklist_1_video_7930408358254892257_llhls.m3u8
(avc1.4d401f 档为 video_720 重编码输出,avc1.42c01f 档为 bypass 直通原流,ABR 双档生效。)
# ③ HLSv3(TS 打包,URL 以 ts: 前缀区分;仅 <HLS> 绑定启用后可用)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8162/app/stream/ts:playlist.m3u8 # 200
# ④ REST API(认证头为 Basic base64(AccessToken),token 原样编码、不带冒号)
curl -s -H "authorization: Basic $(printf 'q1w2e3r4' | base64)" http://127.0.0.1:8163/v1/vhosts
{"message":"OK","response":["default"],"statusCode":200}
# ⑤ WebRTC 信令(WebSocket,应答 offer 与 ICE 候选)
python3 - <<'EOF'
import websocket, json
ws = websocket.create_connection("ws://127.0.0.1:8162/app/stream", timeout=10)
ws.send(json.dumps({"command": "request_offer", "direction": "sendrecv", "id": "test-viewer-1"}))
msg = json.loads(ws.recv())
print(msg.get("command"), msg.get("code"), "| candidates:", len(msg.get("candidates", [])))
ws.close()
EOF
offer 200 | candidates: 1
浏览器端播放可用官方演示播放器 http://demo.ovenplayer.com(WebRTC 源 ws://<服务器IP>:8162/app/stream,LLHLS 源 http://<服务器IP>:8162/app/stream/llhls.m3u8),或部署 OvenPlayer 自建播放页。
五、数据持久化说明
OME 默认为无状态内存打包:流分片在内存中生成后直接经 HTTP 分发,不落盘;进程重启即丢流(推流端自动重连后重建)。需要持久化时按需挂载:
| 数据 | 容器内路径 | 挂载建议 |
|---|---|---|
| 主配置 | /opt/ovenmediaengine/bin/origin_conf/Server.xml | 宿主文件只读挂载(本方案采用) |
| 运行日志 | /var/log/ovenmediaengine/ovenmediaengine.log | 需留存时挂载为卷 |
| 录制输出(File Publisher) | /opt/ovenmediaengine/media/ | 启用录制后挂载持久卷 |
六、环境变量速查
镜像通过 ${env:VAR:default} 语法在默认配置中引用以下环境变量(实测提取自 v0.21.0 镜像内置 Server.xml):
| 环境变量 | 默认值 | 用途 |
|---|---|---|
OME_HOST_IP | 192.168.0.160 | WebRTC 对外候选 IP(必须改为客户端可达地址) |
OME_RTMP_PROV_PORT | 1935 | RTMP 推流端口 |
OME_SRT_PROV_PORT | 9999 | SRT 推流端口 |
OME_MPEGTS_PROV_PORT | 4000-4005/udp | MPEG-2 TS 推流端口 |
OME_WEBRTC_SIGNALLING_PORT | 3333 | WebRTC 信令端口 |
OME_WEBRTC_SIGNALLING_TLS_PORT | 3334 | WebRTC 信令 TLS 端口 |
OME_WEBRTC_CANDIDATE_PORT | 10000-10003/udp | ICE 候选端口段 |
OME_WEBRTC_TCP_ICE_PORT | 10000/tcp | ICE TCP 回退端口 |
OME_WEBRTC_TCP_RELAY_PORT | 3478 | 内嵌 TURN 端口 |
OME_LLHLS_STREAM_PORT | 3333 | LLHLS 分发端口 |
OME_LLHLS_STREAM_TLS_PORT | 3334 | LLHLS TLS 端口 |
OME_API_PORT | 8081 | REST API 端口 |
OME_ORIGIN_PORT | 9000 | OVT(Origin-Edge)端口 |
💡 这些变量只覆盖端口/IP 类值;结构性配置(Publisher 开关、转码档位、AccessToken 等)必须改
Server.xml。修改配置后执行docker compose restart生效——OME 无热加载,实测改完立即生效无需重建容器。
七、等价 docker run 命令
不想用 compose 时,以下单命令与上述编排等价(长命令建议拆行或转 systemd unit):
docker run -d --name ovenmediaengine-0.21.0-app --restart unless-stopped \
--network ovenmediaengine-0.21.0-net \
-e OME_HOST_IP=192.168.146.128 \
-e OME_WEBRTC_CANDIDATE_PORT=10000/udp \
-p 8160:1935/tcp -p 8161:9999/udp -p 8162:3333/tcp \
-p 8163:8081/tcp -p 8164:10000/udp -p 8165:10000/tcp \
-v /opt/ovenmediaengine-compose/Server.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml:ro \
airensoft/ovenmediaengine:v0.21.0
八、常用运维命令
docker compose ps # 栈内容器状态
docker logs -f ovenmediaengine-0.21.0-app # 跟踪 stdout 日志
docker exec ovenmediaengine-0.21.0-app \
tail -f /var/log/ovenmediaengine/ovenmediaengine.log # 跟踪引擎日志文件(含 W/E 级别详情)
docker compose restart # 改配置后重启生效
docker compose down # 停止并移除(外部网络保留)
ss -ltn | grep -E ':(8160|8162|8163|8165)\b' # 核对 TCP 端口监听
ss -lun | grep -E ':(8161|8164)\b' # 核对 UDP 端口监听
日志分级、API 运维面与真实排障案例(Unknown item / Empty AccessToken / HLSv3 404 / SRT streamid 解析失败等 8 例)见《OvenMediaEngine 0.21.0 常用运维命令与排障详解》。
九、常见问题
- 拉不到
v0.21.0(或0.21.0)tag:官方 tag 带v前缀,用airensoft/ovenmediaengine:v0.21.0;国内环境可先docker pull官方镜像,失败时为 Docker 守护进程配置可用的镜像加速或代理后重试。 - API 端口不通 / 进程反复退出:检查
AccessToken是否配置在Server/Managers/API下(不在Bind内)、<Port>是否配置在Bind/Managers/API下;两处错位的报错原文见运维篇 6.1/6.2。 /app/stream/playlist.m3u8返回 404:HLSv3 默认禁用,按第四节第二处新增<HLS>绑定后重启;LLHLS 的默认列表是/app/stream/llhls.m3u8,不要混淆。- WebRTC 播放黑屏:按序检查
OME_HOST_IP是否为客户端可达 IP、8164/udp是否映射、客户端到该 UDP 端口是否连通;TCP 回退(8165)仅在播放器显式启用 ICE-TCP 时生效。 - 推流容器启动报
Name does not resolve:OME 与推流器同网络启动存在时序差,推流器配置自动重试(本方案的--restart unless-stopped+ ffmpeg 内部重连)即可自愈,也可用depends_on约束。 - 改了 ABR 档位但
llhls.m3u8没变化:llhls.m3u8是引擎自动生成的默认单档列表;自定义多档列表访问的是Playlist/FileName指定的名字(本方案master.m3u8)。
十、参考链接
- 官方 Docker 文档:https://docs.ovenmediaengine.com/docs/ome/getting-started/getting-started-with-docker
- Docker Hub:https://hub.docker.com/r/ovenmedialabs/ovenmediaengine
- 官方配置模板:https://github.com/OvenMediaLabs/OvenMediaEngine/blob/master/misc/conf_examples/
- 本站系列文档:《OvenMediaEngine 介绍与选型指南》(架构与选型)、《OvenMediaEngine 0.21.0 配置参数详解》(Server.xml 逐段精讲)、《OvenMediaEngine 0.21.0 常用运维命令与排障详解》(日志/API/排障实录)