Docker部署SRS流媒体服务器:从零搭建直播推流与播放平台
开头我直接以一个真实经历切入:上个月帮朋友接了个小项目,对方要求在半天内搭一个能支撑几百人同时观看的直播服务,还要能随时回放。我第一反应是上SRS,第二反应是必须用Docker。为什么?因为SRS虽然功能强,但编译安装的依赖链太长——gcc、cmake、ffmpeg、openssl,版本一不对就是一下午。而Docker部署SRS这套组合,从拉镜像到出流,熟练的话十分钟搞定。这篇就把我踩过的坑和验证过的方案完整写出来,给想自己搭流媒体平台的朋友一条能直接走通的路。
1. 为什么是 SRS,又为什么必须配 Docker
1.1 SRS 在流媒体领域到底扮演什么角色
SRS全称是Simple Realtime Server,一个开源的实时流媒体服务器。它最核心的价值是把各种乱七八糟的流媒体协议统一收编:推流端可以用RTMP、SRT、WebRTC推上来,播放端可以用RTMP、HLS、HTTP-FLV、WebRTC拉出去。一套服务,多端接入,省掉了同时维护几个服务器的痛苦。
我第一次接触SRS是2018年,当时在做一个在线教育的项目,需要老师端推流、学生端低延迟观看。市面上的方案无非是Nginx-RTMP模块、Red5、SRS三选一。Nginx-RTMP模块胜在轻量但功能单薄,Red5重且Java系维护成本高,SRS则是功能最全的——内置了HLS切片、DVR录像、鉴权回调、集群源站边缘,而且社区活跃,文档质量高,很多国内直播公司的内核就是它。
到现在SRS已经迭代到5.0,WebRTC的体验已经相当成熟。官方把SRS定位为"工业级的流媒体集群",但即便单机部署,也能轻松支撑几千路并发拉流。对于中小型项目、企业内部直播、个人视频站来说,SRS属于那种"装了就能顶很久"的基建。
1.2 Docker 化解了流媒体服务最头疼的环境问题
流媒体服务器的部署难点从来不在SRS本身,而在它依赖的底层库。SRS虽然提供了编译好的release包,但你拿到手还是得手动处理各种动态库、系统版本兼容。我就在Ubuntu 20.04上遇到过openssl版本不匹配导致RTMP握手失败的怪问题,排查了一下午,最后发现是系统自带的libssl和SRS编译时用的版本不一致。
Docker把这一层彻底抹平了。官方镜像ossrs/srs把SRS运行所需的所有依赖都打进了一层层的镜像层里,你拉下来就是一个开箱即用的完整环境。这意味着:
- 不管宿主机是CentOS 7还是Ubuntu 22.04,容器内的行为完全一致
- 升级SRS版本只需要换镜像tag,不需要动系统里的任何库
- 在同一台机器上跑多个不同版本的SRS做对比测试,端口隔离就行,互不干扰
- 迁移时导出镜像或直接换个机器拉镜像,配置文件挂出来,几分钟恢复一套环境
尤其是最后一件事,我深有体会。之前用裸机部署SRS,从一台服务器迁到另一台,光是把依赖环境重搭一遍就花了半天。改成Docker后,docker run一个命令搞定。
提示:SRS官方镜像分为alpine版和ubuntu版。alpine版镜像体积小(约100MB),适合生产环境;ubuntu版体积大但包含了一些调试工具,适合学习时进去排查问题。首次上手建议先用ubuntu版,出了奇怪问题能进容器用netstat、tcpdump排查,alpine里连bash都没有,很多工具要自己装,排查问题会卡手。
2. 动手前的环境准备:Docker 安装与镜像加速
2.1 不同平台的 Docker 安装选型
装Docker这件事,理论上是"下载、安装、启动"三步,但实际上平台不同差异很大。
Linux 最顺利,官方提供了脚本一键安装:
curl -fsSL https://get.docker.com | bash
systemctl enable --now docker
docker version
如果你用的是CentOS 7这种老系统,脚本可能装的是docker-ce 19.03这种老版本,建议装完手动确认一下版本,SRS 5.0的镜像对Docker版本要求不算高,但太老的版本(比如18.09之前)在overlay2存储驱动上会有兼容问题。
Windows 是重灾区。Docker Desktop需要WSL2作为底层虚拟化方案,如果你没装WSL2或者内核版本太旧,启动时会直接报虚拟化支持未检测到的错误,几乎是Windows用户遇到最多的报错之一。
我自己的经验是:Windows上装Docker Desktop前,先在PowerShell(管理员)里执行:
wsl --install
然后重启电脑,再装Docker Desktop。装完后打开设置,确保Settings -> Resources -> WSL Integration里的对应发行版开关是开的。如果你用Windows跑生产环境,那就扯远了,建议直接装Linux虚拟机,Docker Desktop只适合本地开发联调。
macOS 上Intel和Apple Silicon的安装包是分开的,别下错。M系列芯片跑Docker Desktop偶尔会遇到端口映射不通的问题,实际上可能是Docker的虚拟机网络模式问题,通常重装或者重启Docker Desktop能解决。
2.2 镜像加速是部署前的必做项,不是可选项
国内拉Docker官方镜像的速度,不用我多说吧。第一次拉SRS镜像,5MB的层卡了十分钟,进度条纹丝不动,我以为是网络问题,后来发现就是没配镜像加速。
配置加速的方法,以Linux和Docker Desktop为例:
Linux下编辑
/etc/docker/daemon.json
:
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com"
]
}
然后重启Docker:
systemctl restart docker
Windows的Docker Desktop在Settings -> Docker Engine里直接编辑同样的JSON即可,右下角Apply & Restart生效。
选加速地址有个注意点:网上流传的加速地址很多失效了,配置了不生效的话拉镜像照样慢。配好之后先验证一下:
docker info | grep -A 5 "Registry Mirrors"
能看到镜像列表,再试拉一个小镜像测试速度,确认了再拉SRS。
注意:任何第三方加速源都有可能关闭或限速,这不是你配置的错。如果某天突然拉不动了,先检查加速源可用性,再考虑换一个。自建Docker Registry代理是另一个话题,本文不展开。
3. 部署最小可用 SRS:镜像选择、配置编写与容器启动
3.1 镜像标签怎么选
SRS的历史版本主要用分支号区分,现在主流是5.0,4.0还在维护,3.0已经进入EOL。官方镜像标签的规律是:
ossrs/srs:5
表示5.x最新版,
ossrs/srs:4
表示4.x最新版,
ossrs/srs:latest
现在指向5.0系列。
我做生产部署时通常直接用
ossrs/srs:5
,因为它的tag会跟随5.x的小版本更新。但如果你对稳定性要求极高,建议锁定具体版本号,比如
ossrs/srs:5.0.216
。某次我升级SRS后配置文件里的一个字段被废弃,服务启动失败,排查了半天发现是版本行为变更,从那以后生产环境一律锁版本号。
3.2 最小配置文件解析
SRS默认的配置在容器内是
/usr/local/srs/conf/srs.conf
,但你别直接改容器里的,正确做法是:把配置文件放在宿主机目录里,启动时通过挂载卷覆盖进去。下面这份是我验证过的最小可用配置,支持RTMP推流、HTTP-FLV播放、HLS播放和WebRTC播放:
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 $CANDIDATE;
}
vhost __defaultVhost__ {
rtc {
enabled on;
}
hls {
enabled on;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}
几个核心推荐说明一下:
-
listen 1935是RTMP协议的默认端口,推流和RTMP播放都走它 -
http_api是SRS的管理接口,支持调用API查询在线流、踢流、鉴权等,后面生产化会用到 -
http_server是SRS内置的HTTP服务,HLS切片和HTTP-FLV播放的文件由它提供 -
rtc_server是WebRTC的UDP端口,注意这里是listen 8000/udp,Docker映射时要带上/udp后缀,很多新手卡在这里,把UDP端口映射成TCP了 -
candidate $CANDIDATE是WebRTC的ICE候选地址,用环境变量传入服务器公网IP或内网IP,这是WebRTC能不能连上的关键 -
http_remux是HTTP-FLV的挂载点配置,[vhost]/[app]/[stream].flv表示播放地址形如http://服务器IP:8080/live/test.flv
3.3 启动容器的完整命令与端口规划
配置文件准备好后,我习惯用一个单独目录存放SRS相关文件:
mkdir -p /opt/srs/conf
cd /opt/srs
vim conf/srs.conf
# 粘贴上面的配置
然后启动容器:
docker run -d --name srs \
-p 1935:1935 \
-p 1985:1985 \
-p 8080:8080 \
-p 8000:8000/udp \
-e CANDIDATE=192.168.1.100 \
-v /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf \
--restart=always \
ossrs/srs:5
解释一下几个容易忽略的参数:
-
-d后台运行,但如果容器起不来看不到日志,可以先不加-d用前台模式运行,能看到SRS输出到控制台的所有日志,确认没问题再Ctrl+C停掉,换-d正式启动 -
-p 8000:8000/udp明确指定UDP协议,漏掉/udp的话Docker会默认为TCP,WebRTC连不上 -
-e CANDIDATE=192.168.1.100填入的是客户端能访问到的服务器IP,如果是公网服务器就填公网IP,内网测试就填内网IP。填错了WebRTC能推流但别人拉不到流 -
--restart=always让Docker在宿主机重启或容器崩溃后自动拉起SRS,生产环境必备
如果你想更进一步用Docker Compose管理,下面是同等的docker-compose.yml:
services:
srs:
image: ossrs/srs:5
container_name: srs
restart: always
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
- "8000:8000/udp"
environment:
- CANDIDATE=192.168.1.100
volumes:
- /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf
启动后查看日志确认SRS正常运行:
docker logs -f srs
看到类似
SRS is running now
的日志,说明服务已经就绪。这时可以访问
http://服务器IP:8080/
,能看到SRS的默认欢迎页。
4. 推流与播放验证:从 FFmpeg 到多种播放协议
4.1 用 FFmpeg 推一路测试流
SRS跑起来了,但你还需要验证它真的能干活。验证方式就是推一路流进去,再拉出来看。
FFmpeg是流媒体领域最常用的推流工具,装一下:
# Ubuntu / Debian
apt install ffmpeg
# CentOS
yum install ffmpeg
如果你的机器上没装FFmpeg,Windows和macOS可以直接下载官方编译好的二进制包,免安装。
准备一个测试视频文件,没有的话用FFmpeg生成一个彩条测试卡:
ffmpeg -f lavfi -i testsrc=size=1280x720:rate=30 \
-f lavfi -i sine=frequency=440:sample_rate=44100 \
-t 60 \
-c:v libx264 -preset ultrafast -c:a aac \
test.mp4
然后用FFmpeg把视频循环推送SRS:
ffmpeg -re -stream_loop -1 -i test.mp4 \
-c copy -f flv rtmp://192.168.1.100/live/test
参数说明:
-re
表示按视频原始帧率推流,避免CPU空转而推流速度失控;
-stream_loop -1
是无限循环推送,适合做长期测试。
推流后SRS日志会增加一条流上线记录,同时可以通过API验证:
curl http://192.168.1.100:1985/api/v1/streams
返回的JSON里会包含
test
这条流的详细信息。
4.2 三种播放协议,按场景选
验证播放,我一般会同时测试RTMP、HTTP-FLV、HLS三种协议,因为它们的应用场景不同:
| 协议 | 播放地址示例 | 延迟 | 适用场景 |
|---|---|---|---|
| RTMP |
rtmp://192.168.1.100/live/test
| 1-3秒 | 传统Flash播放器、部分直播工具 |
| HTTP-FLV |
http://192.168.1.100:8080/live/test.flv
| 1-3秒 | Web端低延迟直播,配合flv.js |
| HLS |
http://192.168.1.100:8080/live/test.m3u8
| 5-15秒 | iOS/Android原生播放器、点播回放 |
我推荐优先用HTTP-FLV测,因为它在浏览器端不需要任何插件,用flv.js就能播放,而且延迟和RTMP相当。VLC播放器的网络串流地址直接填上面的RTMP或HLS地址都能拉流。
WebRTC的对等连接测试需要额外的信令服务器配合,SRS官方提供了demo页面但又依赖SRS的信令API。如果走WebRTC播放,地址通常由前端调用API动态生成,这里不展开。
4.3 原始数据到底有没有问题,我习惯这样判断
单纯看到播放器里面有画面,并不能说明服务端配置就是最优的。还有几个点我会顺手检查:
推流时的码率。用
ffmpeg -re -i test.mp4 -c copy -f flv
这种copy方式推流,码率取决于原视频。如果想模拟真实直播的码率波动,可以用
-b:v 2500k -maxrate 3000k -bufsize 5000k
限制视频码率。
多路并发测试。我压测过单机SRS撑500路HTTP-FLV拉流,CPU占用大约在30%左右,基本无压力。但并发拉流会同时增加带宽消耗,如果你的服务器带宽只有10Mbps,一百路4Mbps的流就会把带宽撑爆。这个属于网络规划问题,和SRS本身无关。
5. 生产化改造:鉴权、HTTPS、持久化与会话保持
5.1 给推流和播放加鉴权,避免被白嫖
默认配置下,任何人都能往你的SRS上推流,这不是你想看到的结果。SRS提供了几种鉴权方案,最通用的是HTTP回调鉴权。
思路是这样的:客户端推流或播放时,SRS会向你的业务服务器发一个HTTP请求,你的服务器校验通过后返回0,拒绝返回非0,SRS根据响应决定是放行还是拒绝。
配置方法:
vhost __defaultVhost__ {
http_hooks {
enabled on;
on_publish http://your-api-server.com/api/srs/auth;
on_play http://your-api-server.com/api/srs/auth;
}
}
你的后端只要实现一个接口,返回HTTP 200且body为0表示通过。SRS回调时会带上
app
、
stream
、
param
等参数,
param
是推流地址中
?
后面的查询参数,通常用来传递签名。
我实际项目中的推流地址长这样:
rtmp://192.168.1.100/live/test?token=abc123
业务服务器收到回调后解析
param
中的token,和数据库中的密钥比对。这个方法实现简单,而且不需要在SRS里配置一堆密钥,密钥管理完全由业务方控制。
提示:Docker容器内发起HTTP回调需要网络能通到你的业务服务器。如果SRS容器和你API服务器在同一个宿主机上,回调地址不能用
127.0.0.1,因为那是容器内部自己。要填宿主机IP,或者用host.docker.internal(Docker Desktop在macOS/Windows可用,Linux需要额外配置)。
5.2 HTTPS 接入,让播放地址更可靠
浏览器环境对HTTPS的要求越来越严格,不少浏览器已经禁止HTTP协议下的WebRTC连接。SRS支持直接配置SSL证书,也可以在前面挂一层Nginx反向代理。个人更推荐后者,因为SSL证书的更新维护、HTTP/2的支持都在Nginx层搞定,SRS专注做流媒体转发。
但如果你不想引入额外的Nginx,SRS也支持直接配置证书:
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
https_server {
enabled on;
listen 8088;
dir ./objs/nginx/html;
key ./conf/server.key;
cert ./conf/server.crt;
}
把证书文件通过卷挂载进容器:
-v /opt/srs/cert/server.key:/usr/local/srs/conf/server.key \
-v /opt/srs/cert/server.crt:/usr/local/srs/conf/server.crt
HTTPS的播放地址就会变成
https://服务器IP:8088/live/test.flv
。需要注意self-signed证书浏览器会报错,生产环境建议用Let's Encrypt或云厂商的免费证书。
5.3 配置持久化与日志处理,避免一台Docker把数据绑死
Docker容器的文件系统是临时的,容器被删掉后所有数据都没了。因此配置文件的持久化上,我们已经在启动命令中用卷挂载把配置文件放在了宿主机
/opt/srs/conf
下。日志也一样,SRS默认的
srs_log_tank console
是把日志输出到标准输出,由Docker日志机制收集。如果你需要日志落盘,建议配置为文件并挂载目录:
srs_log_tank file;
srs_log_file ./objs/srs.log;
启动时再加一个挂载:
-v /var/log/srs:/usr/local/srs/objs
这样日志会写到宿主机的
/var/log/srs
目录,方便接入ELK等日志收集系统。
另外,如果你的业务需要DVR录像功能,SRS可以把直播流录制成FLV文件,这些文件也一定要挂载到宿主机的数据盘上,否则容器重建录制的文件就全没了。DVR的配置:
vhost __defaultVhost__ {
dvr {
enabled on;
dvr_path /data/dvr/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
dvr_plan session;
}
}
dvr_plan session
表示一个推流会话生成一个录制文件。
5.4 WebRTC 能通但经常吞流?检查 UDP 端口的连通性
很多人在验证WebRTC时遇到一个问题:RTMP推流正常,HTTP-FLV播放正常,唯独WebRTC推流不稳定,偶尔成功偶尔失败。导致这个问题的原因,我排查过几次后发现绝大多数是UDP端口不可达。
SRS的WebRTC默认监听8000/udp,需要确认服务器防火墙、安全组规则中放行了UDP端口。在服务器上可以这样验证UDP端口是否可达:
# 服务器本机测试UDP端口
nc -uvz 127.0.0.1 8000
如果是云服务器,还需要在云控制台的安全组入站规则中放行UDP 8000。很多云厂商默认的安全组只放行了TCP端口,UDP经常被遗漏。
6. 部署与运行中的典型故障排查
6.1 容器起不来:先分清是配置问题还是端口占用
我用Docker部署SRS以来,遇到过几次容器启动失败的情况。排查第一步永远是把启动方式从
-d
改成前台模式:
docker rm -f srs
docker run --rm --name srs \
-p 1935:1935 \
-v /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf \
ossrs/srs:5
不加
-d
,容器直接在前台跑,log输出到终端,问题往往一目了然。最常见的是配置错误,SRS会给出
parse config file failed
的中英文日志提示你哪一行语法出错。
另一种情况是端口被占用。如果你的宿主机上已经有别的服务占用了1935端口,容器会启动失败。用下面的命令确认:
netstat -tlnp | grep 1935
确认端口冲突后,要么停掉占用端口的进程,要么修改SRS的listen端口,并把Docker的端口映射改成
-p 1936:1935
之类的宿主机其他端口。
注意:SRS容器里的配置必须和容器内的路径匹配,比如
listen 1935对应映射-p 1935:1935。如果改了SRS的listen端口为1936,容器端口必须是1936,映射关系也相应调整,不能只改一侧。
6.2 看日志定位问题:SRS的日志信息密度很高
SRS的日志做得非常详细,几乎每个关键步骤都有输出。排查问题先看日志,比瞎猜配置高效得多。
常见的日志关键词和对应的解决方向:
-
... ignore disabled protocol这种是正常日志,表示该协议被关闭 -
rtmp: serve stream success表示RTMP推流成功 -
... http: mount http stream表示HTTP-FLV挂载成功 -
... rtc: candidate表示WebRTC候选地址信息 -
... io: writev failed或socket timeout一般是客户端断开导致的,不代表服务端异常
我见过很多新手上来就把日志级别调到verbose,结果输出几千行,反而看不清问题。SRS默认的日志级别已经够用,没有特别问题不用调整。
6.3 镜像下载慢甚至超时:不只是加速源一个原因
前面说了配加速源,但如果你已经配置了加速却还是拉不动,还有一种情况是镜像层太大。SRS镜像本身不大,百来MB在正常网速下刷一下就好了。但如果你用的是老服务器、带宽受限,可以改拉alpine版镜像,体积小很多:
docker pull ossrs/srs:5-alpine
这个tag的存在证明官方对镜像体积什了很多心思。另外,如果公司有内网镜像仓库,把SRS镜像推到内网仓库,生产环境从内网拉,速度秒开。这是大型团队的通行做法,个人玩家不用太折腾。
6.4 推流正常但播放黑屏:检查切片和路由挂载
有时候推流没有报错,播放器也能连上,但画面黑屏。这种问题基本是HLS切片还没生成或者HTTP-FLV的挂载路径不匹配。
HLS有一个切片时间窗口,默认配置下需要等一段时间生成第一个
.m3u8
文件后才能播放。想验证HLS是否可用,直接看
http://服务器IP:8080/live/test.m3u8
文件是否存在,用浏览器打开是否返回正常内容。
HTTP-FLV如果黑屏,检查
http_remux
挂载路径是否和播放地址一致。配置里的
[vhost]/[app]/[stream].flv
,对应的播放地址是
http://IP:8080/live/test.flv
,app是
live
,stream是
test
。如果推流地址改成
rtmp://IP/live2/test
,播放地址也要对应改成
.../live2/test.flv
,路径不匹配SRS是返回404的。
7. 我做过的一些实用扩展与避坑记录
7.1 把SRS接入自己的业务系统,容易漏掉的几个细节
如果你把SRS当成一个模块接入自己的业务系统,有几点经验值得记录。
回调服务必须快速响应。SRS调用
on_publish
回调是同步的,如果业务服务器响应慢,推流端会一直在等待握手。我在一次活动直播中遇到推流端频繁超时,最终查到是回调接口里做了一次耗时2秒的数据库查询。SRS默认回调超时10秒,建议业务端把这个接口控制在200ms以内。否则体验降级,尤其是推流端用了硬编码延迟的OBS,会频繁弹超时。
回调接口的连接复用特别重要。如果你的业务系统是Java/Python/Go这种支持连接池的语言,尽量复用HTTP client,避免每次回调都建立新连接。SRS的推流断开重连很频繁,连接建立开销会拖慢整体响应。
密钥和流名的对应关系要设计好。我建议流名不要用明文业务ID,而是用一个随机字符串,比如
live/x8k2f9
,这个字符串作为推流凭证的一部分,播放端也要带上才能通过鉴权。这种方式让第三方很难猜测有效流地址。
7.2 Docker层面的几个运维习惯
我自己的习惯是把SRS单独放在一个Docker网络中,其他服务通过容器名访问它,而不是通过IP端口。这个在docker compose里特别方便:
networks:
media-net:
driver: bridge
services:
srs:
...
networks:
- media-net
api-server:
...
networks:
- media-net
这样API服务器调用SRS时只需要写
http://srs:1985/api/v1/streams
,Docker会自动解析到SRS容器的IP。网络隔离也让其他的服务不会随意访问到SRS暴露出来的端口。
容器资源限制也值得设置,我见过SRS在异常流量下吃满整个机器CPU的情况。在docker compose中给SRS加上资源配额:
deploy:
resources:
limits:
cpus: "2.0"
memory: 2g
SRS单机处理几百路流时CPU占用其实不高,限制到2核足够日常使用,但能防止某个推送异常码率的流拖垮整台服务器。
7.3 多机扩展思路:什么时候需要从单机走向集群
如果你的单机SRS撑不住了,可能是带宽、CPU或磁盘写入能力的瓶颈。SRS支持集群模式,标准做法是把SRS拆成源站(Origin)和边缘(Edge)。
源站负责接收推流,边缘节点负责接收播放请求,从源站拉流再分发给用户。这样播放压力被分散到各个边缘节点,源站只需要保障推流和边缘之间的带宽即可。Docker部署这种集群的方式,无非是准备多份配置,用不同参数启动多个容器,再在前面加一层负载均衡。SRS的集群功能是内置的,不需要额外装东西。
但对于大多数项目,单机SRS加Docker的部署方式已经足够日常使用。集群是在单机确实顶不住时才需要考虑的问题。
我在生产环境跑了快两年SRS,从Docker部署的第一天起,省下的时间大多花在了更有意思的业务逻辑上。流媒体服务本身不该是项目的负担,选对工具,处理好上面提到的这些细节,剩下的事情就交给SRS自己好好干活了。
更多推荐
所有评论(0)