做直播服务或者实时音视频应用的开发者,对 SRS 应该不陌生。这个国产开源流媒体服务器,一台普通机器就能扛住不小的并发,支持 RTMP、HTTP-FLV、HLS、WebRTC、SRT 这些主流协议,很多在线教育、电商直播、安防监控、视频会议项目都是在它上面长出来的。不过以前要在生产环境跑起来,得先编译、配依赖、调环境,一套流程下来没几个小时搞不定。用 Docker 部署 SRS 之后,这个门槛被拉得很低——一条命令启动服务,一个配置文件描述业务,想回滚版本也只是一行 docker tag 的事。
这篇文章我不打算写成官方文档的复读机,而是把我从零开始用 Docker 跑通 SRS 实时音视频链路的全过程拆开来讲,包括环境准备、端口规划、推流配置、鉴权回调、常见坑排查。内容覆盖从“先跑起来”到“生产可用”的完整路径,适合正在选型流媒体服务、打算自建直播平台,或者被云厂商带宽费逼到想自建的同学参考。
1. 为什么选 Docker 部署 SRS
1.1 SRS 到底能解决什么问题
实时音视频服务最麻烦的不是媒体协议本身,而是协议太多、链路太长。客户端推流用 RTMP,网页播放想用 HTTP-FLV,苹果生态要 HLS,低延迟通话又得走 WebRTC,不同端不同协议,服务端如果每种协议都自研一套,工作量直接爆炸。
SRS 做的事情就是把这些协议统一收编。它接收 RTMP 推流,内部转封装成 FLV、HLS,也能桥接 WebRTC 的低延迟传输,开发者不需要关心底层错综复杂的协议转换。配合 Docker 之后,整个服务变成一个黑盒镜像,映射几个端口就能对外提供服务,我本地 macOS 和线上 Linux 服务器跑的是同一套镜像,行为完全一致,这是源码编译很难做到的。
1.2 和源码编译部署相比,Docker 好在哪里
我第一次接触 SRS 的时候,官方文档推荐源码编译。在 CentOS 上要装 gcc、make、pcre-devel、openssl-devel,编译一次十分钟起步,中间只要某个依赖版本不对,就要花半小时上网查错误。后来切到 Docker,整体感受是两个词:干净、可回滚。
| 对比项 | Docker 部署 | 源码编译部署 |
|---|---|---|
| 环境依赖 | 无需安装,镜像内置 | 需要 gcc、make、各种 devel 包 |
| 部署耗时 | 分钟级 | 编译至少 30 分钟 |
| 版本回滚 | 切换镜像 tag 即可 | 需要重新编译或备份二进制 |
| 多机扩展 | 镜像一致性强 | 每台机器环境容易漂移 |
| 资源隔离 | 容器级隔离,可限 CPU/内存 | 进程级,依赖宿主机环境 |
我最看重的是可回滚。有一次线上需要升级 SRS 小版本,源码部署的话要先备份原二进制,再重新编译替换,出问题很难迅速退回。Docker 部署只需要把 compose 文件里的镜像 tag 改回去再 up 一次,十几秒就回到旧版本。
1.3 哪些场景适合这套方案
如果你属于下面几类情况,Docker 部署 SRS 是性价比很高的路径:
- 中小团队自建直播服务,不想被云厂商的流量费绑死
- 在线教育、陪练、远程医疗需要低延迟音视频互动
- 安防监控项目需要把摄像头 RTSP 流转成网页可播放的流
- 短视频、UGC 产品要做开播、连麦、录制回放功能
这套方案的优势在资源可控。云厂商的 CDN 按流量计费,高峰期费用吓人,自建 SRS 之后,只要带宽规划合理,一台 4 核 8G 的云主机就能服务几千路直播观看,边际成本低很多。
2. 部署前的准备工作
2.1 硬件与系统要求
SRS 本身对硬件要求不高,纯转发场景 1 核 1G 的机器都能跑,但真要考虑生产使用,需要结合码率和并发量估算。
一个简单的估算方法:视频码率乘以同时观看人数,就是服务器需要承担的下行带宽。假设直播码率是 2Mbps,同时有 100 人观看,下行带宽需要约 200Mbps,换算成字节是 25MB/s 的持续吞吐。这种量级下,CPU 主要消耗在协议转封装上,WebRTC 转播会比纯 RTMP 转发多吃一些 CPU,4 核起步比较稳。
内存方面,SRS 进程本身占用很低,容器内通常不到 100MB,但 HLS 切片、录制文件会写磁盘,需要考虑磁盘 IO 和容量。我实践中发现,720p 视频 2Mbps 码率,一小时录制大约 900MB 磁盘,做录像回放功能时要按这个量级规划磁盘空间。
2.2 端口规划与防火墙放行
SRS 不像普通 Web 服务只开一个端口,它根据功能不同监听多个端口,部署前必须想清楚端口映射关系。
| 端口 | 协议 | 用途 |
|---|---|---|
| 1935 | TCP | RTMP 推流与播放 |
| 1985 | TCP | HTTP API,用于查询流状态、触发回调 |
| 8080 | TCP | HTTP 服务,用于 HTTP-FLV、HLS 播放和管理控制台 |
| 8000 | UDP | WebRTC 媒体传输 |
| 8001 | UDP | WebRTC 媒体传输(多路复用) |
这里面最容易漏的是 8000/8001 的 UDP 端口。很多同学 1935、8080 都映射了,结果 WebRTC 播放一直黑屏,排查半天发现是云服务器安全组没放行 UDP 端口。RTMP 和 HTTP 都走 TCP,云控制台一般默认放行,UDP 经常被忽略。
如果你在本地虚拟机测试,还要注意宿主机防火墙。CentOS 上用 firewalld 的话,需要执行firewall-cmd --add-port=1935/tcp --add-port=8080/tcp --add-port=8000/udp --add-port=8001/udp --permanent并 reload,否则外部设备推流拉流都会被拦。
2.3 Docker 环境安装要点
安装 Docker 这一步,不同系统坑不一样。Linux 上比较省心,用官方脚本安装后启动服务就行。Windows 和 macOS 需要安装 Docker Desktop,这里面有一个高频报错经常把人卡住——Docker Desktop 启动时提示 Virtualization support not detected。
这通常不是 Docker 的问题,而是系统虚拟化没开。Windows 需要确认三件事:BIOS 里开启 Intel VT-x 或 AMD-V,Windows 功能里启用“适用于 Linux 的 Windows 子系统”和“虚拟机平台”,然后执行wsl --set-default-version 2。改完这些重启 Docker Desktop 就能正常启动了。
安装完成后用docker --version和docker compose version验证,两个命令都有输出,环境就算准备好了。
3. 直接用 docker run 快速启动 SRS
3.1 一条命令跑起来
如果想最快看到效果,直接执行下面这条命令:
docker run -d --name srs \ -p 1935:1935 \ -p 1985:1985 \ -p 8080:8080 \ -p 8000:8000/udp \ -p 8001:8001/udp \ registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5.0-r2这里用了阿里云镜像仓库的地址,国内服务器拉取速度快,避免 Docker Hub 超时。镜像 tag 选择5.0-r2,是 SRS 5.0 系列比较稳定的版本,能同时支持 RTMP 和 WebRTC。
启动后执行docker logs -f srs看日志,出现start server successfully就说明 SRS 已经起来了。这时候在浏览器访问http://服务器IP:8080/console/,能看到 SRS 自带的管理控制台,当前流列表、连接数、CPU 占用都有,非常适合初期验证。
注意:这条命令用的是默认配置。默认配置下 WebRTC 的 candidate 会被设置成容器内网 IP,导致局域网外的设备无法建立 WebRTC 连接。本地测试没问题,如果要让外部设备也连得上,需要自定义配置,下一章会详细讲。
3.2 验证服务是否正常
服务起来之后,先别急着推流,用 API 接口快速验证一下:
curl http://服务器IP:1985/api/v1/versions正常会返回版本信息 JSON。再查一下流列表:
curl http://服务器IP:1985/api/v1/streams此时应该返回空数组。等推流上去之后,再请求这个接口就能看到流的 meta 信息,包括推流地址、视频分辨率、码率、在线观看人数。我在做监控脚本时就靠这个接口定期拉取流状态,实现自动报警。
3.3 默认配置有哪些限制
默认配置适合体验,但直接上生产肯定不行,主要限制有三个。
默认不开启 HTTP-FLV 转封装,网页端无法通过 FLV 地址拉流。默认不开启 HLS,苹果设备无法直接播放。WebRTC 的 candidate 指向容器内部 IP,外部设备连不上。
这三个问题的根源都是配置太简略,需要自己挂载一份完整配置。下一章讲 docker compose 的时候就一并解决。
4. 用 docker compose 搭建生产级 SRS
4.1 编写 compose 文件
我建议任何稍微正式的部署都用 docker compose,原因很简单:端口映射、数据卷挂载、环境变量、重启策略都写在文件里,换机器部署不用回忆当初敲了什么命令。
下面是我实际使用的 compose 文件:
version: "3.9" services: srs: image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5.0-r2 container_name: srs restart: always ports: - "1935:1935" - "1985:1985" - "8080:8080" - "8000:8000/udp" - "8001:8001/udp" environment: - CANDIDATE=127.0.0.1 volumes: - ./conf/srs.conf:/usr/local/srs/conf/srs.conf - ./data:/usr/local/srs/objs/nginx/html logging: driver: json-file options: max-size: "50m" max-file: "3"先创建项目目录,把上面的内容保存为docker-compose.yml,然后根据需求编写conf/srs.conf,最后执行docker compose up -d。
这里有几个细节值得说明。restart: always保证机器重启后 SRS 自动拉起,避免人工干预。logging配置防止容器日志无限增长撑爆磁盘。./data挂载目录是 HLS 切片和录制文件的输出位置,必须放到宿主机持久化,否则容器重建历史录像全丢。
4.2 配置文件的重点解析
SRS 的配置格式类似 nginx,注释用#,层级通过花括号表示。下面是一份支持 RTMP 推流、HTTP-FLV/HLS 播放、WebRTC 低延迟播放、HTTP API 查询的完整配置:
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__ { http_remux { enabled on; mount [vhost]/[app]/[stream].flv; } hls { enabled on; } }配置里的关键点:
listen 1935是 RTMP 入口,max_connections决定最大连接数,我自己线上压过 500 并发,8G 内存的机器非常稳。http_api开启后,1985 端口提供 API,后面的鉴权回调也依赖它。http_remux是重点,开启后 RTMP 流自动转成 HTTP-FLV,网页播放器可以拉http://ip:8080/live/livestream.flv播放。rtc_server配了 candidate,WebRTC 能否连通的关键就在这个字段。
4.3 candidate 参数为什么必须设置
WebRTC 不同于 RTMP 这种服务器中转模式,它要协商出 P2P 或媒体服务器的传输地址。SRS 在容器里运行时,默认探测到的 IP 是容器的内网地址,外部浏览器拿到这个地址根本连不上。
我在 compose 里通过环境变量传入宿主机的 IP:
environment: - CANDIDATE=你的服务器公网IP或内网IP配置文件中写candidate ${CANDIDATE},SRS 启动时会自动读取环境变量并替换。如果只在局域网内测试,填宿主机内网 IP 就行,比如192.168.1.100。如果要公网访问,必须填公网 IP,同时确保 8000/8001 的 UDP 端口在防火墙和安全组里放行。
有个调试技巧:如果 WebRTC 播放失败,先看一下 SRS 日志里 candidate 实际用的是什么 IP,和浏览器协商的地址是否一致。大多数 WebRTC 连不上的问题,都是 candidate 配置错误。
5. 推流与播放实测:打通第一个直播链路
5.1 用 OBS 推流到 SRS
服务端配好了,接下来做一次完整的推流播放测试。我推荐用 OBS,免费且跨平台。
打开 OBS 后进入“设置 -> 直播”,服务选择“自定义”,服务器填:
rtmp://服务器IP:1935/live串流密钥填:
livestream点“开始直播”,如果服务端配置没问题,几秒内 OBS 状态栏会显示“直播中”。这时候回到 SRS 管理控制台,就能在流列表里看到一条live/livestream的流,包括分辨率、码率、帧率信息。
注意:串流密钥不要包含特殊符号,SRS 默认配置下密钥里带
?或/会有歧义。我习惯把app固定为live,stream用业务 ID,比如直播间号、设备序列号,这样后面做流转发和录制都好区分。
5.2 用 ffmpeg 验证推流
OBS 适合人工测试,如果要脚本化验证,推荐用 ffmpeg。把一段视频循环推到 SRS:
ffmpeg -re -stream_loop -1 -i test.mp4 \ -c copy -f flv rtmp://服务器IP:1935/live/livestream-re表示按视频原始帧率读取,模拟实时推流而不是瞬间推完。-stream_loop -1让视频无限循环。-c copy直接复制编码数据,不重新编码,CPU 占用极低。跑起来后再拉流验证:
ffplay rtmp://服务器IP:1935/live/livestream能看到画面就说明整个 RTMP 链路已经通了。
5.3 各种播放协议地址对照
SRS 最方便的地方是一条推流,多种协议播放。推流地址是rtmp://ip:1935/live/livestream,那么对应的播放地址如下:
| 协议 | 播放地址 | 适用场景 |
|---|---|---|
| RTMP | rtmp://ip:1935/live/livestream | 传统播放器、低延迟要求低 |
| HTTP-FLV | http://ip:8080/live/livestream.flv | 浏览器播放、PC 直播 |
| HLS | http://ip:8080/live/livestream.m3u8 | 苹果设备、点播兼容 |
| WebRTC | webrtc://ip:8080/live/livestream | 低延迟互动、连麦 |
延迟层面,RTMP 和 HTTP-FLV 通常 1 到 3 秒,HLS 会因为切片缓冲延迟到 5 到 10 秒,WebRTC 可以做到 500 毫秒以内。做在线答题、连麦互动必须上 WebRTC,纯直播观看用 HTTP-FLV 就够了,没必要增加 WebRTC 的复杂度。
6. 后端鉴权与 HTTP 回调:把控制权握在自己手里
6.1 为什么推流接口不能裸奔
服务跑起来之后,最紧急的事情是加鉴权。SRS 默认情况下只要知道你的 IP 和端口,任何人往你的 1935 端口推流,你的服务器就得转发、存储,相当于被人白嫖了带宽和磁盘。更危险的是,你正在直播的内容可能被别人用你的服务器转播到其他平台。
有两种思路做鉴权:内置 token 和 HTTP 回调校验。内置 token 配置简单,但 token 拼在 URL 里容易在日志中泄露。HTTP 回调方式更灵活,由你自己的后端服务决定是否允许推流和播放,适合已有用户体系的业务。
6.2 用 HTTP 回调实现对推流和播放的鉴权
SRS 在推流开始、推流结束、播放开始、播放结束这几个节点会向配置好的回调地址发 HTTP 请求。我在 vhost 配置里加上:
vhost __defaultVhost__ { http_hooks { enabled on; on_publish http://宿主机IP:7000/srs/publish; on_unpublish http://宿主机IP:7000/srs/unpublish; on_play http://宿主机IP:7000/srs/play; on_stop http://宿主机IP:7000/srs/stop; } }回调 JSON 里会带上app、stream、param,param 就是 URL 里的查询参数。比如推流地址写成:
rtmp://ip:1935/live/livestream?token=abc123后端收到 on_publish 回调后,解析 token 字段,判断是否合法。返回0表示允许,返回非 0 值 SRS 就会拒绝推流。
注意一个容器网络细节:SRS 容器内的 localhost 和宿主机不是同一个,回调地址不能写http://localhost:7000。在 Linux 上可以用http://172.17.0.1:7000,这是 Docker 默认网桥的宿主机地址。如果用的是 Docker Desktop,直接写http://host.docker.internal:7000更方便。
6.3 录制与 HLS 回看的配置技巧
很多直播业务需要自动录制,SRS 支持在推流时直接存成 FLV 文件。vhost 里加一段:
vhost __defaultVhost__ { dvr { enabled on; dvr_path ./data/[app]/[stream].[timestamp].flv; dvr_plan session; } }dvr_plan session表示每个推流会话存一个文件,推流断开文件就封存。dvr_path支持按变量自动分目录,我用[app]和[stream]区分不同直播间。
录制文件默认存在容器内的工作目录,但我在 compose 里挂载了./data到宿主机,所以文件会直接写在宿主机上,方便后续转存对象存储或做点播系统。这里要提醒一句,录制文件增长很快,一定要配合定时清理策略,比如用 crontab 删除 7 天前的文件,否则再大的磁盘也扛不住。
7. 常见问题与排查技巧实录
7.1 Docker 启动失败与环境问题
SRS 容器起不来,先看日志:docker logs srs。最常见是端口被占用,报错信息里会明确说 listen 端口失败。用netstat -tlnp | grep 1935查一下占用进程,改掉冲突的端口映射就行。
Windows 上 Docker Desktop 特别容易遇到Virtualization support not detected的报错,这个我在前面说过,本质是系统的硬件虚拟化或 WSL2 没开。还有一个容易被忽略的点:改完 BIOS 虚拟化设置后,如果 Windows 功能里的“虚拟机平台”没勾选,Docker Desktop 依然起不来。
7.2 WebRTC 播放黑屏或卡在连接中
WebRTC 连不上是最让人头疼的,因为表面现象只是黑屏,日志信息还少。按这个顺序排查:
先确认 8000/8001 UDP 端口在防火墙和安全组放行,用nc -u -z或 telnet 测试 UDP 端口连通性。然后看 SRS 日志里 candidate 的实际值,docker logs srs | grep candidate。最后用浏览器控制台看 WebRTC 的 ICE candidate 信息,如果显示的 IP 和服务器公网 IP 不一致,说明 candidate 配置有问题。
我踩过一次很隐蔽的坑:云服务器有内网 IP 和公网 IP 两张网卡,填公网 IP 后发现服务器自身无法通过 WebRTC 播放,是因为公网 IP 的 UDP 包被安全组拦了。最后发现安全组只放行了 TCP 端口,UDP 没放。这个问题排查了我一个下午。
7.3 推流失败或频繁断开
OBS 推流几秒后断开,先看是不是 token 校验失败。如果没用 token,关闭鉴权测试一次。排查网络层面,尝试从服务器本机推流:ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost:1935/live/test,本机能推通则说明是防火墙或云安全组的问题。
还有一种情况是推流地址写错了。RTMP 推流地址的结构是rtmp://ip:port/app/stream,app和stream不能随意带路径分隔。如果配了 HTTP 回调,要确认回调地址返回了0,返回非 0 会被 SRS 判定为拒绝推流。
7.4 延迟和画质的调优方向
如果你对延迟有要求,优先用 WebRTC 或 HTTP-FLV,不要用 HLS。HLS 的切片时长直接决定延迟下限,SRS 默认是 10 秒一个切片,浏览器播放还要缓冲几个切片,延迟很容易到 30 秒。可以把 hls 配置里的切片时长调小:
hls { enabled on; hls_fragment 2; hls_window 10; }画质方面,SRS 本身不做转码,画质取决于推流端的码率设置。OBS 里建议视频码率设 3000 到 6000Kbps,关键帧间隔 2 秒,编码器用硬件编码降低 CPU 占用。
7.5 安全加固的几条建议
最后说安全。自建流媒体服务暴露在公网,等于给所有人开了一扇门,不加防护迟早被刷流量甚至植入恶意内容。
端口尽量只放行业务需要的。如果管理控制台不需要公网访问,8080 端口在安全组里只对办公网 IP 开放。升级域名尽量走 HTTPS/WSS,SRS 5.0 支持在配置里挂证书,避免播放地址被运营商劫持。对推流接口做 token 校验,对播放接口做防盗链校验,这些都是上线前必须完成的动作。
容器本身也要限制资源,compose 里加上:
deploy: resources: limits: cpus: "2.0" memory: 2G防止某个直播间流量异常时把整台机器拖垮。日志大小限制我前面已经写在 logging 配置里,生产环境千万别省略这个,不然/var/lib/docker被日志撑爆的教训会很难忘。
这套 Docker 加 SRS 的组合,我在线上稳定跑了大半年,最深刻的体会是:流媒体服务并没有想象中那么遥不可及,关键是选对工具、把基础配置吃透。如果你也准备自建实时音视频平台,照着这篇文章的路径走一遍,先把 RTMP 推流和 HTTP-FLV 播放跑通,再一步步加 WebRTC、鉴权、录制这些高级功能,整个过程会比预想中顺利很多。最后再分享一个小技巧,SRS 的配置文件和 nginx 很像,改配置之前一定先备份,上线前用docker compose config检查一下 compose 文件的语法,很多低级错误都能提前发现。