Docker部署SRS流媒体服务器实战:从RTMP到WebRTC全指南
1. 为什么把SRS装进Docker,而不是直接源码编译
1.1 SRS到底能干什么,先对齐一下认知
SRS全称是Simple Realtime Server,一个开源的实时视频服务器,光看名字里的Simple就知道它设计初衷是让流媒体服务器不那么劝退。它能做的事一句话概括:接收推流端的音视频数据,经过协议转换和分发,把画面送到各种播放端。支持RTMP推流、HTTP-FLV拉流、HLS切片播放、WebRTC低延迟通话,还有SRT这种抗弱网的传输协议,基本覆盖了直播、监控、视频会议、IoT摄像头这类场景的绝大部分需求。
和商业云直播服务相比,自建SRS最大的价值在两点:一是数据完全在自己手里,推流地址、播放地址、录制文件都是自己掌控;二是成本结构清晰,一台普通云主机就能扛住不小的并发,没有按流量计费那种越用越心虚的账单。很多团队初期选择云厂商的Live服务,等量起来之后发现账单压力,又迁移回自建SRS,我见过不止一次这种情况。
但SRS的问题也明显——它的部署和调优有一定门槛。官方提供了源码编译方式,依赖库一堆,编译时间动不动半小时,而且SRS迭代很快,不同版本之间配置格式有差异,新手照着网上教程操作经常卡在版本对不上。这时候Docker的价值就体现出来了。
1.2 Docker部署和源码编译的真实差距
我最早接触SRS是在一台2核4G的云服务器上,那时候还是SRS 3.0时代,老老实实跟着官方Wiki走源码编译。CentOS 7环境下要先装gcc、g++、make、patch、unzip、python3这些基础工具,然后拉源码、执行./configure,等待编译的过程里CPU直接拉满,服务器上跑着的其他服务都跟着遭殃。编译出错还要根据报错反推缺了什么系统库,装完之后再重来一遍,那个痛苦我现在还记得。
换成Docker之后完全是另一套体验。基础镜像里已经把运行环境和依赖打包好了,拉下来直接跑,镜像本身只有几十MB级别,跟编译一顿操作后占用的磁盘相比反而更轻量。更重要的是容器隔离特性让清理变得极其简单,想升版本或者换配置,删掉容器重建就行,宿主机不会被搞乱。
版本管理方面Docker的优势更明显。SRS从4.0到5.0,配置项变化不小,很多人拿4.0的教程去配5.0的容器,结果启动报错或者配置不生效。Docker镜像通过tag锁定版本,比如说
ossrs/srs:5
,后续升级只需要换个tag重新拉取,回滚也方便,再也不用为了版本问题头疼。
注意:SRS官方镜像地址是
ossrs/srs,Docker Hub直接拉就行。如果服务器在国内,拉取Docker Hub镜像经常超时,建议提前配好镜像加速器,具体怎么配后面会详细说。
2. 环境准备和镜像拉取:正式操作前的关键分叉点
2.1 宿主机选型和基础条件
跑SRS的机器要求不高,但也不能太寒酸。以我个人的实测经验,一台1核1G的云主机能跑起来,但并发拉流超过几十路时CPU会吃紧,最起码也要2核4G起步。操作系统方面Ubuntu 20.04、22.04,CentOS 7、8都可以,核心是能装Docker,系统本身是什么其实无所谓。硬盘建议单独挂一块数据盘给录制文件和历史切片用,如果只是做实时转发,系统盘也就够了。
Docker安装这里有一个新手特别容易踩的坑:直接用系统自带的旧版本Docker。Ubuntu 20.04的apt源里默认带一个docker.io,版本老、更新麻烦,强烈建议用官方脚本装新版:
curl -fsSL https://get.docker.com | bash -s docker
systemctl enable --now docker
docker version
装完之后确认Client和Server都在,尤其要看Server的版本,有些机器之前装过旧版,环境变量PATH指向旧的可执行文件,导致命令操作的是老客户端、连接的却是新守护进程,这种隐藏问题最磨人。
2.2 镜像版本到底选哪个
SRS官方镜像在Docker Hub上的tag分两大类:一类是像
4.0-r0
、
5.0-r2
这种具体版本号,一类是
4
、
5
这种主版本号指向最新release。我的建议是生产环境别追最新,锁定主版本即可,比如用
ossrs/srs:5
。主版本号不会频繁变动,但会持续获得该版本线的bug修复和安全更新,比锁定小版本更实用。
这里要特别提醒:如果你翻到的教程还是让拉
ossrs/srs:3
甚至
ossrs/srs:2
,建议果断放弃。SRS 3.0时代的Docker镜像已经很久不更新,而且4.0之后C++重构了核心模块,性能和稳定性提升非常明显。既然选择Docker就是为了省心,没必要折腾老古董。
2.3 拉取镜像和启动容器的完整命令
配置好镜像加速之后,拉镜像这一步基本不会出幺蛾子。启动一个最基础的SRS容器,官方最简单的方式是:
docker run --rm -p 1935:1935 -p 1985:1985 -p 8080:8080 \
-v /home/srs/config:/usr/local/srs/conf \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
但我实际用下来很不推荐直接这样裸跑。几个问题:
--rm
参数在容器异常退出时直接清理日志,排查问题没现场;没有
--restart
策略,服务器重启后SRS不会自动拉起;配置目录挂载了宿主机路径,可是还没有提前准备好配置文件,SRS会使用容器内默认配置,容易造成"我改了宿主机配置但没生效"的错觉。
我更建议第一步先把容器跑起来,用默认配置验证端口和服务是否正常,确认无误后再深入改配置。具体操作为:
docker run -d --name srs \
-p 1935:1935 \
-p 1985:1985 \
-p 8080:8080 \
--restart always \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
启动后用
docker ps
确认容器状态是Up,再访问
http://服务器IP:8080
,看到SRS欢迎页就算第一步走通了。这一步值得多说一句:先别急着改任何配置,把最简链路跑通,后面出了问题就能区分是环境问题还是配置问题,排查范围小很多。
3. 推拉流链路逐个打通:RTMP、HTTP-FLV、HLS、WebRTC
3.1 RTMP推流:用FFmpeg把视频送进SRS
容器起来了,接下来最激动人心的就是测试推流能不能通。SRS默认配置里已经内置了一个叫
live
的应用,推流地址格式是
rtmp://服务器IP/live/流名称
。
准备一台有FFmpeg的电脑,拿一个MP4文件或者摄像头设备推流,命令长这样:
ffmpeg -re -stream_loop -1 -i /path/to/video.mp4 \
-c copy -f flv rtmp://服务器IP/live/livestream
几秒钟之后命令行没有报错,说明RTMP推流已经建立。这时候打开VLC媒体播放器,选择"打开网络串流",填
rtmp://服务器IP/live/livestream
,如果能流畅播放,恭喜你,最核心的一条链路通了。
这里有一个细节值得注意:
-re
参数指示FFmpeg按视频原始帧率推送,不加速也不丢帧;
-stream_loop -1
是让视频循环推流,方便长时间测试。如果不加
-re
,FFmpeg会把所有帧一股脑推过去,SRS端接收到的数据是不符合真实时间线的,播放端会以极快的速度播完整个视频,初学者很容易被这个现象搞晕。
3.2 HTTP-FLV和HLS播放:浏览器直接看的两种方式
RTMP是当年Flash时代的标准,现在浏览器原生已经不支持了。要让网页端直接看直播,最常用的两种方案是HTTP-FLV和HLS。
HTTP-FLV是SRS默认就支持的,播放地址就是推流地址换个协议前缀:
http://服务器IP:8080/live/livestream.flv
用VLC或者网页里引入
flv.js
就能播放。HTTP-FLV的延迟可以做到1到3秒,比HLS低得多,适合互动直播、在线课堂这类对实时性有要求的场景。但它是长连接流式传输,对于视频平台这类大规模分发场景不够灵活,还得靠HLS。
HLS是苹果推出来的协议,原理是把视频切成一个个几秒长的TS切片,通过m3u8索引文件播放。它的延迟天然比FLV高,通常5到10秒,但好处是兼容性极好,iOS、Android、Web端都能原生播放,不需要额外插件。SRS对HLS的支持默认是关闭的,需要自己开启。修改SRS配置,找到
vhost __defaultVhost__
节点,启用hls模块:
vhost __defaultVhost__ {
hls {
enabled on;
hls_path ./objs/nginx/html;
hls_fragment 5;
hls_window 30;
}
}
改完配置重启容器。之后推送一路RTMP流,几秒钟后访问:
http://服务器IP:8080/live/livestream.m3u8
就能看到HLS切片生成。这里需要说明:
hls_path
配置了切片的存放位置,如果是Docker启动时没有挂载数据卷,切片会写在容器内部,容器删除数据就丢了。生产环境务必把HLS输出目录挂载到宿主机。
3.3 WebRTC低延迟链路:SRS 5的招牌功能
如果说RTMP、HTTP-FLV、HLS是传统的三件套,那WebRTC就是SRS 5主推的新物种。WebRTC能把延迟压到500毫秒以内,而且浏览器原生支持,不需要装任何插件,是视频会议、在线连麦、远程协作这类强互动场景的标配。
SRS启用WebRTC的配置比HLS复杂一些,因为它涉及UDP端口段、Candidate地址配置。我直接贴一份能跑通的精简配置:
rtc_server {
enabled on;
# 设置UDP监听端口范围,注意这里不是单端口
listen 8000;
# 对外通告的IP,必须改成服务器实际公网IP或内网IP
candidate $CANDIDATE;
}
启动容器时要把UDP端口段也映射出来:
docker run -d --name srs \
-p 1935:1935 \
-p 1985:1985 \
-p 8080:8080 \
-p 8000:8000/udp \
-p 8001:8001/udp \
-p 8002:8002/udp \
-e CANDIDATE=你的IP \
--restart always \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
WebRTC测试页面SRS自带,访问
http://服务器IP:8080/players/webrtc.html
即可。这里最大的坑是
candidate
配置,SRS需要把UDP端口和可被客户端访问的IP地址告诉对方,如果机器在NAT后面或者有多个网卡,IP填错就会导致连接建立失败,而且失败现象是"一直在connecting",非常误导人。
4. 端口规划和容器网络:最容易安装时被忽略、出问题最多的地方
4.1 一张表讲清SRS要暴露哪些端口
SRS本身涉及的端口不多,但每个都有自己的位置,搞混了排查问题成本很高。我整理了一张对照表:
| 端口 | 协议 | 用途 | 说明 |
|---|---|---|---|
| 1935 | TCP | RTMP推流/拉流 | 核心端口,必开 |
| 1985 | TCP | HTTP API接口 | 管理接口、回调、统计信息都走这里 |
| 8080 | TCP | HTTP播放和Web管理 | HTTP-FLV、HLS、WebRTC播放器页面都从这里出 |
| 8000-8003 | UDP | WebRTC媒体传输 | 需要按需开启,每个流会占用端口对 |
Docker的
-p
参数格式是
宿主机端口:容器端口
,我建议保持两边一致,否则排查时还要来回换算很麻烦。比如
8080:8080
就是让宿主机的8080直接映射到容器的8080。
4.2 防火墙和云安全组的双重配置
端口映射配好了,容器起来了,但外面还是访问不了,这个问题十有八九出在防火墙或者云安全组。
云服务器的安全组是最容易漏的一层。阿里云、腾讯云这些平台默认只放通22、80、443端口,即便Docker端口映射做对了,安全组不放行,外部流量一样进不来。所以购买云服务器后第一件事,就是到控制台的安全组规则里把1935、1985、8080、8000-8003全部放行。
服务器本身的防火墙也要注意。CentOS 7以上默认用firewalld,Ubuntu用ufw。我自己习惯直接把SRS相关端口加入白名单,避免影响业务:
# 以CentOS firewalld为例
firewall-cmd --permanent --add-port=1935/tcp
firewall-cmd --permanent --add-port=1985/tcp
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=8000-8003/udp
firewall-cmd --reload
排查顺序建议是先从本机
curl http://127.0.0.1:8080
验证SRS服务本身正常,然后用另一台机器
telnet 服务器IP 8080
验证端口通不通,最后才考虑推流测试。这个顺序能帮你把问题快速定位到服务、网络、配置三个层面的某一层。
4.3 用Docker Compose固定环境和网络
docker run
命令在容器少的时候够用,但等SRS要挂载多个数据卷、配置多个环境变量、再旁边跑一个Nginx、一个录制服务的时候,命令变得越来越长,一不小心还容易敲错。这时候换成Docker Compose更合适,配置文件版本化管理,换机器部署也方便。
下面是一个生产可用的
docker-compose.yml
示例:
version: "3.8"
services:
srs:
image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
container_name: srs
restart: always
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
- "8000-8003:8000-8003/udp"
environment:
- CANDIDATE=${SRS_IP}
volumes:
- ./conf/srs.conf:/usr/local/srs/conf/srs.conf
- ./logs:/usr/local/srs/objs/logs
- ./hls:/usr/local/srs/objs/nginx/html
mem_limit: 1g
cpus: "1.0"
用Compose启动只需要
docker-compose up -d
,停止是
docker-compose down
,配置变更时修改yaml然后重新up。这个模式能让你把SRS和周边组件一起编排,后面要加Redis做鉴权缓存、加Filebeat采集日志,都可以往同一个Compose文件里扩展。
5. 配置文件的常用改动:日志、鉴权、转封装和跨域
5.1 日志级别和日志轮转
SRS默认日志输出到控制台,容器方式下会进入docker logs。默认级别是
info
,在排查问题的时候信息量不够,我习惯调到
debug
先看全貌,确认问题后调回
info
。生产环境不建议长期开
debug
,日志增长速度非常快,一两天就能吃掉几个G。
SRS日志配置在
srs.conf
的全局段:
srs_log_tank file;
srs_log_file ./objs/logs/srs.log;
srs_log_level info;
srs_log_rotate 3600;
srs_log_tank file
表示写文件,
srs_log_rotate 3600
是每小时轮转一次日志。给日志单独挂载数据卷之后,配合宿主机的logrotate或者采集工具,日志管理就规整多了。
5.2 HTTP回调鉴权怎么加
流媒体服务直接裸奔肯定不行,尤其推流地址一旦被别人猜到,任何人都能往你的服务器推垃圾流。SRS提供了HTTP回调机制,在推流、播放、断流等事件发生时向业务服务器发HTTP请求,由业务方决定是否允许操作。
配置如下,在vhost节点里增加:
vhost __defaultVhost__ {
http_hooks {
enabled on;
on_publish http://你的业务服务器/api/auth/publish;
on_play http://你的业务服务器/api/auth/play;
on_unpublish http://你的业务服务器/api/auth/unpublish;
}
}
SRS向回调地址POST一个JSON,包含
action
、
client_id
、
ip
、
vhost
、
app
、
stream
这些字段。业务服务器收到后,根据流名称、IP、token等信息判断是否合法,返回
0
表示允许,返回非0表示拒绝。这个机制的最大优点是鉴权逻辑完全在业务侧,SRS本身不存用户数据,灵活度很高。
5.3 CORS跨域问题的处理
很多人把播放器页面和SRS放在不同域名下,结果浏览器控制台报CORS跨域错误。SRS不直接提供跨域配置项,官方推荐的做法是前置一层Nginx处理跨域头,或者让播放器请求API的时候走代理。
我自己的做法是给SRS前面加一个Nginx反代,把
/live/
路径转发到SRS的8080端口,同时在Nginx层统一加跨域头:
location /live/ {
proxy_pass http://127.0.0.1:8080/live/;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
}
这样不仅解决了跨域,也方便统一管理HTTPS证书。毕竟浏览器播放HTTP-FLV和WebRTC,在非local环境下都要求HTTPS,证书配置放在Nginx层比塞进SRS容器里清爽得多。
5.4 SRT等其他协议的启用
除了前面讲的RTMP、HLS、FLV、WebRTC,SRS还支持SRT、GB28181等协议。SRT是当前弱网传输的热门方案,特别适合跨地域的网络传输,官方镜像默认编译了SRT支持,需要在配置里显式开启:
srt_server {
enabled on;
listen 10080;
}
启动容器时映射对应端口即可。对于需要接入海康、大华等安防摄像头的项目,SRS还通过GB28181协议支持国标设备的接入和转推,不过这个配置相对复杂,涉及SIP信令服务,这里不展开,等有空再单独写一篇。
6. 集群扩展和进阶玩法:一台机器不够了怎么办
6.1 Origin + Edge 模式扛住更多拉流并发
单台SRS节点性能再强也有天花板,当拉流并发上涨时,一个常见方案是Origin + Edge集群。Origin节点负责接收推流、处理转码等耗资源的任务,Edge节点负责对外提供拉流服务,可以在前面挂多台Edge横向扩展。Edge和Origin之间通过源站回源协议交互,对播放端来说只是多一次跳转,延迟增加可以忽略。
配置上,Origin节点保持默认行为即可,Edge节点需要在配置里指定源站地址:
vhost __defaultVhost__ {
mode remote;
origin 192.168.1.10:1935;
}
Edge启动后,播放端访问的是Edge节点的端口,Edge实时从Origin拉取流再分发给播放端。这种架构的好处是推流链路和播放链路解耦,推流稳定性不受播放量波动影响。实际部署时Edge可以按需扩容,用前面的Docker Compose配合多台机器,效果很好。
6.2 与CDN配合的使用思路
自建SRS并不排斥CDN。常见做法是让SRS作为源站,CDN节点回源到SRS拉流,再由CDN节点把流分发到全国各地。这种混合架构下,SRS只需要承担一路来自CDN回源的请求,压力很小,而播放端从最近的CDN节点取流,体验远好于直接连源站。
实现方式是在CDN平台配置回源地址为SRS的HTTP-FLV或HLS地址。需要注意SRS源站必须有公网可访问的地址,如果机器在NAT后面,还要做好端口映射或者使用Nginx反代。这个方案的运维复杂度比纯自建高一点,但换来的是用户播放体验的大幅改善,尤其在运营商网络环境下。
6.3 容器资源限制和监控
Docker容器默认不限制资源,一个SRS进程可能吃掉宿主机全部内存。我自己的习惯是给每个容器都设置资源上限,防止某个异常流量把机器拖垮:
deploy:
resources:
limits:
cpus: "1.0"
memory: 1G
监控方面,SRS自带HTTP API接口,可以返回当前连接数、带宽占用、推拉流详情,比如:
curl http://127.0.0.1:1985/api/v1/summaries
这个接口返回的是一个JSON,包含
clients
、
streams
等字段。配合Prometheus等监控工具定时采集,可以在连接数异常飙升时提前收到告警,而不是等用户投诉了才发现问题。
7. 排错实战:我在部署过程遇到的几个典型问题和排查链路
7.1 容器正常启动但VLC播放黑屏
有一次我在新服务器上部署,容器状态正常,API接口能访问,FFmpeg推流也没有报错,但VLC打开RTMP地址就是黑屏,也不缓冲。排查链路是这样的:先用
docker logs
看SRS日志,发现推流事件已经记录,说明推流链路没问题。再看进程状态,
docker stats
显示CPU和内存占用都很低,进一步判断问题可能在播放端到服务器的网络路径上。
用
telnet 服务器IP 1935
测试,发现TCP端口都能通,但RTMP协议层面一直没响应。后来打开云服务商控制台才发现,安全组里虽然放行了1935端口,但放行的源地址
0.0.0.0/0
被之前配置的其他规则优先拦截了。安全组规则的生效顺序是从前往后匹配,一旦前面有拒绝规则,后面的放行就不会生效。调整规则顺序后问题解决。
这个案例给我的收获是:排查网络问题时,先把安全组、防火墙、端口监听三层全部过一遍,不要想当然认为"端口通了服务就正常"。
7.2 WebRTC拉流一直connecting
WebRTC的坑比RTMP多一个数量级。之前配好RTMP之后继续配WebRTC,页面打开后一直处于connecting状态,等了十几秒也连不上。先怀疑UDP端口没映射,检查之后发现8000-8003都映射了。再看日志,发现SRS打印了一段
candidate
信息,实际通告给客户端的IP不是公网IP而是内网IP。
问题根源是容器里的SRS拿到的是容器虚拟网卡的IP,不是宿主机的公网IP。而
candidate $CANDIDATE
这个配置并没有正确读取环境变量,导致SRS认为自己的IP是不对外的内网地址。修正方法是在启动命令里显式传入环境变量,或者在配置里直接写死公网IP。同时还要确认UDP端口8000-8003在安全组里全部放行了,少一个数字都连不上。
7.3 容器重启后IP变化导致转发失效
在Kubernetes或Docker网络环境里,容器重启后IP会变化,这一点在配置Edge回源时非常坑。有一回我配置好Edge节点指向Origin的容器IP,跑了一天后Origin容器重启,IP变了,Edge全部拉流失败。
排查时一度以为Edge配置被覆盖了,后来才发现根因是容器IP不固定。解决办法有几个:最直接的方案是在docker-compose里给Origin和Edge都配置静态IP,属于同一个自定义网络内固定下来;也可以给服务加DNS别名,用服务名互相访问;如果只有一台机器跑多个容器,直接让Edge使用宿主机IP访问Origin的映射端口,也能规避IP变化问题。
这三个问题看起来各不相同,但有一个共同点:SRS本身的核心逻辑没问题,出问题的地方都在网络规划和配置细节。而Docker部署方式能帮你在排查时快速定位问题边界——先确认容器内部服务是否正常,再逐层向外排查网络,思路清晰了,问题就不难解决。
更多推荐
所有评论(0)