开头我直接以一个真实经历切入:上个月帮朋友接了个小项目,对方要求在半天内搭一个能支撑几百人同时观看的直播服务,还要能随时回放。我第一反应是上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自己好好干活了。

Logo

邀请您加入社区

更多推荐