项目标题: "Docker 部署 SRS:轻松搭建实时音视频流媒体平台" 项目正文: "" 关键词: "" 摘要描述: ""
你大概率遇到过这种场景:流媒体服务器好不容易装完了,给前端同事发了个播放地址,结果对方那边要么黑屏,要么一直转圈。排查半天发现不是配置问题,而是最初部署方案就选得别扭。我自己在做过几次自建流媒体平台之后,现在的结论非常明确:Docker 部署 SRS,是目前把实时音视频流媒体平台跑起来最省心的一条路,没有之一。
SRS 本身是一个开源的实时流媒体服务器,全称是 Simple Realtime Server,它在流媒体链路里扮演的角色可以理解为一个“协议中转站”:推流端把 RTMP、SRT、WebRTC 等协议的数据送进来,SRS 把流接收下来,再转成 HTTP-FLV、HLS、WebRTC 等不同协议分发给各平台的播放端。你可以用它做直播、做监控大屏、做在线教学,也可以作为内部视频分发的中转节点。无论你是有一定基础的后端开发,还是刚接触流媒体的小白,用 Docker 跑起来 SRS 之后,再逐步对照协议知识去加深理解,是一个性价比很高的路径。
1. 为什么最后我选了 SRS + Docker,而不是手动编译或 nginx-rtmp
很多人刚开始做流媒体时,第一反应是去源码编译 SRS,或者装个 nginx 加 rtmp 模块。这两条路我都走过,各有各的坑。先说结论:如果你只是在自己电脑或一台云服务器上做验证,Docker 镜像能帮你省掉至少一上午的折腾时间。等你把整套链路跑通,再回头决定要不要手动编译也不迟。
1.1 SRS 在流媒体链路里到底承担什么角色
要理解为什么选 SRS,先要弄清楚它在一条直播链路里的位置。最常见的直播场景是这样的:主播端用 OBS 或手机推流,把视频流推到服务器;服务器把流接收下来,做协议转换;播放端用浏览器、VLC 或小程序拉流观看。
这个过程中,服务器端软件要解决三个核心问题:第一是稳定接收推流,不能因为网络抖动就断开;第二是协议转换,因为推流端常用 RTMP 或 SRT,而浏览器播放端通常要 HTTP-FLV 或 HLS,移动端 App 可能还要 WebRTC;第三是分发效率,当几十个、几百个用户同时观看时,服务器不能把每一路流都复制一份往外部带宽上怼。
SRS 的核心能力正好都覆盖了这三块。它原生支持 RTMP、SRT、HTTP-FLV、HLS、WebRTC 等多种协议,而且内置了会话管理、GOP 缓存、集群转发等能力。更关键的是,SRS 的配置复杂度在同类产品里算低的,默认配置下就能直接跑通一条 RTMP 推流、HTTP-FLV 播放的链路。这一点对新手特别友好。
1.2 手动编译的真相:不是性能问题,是时间成本
SRS 官方确实提供了非常详细的编译文档,源码编译本身也不复杂,脚本执行完等十几分钟就能出二进制。如果你是用一台干净的 Ubuntu 或 CentOS 服务器,编译过程中最常翻车的地方在于系统依赖不满足,比如缺少 gcc、g++、make、patch、unzip 等基础工具,或者 OpenSSL 版本不匹配。
我踩过的最典型的坑是 CentOS 7 上编译 SRS 4.0,系统自带的 OpenSSL 版本偏低,configure 阶段就报错。当时我以为是路数不对,后来才发现需要先装高版本 OpenSSL,然后把 PKG_CONFIG_PATH 指过去。这些操作对老手来说不算什么,但对一个只想赶紧把流媒体环境跑起来的人来说,非常劝退。而且编译完成并不等于结束,SRS 编译产物默认会带上 FFmpeg 依赖和自带工具链,目录结构比较复杂,新手去理解哪些文件是配置、哪些是日志、哪些可以删,又要花不少时间。
Docker 镜像解决的就是这部分“环境问题”。镜像里已经把 SRS 编译好、依赖装好、目录结构固定好,你只需要关心端口映射和配置挂载。线上环境真出了问题,也可以直接删掉容器重新拉起,比在一台服务器上反复编译清理要干净得多。
1.3 和 nginx-rtmp 对比,什么时候才需要换
nginx 加 rtmp 模块的方案也很多人用,它确实简单,配一个 nginx.conf 就能接收 RTMP 推流。但你一旦需要在同一个服务端同时输出 HLS 和 HTTP-FLV,nginx 的配置就会变得繁琐。更关键的是,WebRTC 低延迟播放是 nginx-rtmp 模块本身做不了的,你需要在旁边再搭一个 WebRTC 网关,整体架构复杂度立刻上来了。
我用一张表简单对比一下三个方案:
| 对比项 | SRS(Docker) | SRS(源码编译) | nginx-rtmp |
|---|---|---|---|
| 首次部署耗时 | 5 分钟内 | 30 分钟以上 | 15 分钟左右 |
| 协议覆盖 | RTMP/FLV/HLS/SRT/WebRTC | 同左 | 主要以 RTMP 为主 |
| 配置复杂度 | 低,专注流媒体 | 中等 | 中等,但扩展协议麻烦 |
| 升级回滚 | 拉镜像即可 | 重新编译 | 重新编译模块 |
| CPU 开销 | 略低于源码运行 | 基准 | 基准 |
如果你只是做一个内部小范围的 RTMP 拉流验证,nginx-rtmp 还能应付。但只要你的场景涉及浏览器直接播放、小程序播放、低延迟互动,SRS 就是更合适的选择。而用 Docker 部署 SRS,等于把门槛又降低了一截。
2. 镜像、端口与运行环境:动手前先理清这三件事
部署之前不要急着执行 docker run,先把镜像选型和端口规划搞清楚。这三件事没理清,后面配置挂载和网络排障的时候很容易一头雾水。
2.1 官方镜像的 Tag 选择:别闭眼拉 latest
SRS 在 Docker Hub 上有两个常用的镜像仓库,老的是ossrs/srs,新的官方仓库是srsglobal/srs。我建议直接使用srsglobal/srs这个仓库,因为它跟 GitHub Releases 同步更及时。
Tag 选择上,日常使用首选固定大版本号,比如srsglobal/srs:4或srsglobal/srs:5,而不是latest。SRS 4 是目前生产环境最稳的一个大版本,配置文档和社区资料最多,搜问题容易搜到答案。SRS 5 增加了更多 WebRTC 相关的特性和性能优化,如果你的主要场景是低延迟 WebRTC 互动,可以考虑 5。选择固定版本之后,镜像更新逻辑也更清晰:先在测试环境拉新版本跑一遍,确认没问题再切生产,而不是今天 latest 明天 latest,某天配置格式变了都不知道。
另外顺便说一句,SRS 这个项目本身是开源的流媒体服务器,社区活跃度一直不错,协议支持和 Bug 修复都比较及时。这也是我敢把它作为自建流媒体基础设施的原因。
2.2 端口与协议对照:每暴露一个端口都要知道为什么
SRS 默认涉及四个主要端口,每个端口对应一类能力:
| 端口 | 协议 | 作用 |
|---|---|---|
| 1935 | TCP | RTMP 推流和拉流 |
| 1985 | TCP | HTTP API 和后台管理接口 |
| 8080 | TCP | HTTP-FLV/HLS 等 HTTP 流服务 |
| 8000 | UDP | WebRTC 推流和播放 |
很多新手把容器启动起来之后,只知道 1935 是推流的,剩下几个端口全映射出去了,结果安全组里开了一大堆,实际用到的没几个。这里我建议这样理解:
- 1935 是必须暴露的,因为最常见的推流方式是 RTMP,OBS、FFmpeg、部分摄像头都走这个端口。
- 1985 是管理接口,主要用来查流状态、调用 API,生产环境一般不需要对公网开放,甚至可以只绑定在内网。
- 8080 是 HTTP 流服务端口,浏览器播放 HTTP-FLV 和 HLS 都靠它。
- 8000 UDP 只有在用到 WebRTC 播放或推流时才需要暴露,而且 UDP 端口的安全组配置经常被人漏掉,后面我会单独说。
理清端口之后,你在 docker run 或者 docker-compose 里做端口映射时,就能按需开放,而不是盲目-p 1935:1935 -p 1985:1985 -p 8080:8080一把梭。
3. 三分钟跑通第一路直播流:从容器启动到推流播放
这个部分我直接给完整命令。你不需要理解所有细节,先把链路跑通,有了直观感受之后,再看后面配置部分会容易很多。
3.1 启动一个最简容器
先执行这条命令:
docker run -d --name srs -p 1935:1935 -p 1985:1985 -p 8080:8080 srsglobal/srs:4镜像下载完成后,容器会在默认配置下运行。SRS 官方镜像自带一个基础配置,开启了大三件:RTMP 接收、HTTP-FLV 播放、HLS 切片。启动之后,通过 API 接口快速验证一下服务是否正常:
curl http://localhost:1985/api/v1/versions如果返回的 JSON 里包含"major": 4之类的版本信息,说明 SRS 已经在正常运行了。这个 API 也是后面排查问题时最常用的探活接口。
这里要特别提醒一点:一定要用-d放到后台运行,别直接前台挂着,否则终端一关服务就停了。如果容器启动失败,用docker logs srs看日志,大部分问题都能在日志里找到明确报错。
3.2 推流端:FFmpeg 和 OBS 两种最常用方式
当 SRS 容器跑起来之后,第一步是验证推流。
如果本地有一个视频文件,最简单的推流命令是:
ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost/live/livestream这条命令里的-re参数值得多说一句:它让 FFmpeg 按照视频原始帧率来读取文件,相当于模拟真实直播的实时节奏。如果不加这个参数,FFmpeg 会以最快速度把文件推完,几秒钟就结束了,你根本来不及测试播放端。
如果你想推摄像头或采集卡画面,命令稍微改一下:
ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -preset veryfast -tune zerolatency -f flv rtmp://localhost/live/livestream麦克风采集也类似,只是要加-f alsa之类的音频输入源。实战中,我建议新手先用视频文件推流,因为文件推流不依赖硬件设备,出错概率最低,最适合验证链路。
如果你更习惯图形化操作,用 OBS 推流也很简单:设置里选“自定义”,服务填rtmp://你的服务器IP/live,串流密钥填livestream,然后开始推流。这里应用的底层协议还是 RTMP,跟 FFmpeg 走的是同一条链路。
3.3 播放端:HTTP-FLV、HLS、WebRTC 三种地址怎么拼
推流端一旦开始推送,播放端就可以拉流了。SRS 默认配置下,同一路流会同时输出三种协议,地址拼接逻辑如下:
| 播放方式 | 地址格式 |
|---|---|
| HTTP-FLV | http://服务器IP:8080/live/livestream.flv |
| HLS | http://服务器IP:8080/live/livestream.m3u8 |
| WebRTC | webrtc://服务器IP:8080/live/livestream |
用 VLC 播放最方便:打开 VLC,按 Ctrl+N,输入 HTTP-FLV 或 HLS 地址,就能直接看到画面。HTTP-FLV 的延迟比 HLS 低很多,局域网环境下一般能控制在 1 到 3 秒;HLS 因为有切片和缓冲,延迟通常在 5 到 10 秒以上。
WebRTC 地址在普通 VLC 里是播不了的,需要浏览器配合 flv.js 或 WebRTC 播放器才能测。如果你暂时不想折腾前端播放器,先用 HTTP-FLV 验证通链路就够了。这一步跑通,说明你已经完成了一次完整的“推流-服务端分发-播放”闭环。
4. 验证服务与排查问题:别只会看“容器在运行”
很多人在部署完流媒体服务之后,判断正常的标准就是“容器 Status 显示 Up”,这其实远远不够。流媒体服务是否真的在工作,要看系统里有没有真实流在跑、播放端能不能正常拉到流、切片文件有没有在生成。
4.1 用 API 和 ffprobe 确认流真的推上来了
容器运行正常,但推流端可能根本没连上。这时候最好的验证方式是通过 SRS 的 HTTP API 查询当前流列表:
curl http://localhost:1985/api/v1/streams/如果返回的 JSON 里能看到类似下面这样的内容,说明流已经成功推上来了:
{ "streams": [ { "app": "live", "name": "livestream", "url": "rtmp://localhost/live/livestream" } ] }如果列表是空的,说明推流端根本没连上 SRS,问题大概率出在推流地址、防火墙或网络链路上。
另一种更直接的验证方式是使用 ffprobe,它是 FFmpeg 家族里的探测工具,可以直接拉流检查元数据:
ffprobe -v error -show_entries stream=codec_name,width,height -f flv http://localhost:8080/live/livestream.flv命令能正常输出视频编码信息和分辨率,说明 HTTP-FLV 分发链路没问题。如果卡住不动或报错,说明 8080 端口的 HTTP 服务或流在服务端没有正常关联。
4.2 日志里的高频故障怎么快速定位
SRS 的日志是排查问题最重要的依据。当容器跑在后台时,输入:
docker logs -f srs就可以实时查看 SRS 日志。我建议刚部署完的阶段不要关闭这个窗口,推流、播放的时候盯着日志看,能非常直观地看到连接建立、流发布、流播放的完整过程。
常见的两个高频故障,我都会在日志里看到明确特征。
第一个是端口被占用,典型日志是:
[ERROR] listen :1935 failed, port is used这通常说明宿主机上已经有一个进程占用了 1935 端口,比如之前残留的 nginx 或另一个 SRS 实例。处理方式很简单:先查占用端口的进程,再决定是停掉旧进程还是换端口映射。用netstat -tlnp | grep 1935就能定位。
第二个是推流端频繁断开,日志里会看到大量连接超时或断流记录。这种情况优先检查网络,尤其是跨公网推流时,RTMP 基于 TCP,对网络抖动比较敏感。如果推流端和服务器都在国内不同地域,延迟高、丢包多,就会出现“推上去了,过几秒又掉”的情况。
4.3 检查 HLS 切片是否正常生成
如果你计划用 HLS 播放,还需要确认切片文件真的在生成。SRS 4 默认配置下,HLS 切片会写到容器内的/usr/local/srs/objs/nginx/html目录。你可以进入容器查看:
docker exec -it srs ls /usr/local/srs/objs/nginx/html/live/正常情况下,这个目录里会看到livestream.m3u8和一堆.ts切片文件。.m3u8是播放索引文件,.ts是实际的视频切片。如果推流已经开始、这个目录却是空的,说明 HLS 配置没生效或写入路径不对。
如果后续做了配置挂载,这个目录通常会被挂载到宿主机上。检查挂载目录的文件生成情况,是判断 HLS 是否正常的最直接手段。
5. 从临时容器升级到可维护部署:docker-compose 与配置挂载
跑通第一路流之后,你大概率会面临一个尴尬情况:默认配置能跑,但你想调整 HLS 切片长度、开启鉴权、修改日志级别,发现根本无从下手。因为容器里的配置文件和宿主机是隔离的,每次修改都要进入容器操作,容器一删就全没了。
5.1 为什么跑通后要第一时间切到配置化部署
我见过不少人跑通一次之后,就在一台云服务器上持续用默认容器跑了很多天。直到某天需要重启服务器,Docker 容器因为不是restart: always策略没有自动拉起,整套服务直接消失。这种经历一次就够了。
更典型的场景是:你要改一个流媒体参数,比如把 GOP 缓存关掉、调整 HLS 分片时长,如果只用 docker run 起的容器,你需要docker exec进容器去修改/usr/local/srs/conf/srs.conf,改完还得重启容器。而容器每次删掉重建,内部文件又恢复原样,等于白改。正确做法是把配置文件通过 volume 挂载到宿主机上,让外部可以持久化和版本管理。
配置化部署之后,你的配置文件可以提交到 Git,每次变更可追溯;宿主机出了问题,拉一个新容器挂载同一份配置就能快速恢复。这才是生产环境该有的状态。
5.2 一份直接能用的 docker-compose.yml 和 srs.conf
我先给一份最小但完整的 docker-compose 配置。在宿主机建一个目录作为部署根目录,比如~/srs-deploy,然后在里面创建docker-compose.yml:
version: "3.8" services: srs: image: srsglobal/srs:4 container_name: srs restart: always ports: - "1935:1935" - "1985:1985" - "8080:8080" - "8000:8000/udp" volumes: - ./conf/srs.conf:/usr/local/srs/conf/srs.conf - ./logs:/usr/local/srs/objs/logs同时创建配置文件目录conf,并在其中新建srs.conf:
listen 1935; max_connections 1000; daemon off; srs_log_tank console; http_api { enabled on; listen 1985; } http_remux { enabled on; listen 8080; } vhost __defaultVhost__ { hls { enabled on; hls_fragment 6; hls_window 60; } }这份配置里我刻意强调了几个点在启动时容易出问题的地方。daemon off让 SRS 在前台运行,这样 Docker 容器不会因为守护进程退出而被杀掉;srs_log_tank console让日志输出到标准输出,这样docker logs才能看到。这两个配置是容器场景下的必备项,缺一个都会让排查问题变成地狱模式。
启动方式也很简单:
cd ~/srs-deploy docker compose up -d然后还是用前面的 API 验证方式检查服务状态。如果一切正常,你会看到日志里开始输出rtmp server listen on 1935等字样。
5.3 改配置后的几个注意点
配置切到宿主机之后,改配置变成一件平常事,但有三个注意点值得说清楚。
第一,SRS 的配置文件语法比较敏感,每个配置项后面必须有分号,花括号的位置也有限制。改完之后如果容器一直启动失败,第一反应应该是用docker logs srs看输出,配置语法错误通常会在启动日志里明确报出。
第二,挂载配置文件时要注意容器内路径必须正确。SRS 官方镜像的工作目录是/usr/local/srs,默认配置路径是/usr/local/srs/conf/srs.conf,你在 volume 里挂载时,冒号右半边必须严格写成这个路径。挂错路径的情况下,容器可能还会用镜像内置的默认配置启动,造成“我改了配置但没生效”的错觉。
第三,修改 HLS 输出路径时,要确保容器内对应目录有写权限。挂载目录如果权限不对,SRS 启动时不会直接报错,但切片文件写不进去,播放端拉流会一直转圈。我习惯在挂载之后用docker exec -it srs ls检查一下目标目录能否正常访问。
6. 生产环境不能忽略的三件事:鉴权、安全组和低延迟参数
跑通了、配置也挂载了,接下来就是生产环境真正考验人的地方。公网环境下跑一个没有任何鉴权的流媒体服务,和把你的服务器裸奔在互联网上没多大区别。任何人都可以往你服务器上推流,不仅消耗带宽,还可能产生内容风险。
6.1 推流鉴权:别让公共服务器变成别人的直播源
SRS 提供 push 鉴权机制,最常用的方式是 HTTP 回调鉴权。原理是:当有推流端连上来时,SRS 会把流信息通过 HTTP 请求发送到你指定的鉴权服务器,鉴权服务器返回允许或拒绝,SRS 再决定是否接受这路推流。
在srs.conf的 vhost 里加入类似这样的配置:
vhost __defaultVhost__ { http_hooks { enabled on; on_publish http://your-auth-server/api/v1/srs/auth; } }具体回调参数格式 SRS 官方文档写得很清楚,核心就是对比推流 URL 里的参数和你的业务系统校验结果。如果你的场景暂时没有独立鉴权服务,也可以在推流地址里带一个固定密钥,在回调接口里做字符串比对,实现成本很低。
我建议即便在测试环境,也至少把 1985 这个管理端口从公网屏蔽掉。因为 SRS 的 HTTP API 默认没有鉴权,外部任何人访问 1985 端口都能通过/api/v1/streams/看到你服务器上正在直播的流列表,这显然不是想暴露的信息。
6.2 端口暴露范围和安全组规划
Docker 端口映射不等于安全组规则。很多云服务器上,Docker 的-p 1935:1935只负责把容器端口映射到宿主机,能不能从公网访问,还要看云服务商的安全组和系统防火墙。
生产环境我的建议是分层控制:
- 1935 端口只对推流端 IP 或网段开放,如果可以,限制到已知的推流来源。
- 8080 端口可以对公网开放,因为播放端来源不可控。
- 1985 端口只绑定内网或本机,禁止公网访问。
- 8000 UDP 端口按需开放,不要默认打开。
另一个容易被忽略的细节是 Docker 本身的端口绑定地址。如果你在 docker run 里写的是-p 127.0.0.1:1985:1985,那么只有宿主机本机可以访问 1985 端口,公网一律连不上。这有时候是好事,有时候也会让你误判服务状态。
6.3 低延迟和稳定性的关键参数
如果你追求比较低的直播延迟,有几个参数可以调。SRS 默认配置更多是兼顾兼容性,真要跑低延迟场景,需要在配置里做调整。
第一个是 HLS 切片时长hls_fragment,默认是 6 秒。切片时间越长,播放端加载索引后需要缓冲的数据越多,延迟自然越高。但切得太短,又会增加服务器切片写入压力和播放端请求频率。一般 2 到 4 秒是比较平衡的选择。
第二个是gop_cache,默认是开启的。这个参数的意思是缓存当前 GOP(关键帧间隔)的数据,新播放端接入时可以快速从关键帧开始播放,实现“秒开”。但代价是延迟会增加一点。低延迟场景下,可以考虑关闭:
vhost __defaultVhost__ { gop_cache off; }第三个是 TCP 相关的调优参数,比如开启tcp_nodelay,减少小包堆积带来的延迟抖动。这些参数在 SRS 文档里都有说明,配置方式也比较统一,改完之后重启容器即可。
7. 我踩过的坑和一些长期运维心得
最后这部分是实际操作中的教训汇总。每一个坑都真实发生过,写出来希望帮你少走弯路。
7.1 外网连不上第一排查清单
“本机推流播放都正常,换了台机器就访问不了了”是出现频率最高的问题。这个问题的排查顺序应该是:先查云安全组是否放行了对应端口,再查系统防火墙,最后查 Docker 端口映射绑定地址。
有一次我排查了很久,最后发现是云服务器安全组只放行了 1935,没放行 8080,导致播放端访问不了 HTTP-FLV。那一次的经历让我养成了一个习惯:任何一次部署,都把端口清单写成一个表格,对照安全组一条条核对,绝不依赖记忆。
7.2 理解 CUID 和流地址的映射关系
在 SRS 日志里频繁出现 CUID 这个词,它指的是 Connection Unique ID,即每个客户端连接的唯一标识。刚开始看日志时,看到一大串 CUID 很容易懵,但其实只要知道它是连接的 ID 就足够了。真正需要注意的是流的 URL 映射关系。
SRS 定义一个流的位置靠三要素:路径中的 app、流名称 stream,以及对应的 vhost。默认配置下 vhost 是__defaultVhost__,app 是live,stream 是livestream。这三要素必须一致,推流和播放才能对上。比如你用rtmp://ip/live/livestream推流,播放地址就必须是http://ip/live/livestream.flv或http://ip/live/livestream.m3u8,中间不能换 app 或 stream 名称。
很多新手把 HLS 地址写成了http://ip/hls/livestream.m3u8,结果一直播不出来,原因就是 app 从live变成了hls,和推流路径对不上了。
7.3 Docker 日志和配置文件的运维习惯
长期跑 SRS 之后,运维习惯比技术细节更重要。我对所有流媒体容器的统一建议有三条。
第一,Docker 的日志驱动一定要限制大小。默认情况下,容器日志会无限增长,SRS 在实况直播场景下的日志量不小,跑几个月能把磁盘塞满。docker-compose 里可以这样限制:
logging: driver: json-file options: max-size: "50m" max-file: "3"第二,srs.conf 一定要纳入版本管理。流媒体配置的改动往往不是频繁的,但一旦改错了,想快速回滚到上一个可用版本,没有版本管理就只能凭记忆改回来。
第三,升级前先看版本差异。如果要从 SRS 4 升级到 SRS 5,不要直接切换镜像 Tag 了事,一定要先看官方文档里的配置变更说明,特别是 WebRTC 相关配置。我就遇到过从 4.x 切到 5.x 后,某些配置项写法变了,启动直接报错的情况。
7.4 一条很实用的收尾建议
如果你也打算在生产环境跑 SRS,我建议在正式上线前做一次全链路延迟测试:推流端放一个毫秒级计时器画面,播放端拍照对比延迟。这个测试能同时检验网络环境、协议选择和服务器参数调优的效果。只有亲眼看到延迟数据,你才知道当前配置是否真的适合你的业务场景。大多数人第一次测出来的结果都会和预期差很多,而这恰好就是调优的开始。