Docker部署SRS流媒体服务器:RTMP与WebRTC低延迟直播实战指南
很多人第一次接触 SRS,都是因为在某个讨论组里看到同事甩出一条
rtmp://xxx/live/xxx
的推流地址,或者是在公司内部想搭一套能支撑直播、连麦、摄像头接入的实时音视频流媒体平台,结果发现 Nginx 那一套方案要么撑不住高并发,要么在 WebRTC 和低延迟这块绕来绕去。我自己最开始也是从 Nginx-RTMP 模块入门的,后来被项目逼着接入 WebRTC 和 SRT,才彻底转投 SRS。如果你也正在纠结“用什么方案自建流媒体服务”,又希望部署过程足够省心,那 Docker + SRS 这套组合可以说是我目前用下来最顺手的方案。这篇文章会把我的选型思路、完整的部署过程、实测遇到的坑和进阶配置一起梳理出来,希望能帮你少走几步我走过的弯路。
做这套部署之前,先说清楚目标场景:我这里要搭建的是一个支持 RTMP、HTTP-FLV、HLS 和 WebRTC 四类协议共存的实时音视频流媒体平台,前端用 OBS 推流,浏览器和 VLC 都能拉流播放,后面还要留出鉴权和转码的升级空间。文章里的每一步操作都基于这个目标展开,不涉及复杂的集群和高可用,适合中小型项目、内网服务和个人开发者参考。
1. 为什么是 SRS + Docker,而不是其他组合
1.1 SRS 在流媒体服务器里的定位
SRS 全称是 Simple Realtime Server,中文名叫“简单实时服务器”,由国内开发者主导开源,目前在 GitHub 上有非常活跃的社区,文档的中文支持也做得很好。它在流媒体服务器里的定位,简单说就是“一套服务,把推流、拉流、协议转换、低延迟传输全包了”。相比 Nginx-RTMP 需要自己拼凑插件和模块,SRS 原生就支持 RTMP、HLS、HTTP-FLV、WebRTC、SRT、GB28181 等一长串协议,这种多协议支持能力让它的应用场景一下子被拉开:直播平台可以用它做源站,在线教育能用它支撑低延迟互动,安防项目还能直接对接国标摄像头。
我当初选 SRS 还有一个很现实的原因:WebRTC 的支持做得很顺手。Nginx-RTMP 对 WebRTC 几乎是无能为力的,如果只做传统 RTMP 直播倒无所谓,但一旦要做网页低延迟播放、视频连麦这些功能,SRS 基本上是一条捷径。它的内置 WebRTC 网关能直接接收 WHIP 推流,也能通过内置播放器做 RTC 拉流,这在同类开源方案里非常少见。
1.2 Docker 解决的最大痛点
SRS 本身是一个 C++ 写的服务,官方也提供了编译安装的方式。但如果你像我一样,需要频繁切换版本、在不同服务器上重复部署,又或者只是想在本地 Mac/Windows 上快速跑一个测试环境,那编译安装就有点让人头疼了——依赖环境、编译参数、版本兼容性,随便一个都能耗掉你半个下午。
Docker 在这里解决的最大痛点,就是
把 SRS 和它的运行时环境打包成了一个不可变单元
。本地跑起来不过十几秒,切换到生产服务器也只是
docker pull
一下的事,不用关心目标机器上缺了哪个库、glibc 版本够不够。这一点对后续升级也特别重要:SRS 版本从 4.0 升到 5.0 的时候,我只需要改一个镜像 tag,然后重启容器,就完成了整个软件的升级,配置文件和宿主机上的其他服务完全不受影响。
另外一个容易被忽略的优势是 资源隔离 。流媒体服务一旦并发上来,CPU 和带宽占用都很猛,Docker 的容器化让它可以被限制在可控的 CPU、内存范围内,不至于因为某一路异常流把整台服务器拖垮。
2. 部署前的网络规划与镜像准备
2.1 端口清单:一次想清楚,后面少返工
SRS 涉及的协议和端口比较多,部署前一定要先把端口映射的规划想明白。我个人习惯是先把一张端口清单列出来贴在终端旁边,后面启动容器和配置防火墙的时候对照着看,能省掉很多低级错误。
| 端口 | 协议 | 用途 | 是否必开 |
|---|---|---|---|
| 1935 | TCP | RTMP 推拉流 | 必须要开 |
| 1985 | TCP | HTTP API 接口 | 建议开启 |
| 8080 | TCP | HTTP 服务(播放器页面、FLV 拉流) | 建议开启 |
| 8000 | UDP | WebRTC 信令媒体传输 | WebRTC 场景必开 |
| 8001 | UDP | WebRTC 媒体传输备份端口 | WebRTC 场景必开 |
大多数人在配置阶段最容易犯的错,就是只放行了 TCP 端口,结果 WebRTC 怎么都打不开。WebRTC 的音视频数据走的是 UDP,只听 TCP 是不够的。另外,RTP 协议族的端口范围比较大,如果严格做了防火墙白名单,建议把 UDP 10000-20000 也可能涉及到的范围一并考虑进去,具体取决于你后面的 SRS 配置方式。
2.2 镜像拉取与加速配置
SRS 官方 Docker 镜像有两个地址,一个是 Docker Hub 的
ossrs/srs
,另一个是阿里云的
registry.cn-hangzhou.aliyuncs.com/ossrs/srs
。如果你在国内的云服务器上操作,我强烈建议直接用阿里云镜像地址,别跟 Docker Hub 的超时问题死磕。我的服务器在香港和国内都有测试过,国内节点直接拉 Docker Hub 经常需要几分钟甚至直接超时,换成阿里云地址后基本秒级完成。
注意:拉取镜像时建议显式指定版本 tag,不要用
latest。SRS 版本升级引入的行为变化不少,一旦拉到一个意外的新版本,可能你的配置文件就不兼容了。我目前使用的是:5这个 tag,对应 SRS 5.0 系列。
如果只想拉镜像先看看,可以执行这条命令:
docker pull registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
如果你使用的服务器已经配置了私有镜像仓库,或者在离线环境部署,也可以用
docker save
/
docker load
的方式迁移镜像,这个属于基础操作,不再展开。
3. 手写配置与一键启动:完整部署过程
3.1 配置文件目录结构与 srs.conf 详解
SRS 的配置逻辑不算复杂,核心是一个
srs.conf
文件,所有协议、虚拟主机、录制、转码、鉴权策略都在这个文件里定义。用 Docker 部署的时候,我习惯把宿主机上的一个目录挂载进容器,这样改配置文件以后,只需要重启容器就能生效,不用重新构建镜像,非常方便。
我的工作目录长这样:
/opt/srs
├── conf
│ └── srs.conf
└── docker-compose.yml
下面是我实际在用的
srs.conf
文件,注释已经写清楚了每个配置项的作用:
# srs.conf
listen 1935;
max_connections 1000;
daemon off;
srs_log_tank console;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
rtc_server {
enabled on;
listen 8000;
candidate 127.0.0.1;
}
vhost __defaultVhost__ {
rtc {
enabled on;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 2;
hls_window 10;
}
}
逐段拆解一下:
-
listen 1935:SRS 主监听端口,RTMP 协议专用。 -
daemon off+srs_log_tank console:让 SRS 以前台模式运行,日志直接打到控制台。在 Docker 里,这个配置几乎是必须的——容器要靠前台进程保活,日志也要通过 stdout 交给 Docker 的日志系统。 -
http_api:开启 1985 端口的 HTTP API,通过它就可以查询在线流、踢流、获取服务器状态。测试链路的时候特别有用。 -
http_server:开启 8080 端口的 HTTP 文件服务,SRS 自带的 WebRTC 播放器页面就靠它来承载。 -
rtc_server:WebRTC 的媒体面入口。candidate这个参数是后面最容易踩坑的地方,测试环境可以配127.0.0.1,但生产环境一定要改为服务器公网 IP 或域名,否则客户端无法把媒体包发回来。 -
vhost __defaultVhost__:FRS 的虚拟主机概念,类似 Nginx 的 server 块。默认虚拟主机接受任意 Host 头,适合简单部署。 -
rtc:开启该虚拟主机下的 WebRTC 支持。 -
http_remux:把 RTMP 流实时转换成 HTTP-FLV 流,浏览器里的播放器就是通过这个协议做低延迟播放的。 -
hls:开启 HLS 切片输出。hls_fragment是每个切片的时长(秒),hls_window是窗口里保留的切片数量。这个功能适合需要兼容 iOS Safari 等不能直接播放 FLV 的场景。
3.2 docker-compose.yml 与启动命令
接下来是
docker-compose.yml
。我使用 compose 而不是裸的
docker run
,原因很简单:端口映射、目录挂载、重启策略这些配置可以写进代码,换一台机器部署时直接拷贝文件即可,不会因为忘记了某个参数导致环境不一致。
services:
srs:
image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
container_name: srs
restart: unless-stopped
network_mode: host
volumes:
- ./conf/srs.conf:/usr/local/srs/conf/srs.conf
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
这里有一个很关键的选型:
network_mode: host
。我在前两轮用 bridge 模式部署过,结果 WebRTC 的 UDP 端口映射和 candidate 配置都要额外处理,经常出现能推不能拉的情况。后来改成 host 网络,容器直接共享宿主机的网络栈,端口映射的问题就全部消失了,SRS 看到的 IP 就是宿主机 IP,candidate 配置也简单直接。
注意:host 网络模式在 Docker Desktop(Windows 和 macOS)上支持不完整,如果你只是在本地做测试,建议在 Linux 服务器上实践这套配置。这也是为什么我推荐把设计好的 Compose 文件直接拿到云服务器或内网 Linux 主机上运行,而不是依赖本地虚拟机。
启动服务:
cd /opt/srs
docker compose up -d
查看状态:
docker ps | grep srs
docker logs -f srs
如果配置没问题,日志里应该会出现类似这样一行:
SRS is up. You can play the following streams:
此时基础服务已经跑起来了。
4. 从推到拉:用 OBS 和播放器验证整套链路
4.1 OBS 推流配置,关键参数别选错
服务端起来了,接下来就是用实际流来验证链路。我用 OBS Studio 做推流端,因为它在推流参数上暴露得足够清楚,方便排查问题。
OBS 里打开“设置 — 直播”:
- 服务:自定义
-
服务器:
rtmp://你的服务器IP/live -
推流码:
test
这里有一个容易踩的坑:推流码不要带前缀斜杠,直接写
test
。有些教程会写成
/test
,在某些版本下虽然也能通,但流的
app
和
stream
参数解析会和你预期的不同,导致播放地址对不上。
关于视频编码,如果你的网络带宽充足,建议先用 H.264 + 普通的 CBR 码率;音频建议先用 AAC。WebRTC 播放端对 H.264 + AAC 的兼容性总体最好,如果你一上来就推 HEVC 或者 Opus 音频,后面拉流很容易在某个环节莫名其妙黑屏/无声。
4.2 三种方式拉流验证
OBS 点击“开始推流”之后,我们可以从三个角度验证流是否正常。
第一种,用 VLC 拉 RTMP 流。打开 VLC,选择“打开网络串流”,地址填:
rtmp://你的服务器IP/live/test
如果能正常播放,说明 RTMP 链路通了。
第二种,用浏览器拉 HTTP-FLV。SRS 在 8080 端口内置了测试播放器页面,直接访问:
http://你的服务器IP:8080/players/srs_player.html
在播放地址框里填:
http://你的服务器IP:8080/live/test.flv
点播放,如果浏览器能流畅出画面,说明 HTTP-FLV 链路正常。这个方式对低延迟直播特别有用,延迟可以控制在 1 到 3 秒。
第三种,验证 WebRTC。访问:
http://你的服务器IP:8080/players/rtc_player.html
地址填:
webrtc://你的服务器IP/live/test
这里有两个大坑点,我后面会单独展开讲。如果 WebRTC 播放页能正常看到画面和声音,说明 UDP 媒体通路和 candidate 配置都没问题,整套链路就基本打通了。
4.3 用 HTTP API 确认流状态
播放验证没问题后,还可以通过 HTTP API 再做一次“体检”:
curl http://127.0.0.1:1985/api/v1/streams
正常会返回一个 JSON 数组,里面包含当前在线的流列表以及每个流的
app
、
stream
、
vhost
、
create
时间等元数据。这个接口在后面的自动化运维和监控告警里非常有用,也是流是否真正稳定在线的权威凭证。
5. 翻车现场记录:部署和试用中的真实坑点
5.1 我在内网环境第一次启动时的完整排查链路
说一个我真实经历过的翻车现场,因为很有代表性:内网服务器第一次部署完后,OBS 能正常推流,VLC 也能拉到 RTMP 流,但办公室同事的浏览器打不开 WebRTC 播放页面,一直转圈。
我当时的第一步是检查 SRS 日志,
docker logs -f srs
里能看到 WebRTC 推流或拉流的握手记录。日志里在拉流请求的位置抛出了
parse candidates error
之类的信息。
第二步,确认 candidate 配置。我在
rtc_server
里把 candidate 配成了
127.0.0.1
,服务器自己认为自己是本机回环地址,发给浏览器的 ICE candidate 当然就包含
127.0.0.1
。浏览器拿到一个指向本机的 candidate,怎么可能连到内网服务器。把配置改成服务器的内网 IP 之后,WebRTC 拉流才能正常。
第三步,检查 UDP 端口通不通。直接在服务器上执行:
nc -uvz 你的服务器IP 8000
以及
nc -uvz 你的服务器IP 8001
如果返回
Connected
说明端口可达。如果网络管理员没有放行 UDP 8000/8001,浏览器就可能一直卡在 ICE 连接超时,和 candidate 配置错误的现象高度相似。
整个排查链路走下来,我才意识到 WebRTC 这个环节的“网络 — 配置 — 防火墙”三者必须同时装对,少一个都不行。后来我把这套排查顺序整理成了固定流程: 先看日志 → 再查 candidate → 最后查防火墙 ,再也没在 WebRTC 这个问题上卡过超过十分钟。
5.2 音频编码引发的无声玄学
另一个容易被忽略的坑是音频编码。SRS 5.0 里对音频的兼容逻辑有一定调整,如果你的推流端用的是带 Opus 编码的 WebRTC 推流,而拉流端用的是 HLS 或者 RTMP 播放器,声音偶尔会出现无法播放或延迟异常的情况。这在混合协议场景下很常见——RTMP 和 HLS 对音频的要求是 AAC 为主,WebRTC 则偏爱 Opus。
如果你的直播场景里有网页低延迟互动,又有传统的 RTMP/HLS 分发需求,最省心的办法是推流端统一用 AAC 音频编码,并在 SRS 配置里把 WebRTC 到 RTMP 的转封装逻辑确认好。视频编码尽量用 H.264(而不是 H.265/HEVC),因为 HEVC 在 WebRTC 的浏览器兼容性上至今还有不少坑。
注意:Windows 录屏或游戏直播场景中,OBS 默认音频设备可能是 48kHz 的立体声,这本身没问题。但如果你在采集设备上启用了“音频增强”或“降噪”功能,推上去的音频在某些播放器里可能出现爆音或静音,建议推流测试时把音频滤镜全部关掉。
5.3 Docker 与宿主机的时区问题
还有一个很小但很容易让人误判的问题:时区。SRS 日志默认使用 UTC 时间,而国内服务器的本地时间通常是 UTC+8。排查问题时你可能会发现,日志里的时间“慢了 8 小时”,如果不够细心,很容易误认为系统有延迟或者服务重启过。
解决办法是在 Compose 文件里注入系统时间:
environment:
- TZ=Asia/Shanghai
添加这一行后重启容器,日志时间就和本地时间对齐了。这个配置在后续排障、对比推流时间时能节省不少心力。
6. 让 SRS 真正可用:鉴权、HTTPS 和转码扩展
6.1 推流鉴权:防止陌生人往你的服务器上推流
基础链路通了以后,首先要考虑的就是安全。SRS 如果不做任何鉴权,任何知道你的服务器地址的人都可以往
live
应用里推流或者拉流,这在公网环境里非常危险,很快就有被人塞满垃圾流口的风险。
SRS 支持通过 HTTP 回调做推流鉴权,原理是:有推流请求进来时,SRS 会向你的业务服务器发送一个 HTTP 请求,带上推流的
app
、
stream
、
param
等信息,你的业务服务器返回 0 就允许推流,返回非 0 就拒绝。
配置片段示例:
vhost __defaultVhost__ {
http_hooks {
enabled on;
on_publish http://你的业务服务器地址/api/srs/on_publish;
on_play http://你的业务服务器地址/api/srs/on_play;
}
}
这种鉴权方式的灵活度很高,你可以根据推流码中的 token 来判断是否有权限,也可以对接自己的用户体系。实测中需要注意:回调接口必须要快速响应,建议控制在 100 毫秒以内,否则推流端会感觉“卡了一下才推上去”。
6.2 HTTPS 与 WSS:跑公网无法回避的问题
如果你需要把流媒体平台暴露到公网,且网站本身用的是 HTTPS,那 8080 端口上的 HTTP 播放器页面就会出现“混合内容”问题,浏览器会禁止播放 HTTP 的流。此时你需要给 SRS 配置 HTTPS/WSS。
SRS 的 TLS 配置不复杂,需要准备证书文件(或者使用反向代理做 TLS 终结)。我自己的习惯是前面放了一层 Nginx 反代,终止 HTTPS 和 WSS,然后转发到 SRS 的 1985/8080 端口,这样 SRS 本身不需要管理证书,证书续期也统一到 Nginx 上处理。
如果你不想引入 Nginx,也可以直接在 SRS 里配置 TLS,需要在
http_server
和
rtc_server
里分别开启 HTTPS/WSS,并把证书路径挂载进容器内:
http_server {
enabled on;
listen 8080;
https {
enabled on;
listen 8443;
key ./conf/server.key;
cert ./conf/server.crt;
}
}
改完配置后重启容器即可生效。公网环境下,WSS 是 WebRTC 拉流的基本要求,纯 WebSocket 是不被浏览器接受的。
6.3 转码、录像与更多玩法
SRS 本身不侧重转码,它对标的是流媒体服务,转码能力有限。如果你有转码需求,比较常见的套路是让 SRS 把流回调给 FFmpeg 或专门的转码服务,或者直接用 FFmpeg 在推流端做二次推流。这个属于进阶玩法了,我这篇主要讲部署,先点到为止。
录像功能倒是 SRS 原生支持的。在
vhost
里开启
dvr
模块:
vhost __defaultVhost__ {
dvr {
enabled on;
dvr_path ./objs/nginx/html/record/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
dvr_plan session;
}
}
dvr_plan session
表示每次推流会话生成一个独立的录像文件,录像文件路径支持按时间戳自动生成目录,方便后续做回看管理。容量管理上要特别留意,录像文件增长很快,建议挂载一块独立的数据盘并配置周期清理策略。
7. 我这一路用下来的体会与延伸建议
部署这条链路到现在,我最大的体会是: 流媒体服务最难的不是把服务跑起来,而是让各种不同协议之间能真正协作起来。 Docker 帮我们解决了系统环境的一致性问题,SRS 帮我们解决了很多协议兼容问题,但剩下的——网络规划、编码选型、安全策略——仍然需要自己动手去理解和测试。
如果你准备在生产环境推进,我最后再分享三个建议:
第一,无论服务多么简单,建议从一开始就启用 HTTP API 的访问日志记录。SRS 的 API 接口非常方便做监控,自己写一个定时任务去拉取
api/v1/streams
并记录在线流数量,就能对你平台的实际使用情况有一个持续的量化认知。
第二,推流端的编码参数尽量提前统一。我经历过一次线上事故,就是某个主播用 HEVC 推流,而平台大部分播放器都只支持 H.264,结果他的直播间在网页端直接黑屏。后来我们把推荐编码写进了主播端的开播设置里,从源头避免这类兼容性事故。
第三,容器版本升级前,一定先在生产环境之外做一次完整回归。SRS 官方文档里有详细的版本迁移说明,不要想当然地认为小版本升级不会破坏现有配置。我有一次从 4.0 升到 5.0 时,配置文件里 HTTP 回调的字段格式有变化,导致鉴权短暂失效,后来对照官方文档逐一核对才恢复正常。
Docker + SRS 这套方案的坑基本都在这里了。按照这篇文章的顺序来部署,正常情况下你两个小时之内就能拥有一个支持 RTMP、HTTP-FLV、HLS 和 WebRTC 的实时音视频流媒体平台。后续的录像、鉴权、转码、监控等需求,也都可以在这个基础架构上稳步展开。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)