做直播、低延迟连麦、监控大屏这类音视频项目时,SRS 是我第一时间会想到的开源流媒体服务器。它协议支持全、社区活跃,配合 Docker 部署更是能把环境编译、依赖冲突这些麻烦事直接挡在门外。这篇文章不聊虚的,直接讲清楚怎么用 Docker 把 SRS 跑起来,打通 RTMP、WebRTC、HLS、HTTP-FLV 这几条关键链路,并把我在实际部署中踩过的坑和排查思路一并分享出来。无论你是刚接触流媒体方向的新手,还是已经在做音视频落地的工程师,照着这篇文章操作,基本能把“推流-分发-播放”这条主链路完整跑通。
我自己的建议是:先别看那么多协议文档,先把服务跑起来,看到画面,再回头对照配置理解原理,这样效率最高。
1. 为什么用 Docker 部署 SRS:需求、选型与准备
1.1 SRS 在音视频项目里到底解决什么问题
SRS 全称 Simple Realtime Server,是一个开源的高性能流媒体服务器,作者是开源社区里的知名团队 OSSRS。它最大的特点是“协议全家桶”:既能接收 RTMP 推流,也能转出 HLS、HTTP-FLV、WebRTC、SRT、GB28181 等多种格式,几乎覆盖了直播、会议、监控、在线课堂这些主流场景。
在项目里,SRS 解决的核心问题就是“把一路画面变成多路可播放的地址”。比如你有一台摄像头或者一个主播端,原始画面不可能直接让成千上万人拉取,SRS 在中间做协议转换和分发,客户端只需要拿到一个 HTTP 或者 RTMP 地址,就能在浏览器、手机、VLC 播放器里看到内容。
适合使用 SRS 的典型场景我列几个:企业内部直播培训、在线教育一对一/小班课、安防监控流接入、用 OBS 推流做个人直播、甚至是一些物联网设备推流上云的场景。相比云厂商的直播服务,SRS 自建方案没有按流量计费的问题,数据完全在自己服务器上,对于重视隐私或者预算有限的团队非常友好。
1.2 对比裸机源码编译,Docker 方案为什么更香
我最早接触 SRS 的时候是在一台 CentOS 服务器上源码编译部署,当时被依赖问题折腾得够呛。SRS 编译需要匹配的 gcc、cmake、openssl、ffmpeg 库,稍有不慎就报错,一个上午就耗进去了。后来切到 Docker 部署之后,整个体验完全是两个级别。
Docker 方案最核心的优势是“开箱即用”。官方镜像 ossrs/srs 已经把各种协议特性默认编译好了,一条 docker run 就能启动完整的流媒体服务,不需要关心底层操作系统、库版本,也不需要在宿主机上装任何 SRS 的编译依赖。镜像的 tag 体系也很好用,想升级就换 tag,想回滚就改回旧版本,没有任何残留文件的问题。
如果你是在 Windows 或者 macOS 上做开发调试,Docker 更是绕不过去的方案。SRS 官方调研也提到 Windows 原生支持并不好,但在 Docker Desktop 里跑 Linux 容器就完全没有障碍。生产环境同样推荐容器化,因为交付物、运行配置、运维命令都是同一套,团队协作成本低。
当然,Docker 也不是万能的。如果你追求极端性能,比如单机支撑上万并发并且需要针对内核参数做深度调优,那用裸机编译部署可能更适合。但常规的百级到千级并发场景,Docker 的开销完全可以忽略,我认为没必要为了几毫秒的性能差异付出大量运维成本。
1.3 前置准备:Docker 环境与镜像加速
部署之前,先把 Docker 环境准备好。不同系统的安装方式差异比较大,我按常见情况简单说一下。
Linux 服务器(Ubuntu/Debian 系)通常这样装:
sudo apt update sudo apt install -y docker.io docker-compose-v2 sudo systemctl enable --now dockerCentOS/RHEL 系则推荐使用官方源安装 docker-ce,一条脚本也能完成:
curl -fsSL https://get.docker.com | bash sudo systemctl enable --now dockerWindows 和 macOS 用户一般是安装 Docker Desktop。如果启动时提示 “virtualization support not detected” 或者 “Docker Desktop failed to start because virtualisation support wasn't detected”,多半是 BIOS 里的虚拟化开关没打开,需要重启进 BIOS 开启 Intel VT-x 或 AMD-V,然后在 Windows 功能里确认打开了 Hyper-V 或 WSL2。这类问题在论坛里刷屏最多,但九成都是这个原因。
装完 Docker 之后,建议顺手配置一个镜像加速。国内网络直接从 Docker Hub 拉镜像有时候会比较慢,可以在 Docker 的 daemon.json 里配置国内公共镜像加速地址,例如阿里云容器镜像服务提供的个人加速地址:
{ "registry-mirrors": ["https://your-id.mirror.aliyuncs.com"] }配置后重启 Docker 服务生效。验证环境是否正常,执行 docker version 和 docker run hello-world,看到正常提示就说明 Docker 服务没问题,可以进行下一步了。
注意:如果你不想手动配置 daemon.json,也可以使用 docker desktop 的设置界面添加 registry-mirrors,效果一样。
2. 核心细节解析:协议、配置与端口规划
2.1 RTMP / WebRTC / HLS / HTTP-FLV 怎么选
很多新手第一次用 SRS 时最大的困惑不是怎么安装,而是“我到底该用哪个协议地址去播放”。这里我花点篇幅把协议之间的关系讲清楚,后面配置时就心里有数了。
RTMP 是最老牌的直播推拉流协议,基于 TCP 的 1935 端口,Adobe 公司提出,大多数直播软件(比如 OBS)默认推流都支持。它的优点是稳定、兼容性好,缺点是不被浏览器原生支持,需要 Flash 或插件,所以现在主流做法是用 RTMP 做“推流入口”,播放则交给下面的协议。
HTTP-FLV 是当前国内直播场景使用最广泛的分发协议。简单理解就是把 RTMP 流封装成 FLV 格式,通过 HTTP 传输给浏览器。浏览器端用 flv.js 这个库就可以播放,延迟通常控制在 1 到 3 秒,适合直播、监控等场景。SRS 默认在 HTTP 服务端口下提供 .flv 后缀的播放地址,非常方便。
HLS 是苹果主导的协议,原理是把直播流切成一个个小 ts 文件,再通过 m3u8 索引文件播放。它的优势是浏览器和移动端原生支持,不需要插件,缺点是切片导致延迟偏高,通常有 5 到 15 秒甚至更高。适合点播、对延迟要求不高的电视直播场景。
WebRTC 则是实时互动的王牌协议,基于 UDP 传输,端到端延迟能压到 500 毫秒以内,适合视频会议、连麦、低延迟监控这些场景。SRS 对 WebRTC 的支持做得非常好,可以直接把 RTMP 流转成 WebRTC 流给浏览器播放,延迟体验远好于 HTTP-FLV。
一句话总结选型逻辑:推流用 RTMP,跨端分发优先 HTTP-FLV,追求极低延迟用 WebRTC,移动端苹果生态或者不着急的场景用 HLS。SRS 的好处是这些协议可以同时开启,一份流进来,多个协议地址同时输出,不需要你做二选一。
2.2 srs.conf 关键配置逐项拆解
SRS 的配置集中在 srs.conf 文件里,默认路径是 /usr/local/srs/conf/srs.conf,Docker 镜像启动时也会读取这个文件。下面我把最关键的几项配置拆开讲。
listen 1935; max_connections 1000; daemon off; srs_log_tank console;listen 指定 RTMP 监听端口,默认 1935,一般不用改。max_connections 是最大并发连接数,根据服务器资源调整,个人测试 1000 足够。daemon off 是 Docker 部署里的关键项,意思是让 SRS 在前台运行,如果没有这一项,SRS 默认会后台守护进程方式运行,容器因为找不到前台进程就会直接退出,很多人第一次跑容器“秒退”就是这个问题。srs_log_tank console 表示日志输出到控制台,这样我们才能用 docker logs 看到运行日志。
再看 HTTP 相关配置:
http_api { enabled on; listen 1985; } http_server { enabled on; listen 8080; dir ./objs/nginx/html; }http_api 是 SRS 的 HTTP API 服务,可以查询当前流列表、连接数等统计信息,比如访问 http://服务器IP:1985/api/v1/streams 就能看到正在直播的流列表。http_server 是内嵌的 HTTP 静态文件服务,主要用来做 HLS 文件分发,以及提供 SRS 自带的播放器测试页面,默认目录就是 SRS 安装目录下的 objs/nginx/html。
WebRTC 相关的配置是:
rtc_server { enabled on; listen 8000; candidate 127.0.0.1; }rtc_server 开启 WebRTC 功能,listen 指定 UDP 端口 8000。candidate 是踩坑重灾区,它的作用是告诉浏览器“你的媒体数据应该发到哪个 IP”。如果是本地调试,填 127.0.0.1 没问题;但如果部署在云服务器上,这里必须填服务器的公网 IP 或者 EIP,否则客户端拿到的 SDP 里是一个内网地址,WebRTC 的 UDP 连接根本打不通。
vhost 配置块负责多域名、多应用的逻辑隔离,默认的defaultVhost就是默认虚拟主机。里面的 tcp_nodelay 和 min_latency 和延迟优化相关;play 区域里的 gop_cache 表示是否开启关键帧缓存,开启后新进用户能快速起播,但会增加延迟,实时互动场景建议关闭,直播场景建议开启。
2.3 端口映射到底怎么规划
Docker 部署最需要提前想清楚的就是端口规划。SRS 涉及多种协议,每种协议都有对应端口,一旦容器启动了再改端口映射就麻烦不少。我通常采用下面的端口规划:
| 协议 | 默认端口 | 传输类型 | 用途 |
|---|---|---|---|
| RTMP | 1935 | TCP | 推流/拉流 |
| HTTP API | 1985 | TCP | 查询流状态、控制接口 |
| HTTP 服务 | 8080 | TCP | HLS、HTTP-FLV、测试播放页 |
| WebRTC | 8000 | UDP | 低延迟音视频传输 |
| HTTPS(可选) | 8443 | TCP | 配合 WebRTC 的 HTTPS 访问 |
在 docker run 命令里,端口映射的写法是“宿主机端口:容器端口”。如果宿主机端口被占用,可以修改左边的数字,比如 19360:1935,但需要注意播放地址里的端口也要跟着改成 19360。
这里特别提醒一下,WebRTC 的 8000 端口必须映射为 UDP 格式,命令里要写 8000:8000/udp。很多人在云服务器上配好了一切,结果 WebRTC 就是连不上,一查才发现安全组只放行了 TCP 的 8000,UDP 没放行。云服务器安全组和系统防火墙(firewalld/ufw)都需要同时支持 UDP 8000 端口。
如果后续要支持 SRT 协议,额外映射 9000/udp;需要 GB28181 监控接入,默认也是 9000 端口(可以改)。我的原则是:先按最小集把核心协议跑通,再根据业务需要逐步加端口,不要一开始就把所有端口全开,增加安全风险。
3. 实操过程:从拉镜像到完成推流播放
3.1 第一跑:docker run 最小化启动
一切准备就绪后,先拉取 SRS 官方镜像。推荐使用 SRS 5.0 版本,镜像 tag 是 ossrs/srs:5,这个版本对 WebRTC 的支持已经非常稳定。如果你的网络拉取缓慢,可以换成阿里云镜像仓库的地址 ossrs/srs:5 或者 registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5。
docker pull ossrs/srs:5拉取完成后,执行下面这条命令启动一个最简实例:
docker run -d --name srs \ -p 1935:1935 \ -p 1985:1985 \ -p 8080:8080 \ -p 8000:8000/udp \ ossrs/srs:5-d 表示后台运行,--name srs 给容器命名。启动后立即执行 docker ps 查看状态,正常情况下 STATUS 应该是 Up。然后打开浏览器访问 http://服务器IP:8080/,如果可以看到 SRS 的欢迎页面,说明基础服务已经起来了。
日志排查这一步很重要,养成习惯:
docker logs -f --tail=100 srs你会看到类似下面的输出:
SRS is up, version: 5.0.0 live/stream: play, client: 127.0.0.1:51892这说明 SRS 正在正常运行,已经用默认配置跑起来了。注意,这种启动方式没有挂载自定义配置,使用的完全是镜像内部的默认 srs.conf,适合快速验证环境,真正要落地使用还是要进入下一步的 compose 配置。
3.2 进阶:docker compose 挂载自定义配置
实际项目中,我通常不会用 docker run 直接跑生产服务,而是用 docker compose 管理。这样配置一目了然,团队协作也方便。先建立一个项目目录:
mkdir -p /opt/srs/conf cd /opt/srs在 /opt/srs/conf 下创建 srs.conf,写入一个适合本地测试和生产起步的完整配置:
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 127.0.0.1; } vhost __defaultVhost__ { tcp_nodelay on; min_latency on; play { gop_cache on; queue_length 10; } }然后在 /opt/srs 下创建 docker-compose.yml:
version: "3" services: srs: image: ossrs/srs:5 container_name: srs restart: unless-stopped ports: - "1935:1935" - "1985:1985" - "8080:8080" - "8000:8000/udp" volumes: - ./conf/srs.conf:/usr/local/srs/conf/srs.conf启动命令:
docker compose up -d重点解释两个配置的意义。restart: unless-stopped 表示 Docker 会自动拉起意外退出或重启后恢复的容器,生产环境必须加。volumes 把宿主机的 srs.conf 挂载到容器内部对应路径,覆盖默认配置。这样改配置后只需要执行 docker compose restart srs 就能生效,不用重新构建镜像。
3.3 推流与拉流实战
服务跑起来后,最激动人心的环节就是推流和拉流。我用 ffmpeg 推流是最常见的做法,先准备一个测试的视频文件 test.mp4,然后执行:
ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost/live/stream-re 的作用是让 ffmpeg 按视频原始帧率读取文件,模拟实时推流。如果没有本地文件,也可以用摄像头实时推流:
ffmpeg -f dshow -i video="USB Camera" -c:v libx264 -preset ultrafast -f flv rtmp://localhost/live/streamWindows 的 dshow 设备名可以通过 ffmpeg -list_devices true -f dshow -i dummy 查看,macOS 用 avfoundation。推流过程中,SRS 日志会有 publish 记录。
推流成功后,我们有四种播放地址可以验证:
RTMP: rtmp://localhost/live/stream HTTP-FLV: http://localhost:8080/live/stream.flv HLS: http://localhost:8080/live/stream.m3u8 WebRTC: webrtc://localhost:8080/live/streamPC 端最简单的播放方式是打开 VLC 播放器,选择“媒体 → 打开网络串流”,粘贴 RTMP 地址或者 HTTP-FLV 地址。如果 VLC 能看到画面,说明整个链路已经通了。
浏览器端验证更方便,SRS 自带了播放器测试页面,直接访问:
http://localhost:8080/players/flv_player.html http://localhost:8080/players/rtc_player.html在页面里填上对应的流地址,点击播放即可。HLS 的 m3u8 地址也可以直接丢给浏览器原生 video 标签,比如:
<video src="http://localhost:8080/live/stream.m3u8" controls autoplay></video>同时,通过 HTTP API 可以确认当前流的状态:
curl http://localhost:1985/api/v1/streams返回 JSON 里会包含 streams 数组,里面有 stream 名称、推流客户端的 IP、创建时间等信息。这是我平时排查问题最常用的接口。
3.4 WebRTC 低延迟播放与 HTTPS 配置
如果你只想公网播放 HTTP-FLV,其实不需要 HTTPS。但 WebRTC 的很多高级能力,比如调用摄像头、麦克风,浏览器强制要求页面必须是 HTTPS 环境。本地 localhost 是例外,公网 IP 访问时必须走 HTTPS。
一种方案是直接用 SRS 内置的 HTTPS 支持,在 srs.conf 的 http_server 区域增加 https 段落:
http_server { enabled on; listen 8080; dir ./objs/nginx/html; https { enabled on; listen 8443; key ./conf/server.key; cert ./conf/server.crt; } }key 和 cert 指向 SSL 证书和私钥文件,可以用 openssl 生成本地自签名证书测试,生产环境建议使用域名并配置 Let's Encrypt 自动签发证书。
另一种更灵活的生产方案是用 Nginx 或 Caddy 做 HTTPS 反向代理,统一管理证书和域名转发。相对而言,Caddy 配置更简单,只需要在 Caddyfile 里写一行:
srs.example.com { reverse_proxy 127.0.0.1:8080 }Caddy 会自动申请和续期 HTTPS 证书,省去手工运维证书的麻烦。
无论用哪种 HTTPS 方案,WebRTC 播放时还需要注意 candidate 的配置必须改成服务器的公网 IP,否则即使 HTTPS 正常,WebRTC 也无法完成媒体传输。我在云服务器上排过一个多小时就是卡在这个问题上,日志里看不到明显报错,实际上是 candidate 一直是内网地址。
4. 生产环境注意点与常见问题排查
4.1 常见问题速查表
把我在使用中最常遇到的问题整理成一张表,建议收藏。每个问题都对应有明确的排查方向:
| 现象 | 常见原因 | 解决办法 |
|---|---|---|
| 容器启动后立刻退出 | 配置里没有 daemon off | 在 srs.conf 中确认 daemon off; |
| 访问 8080 打不开 | 端口未映射/防火墙未放行 | docker ps 查看端口映射,确认安全组放行 8080 |
| 推流时报 Connection refused | RTMP 地址/端口不对 | 检查 1935 端口映射和防火墙 |
| FFmpeg 推流成功但播放黑屏 | 播放器不支持指定协议 | 换 VLC 或 SRS 自带的 player 页面 |
| WebRTC 播放连接失败 | candidate 为内网 IP、UDP 端口未放行 | 修改 candidate 为公网 IP,放行 8000/udp |
| HTTPS 请求证书报错 | 证书路径挂载不正确 | 确认 key/cert 文件已挂载进容器 |
| 镜像拉取速度很慢 | Docker Hub 网络问题 | 配置镜像加速或使用阿里云镜像仓库 |
| Docker Desktop 启动失败 | BIOS 虚拟化未开启 | 开启 VT-x/AMD-V,启用 WSL2/Hyper-V |
| 修改配置后浏览器还是旧行为 | 容器未重启、浏览器缓存 | docker compose restart srs,换无痕窗口 |
| HTTP API 返回 404 | http_api 未开启或端口不对 | 确认 1985 端口映射和 http_api on |
这张表的排查顺序基本也是我日常排障的顺序:先看到容器状态,再看端口映射,再看安全组,最后看应用日志。
4.2 排查思路与实操技巧
流媒体服务排障,我建议遵循“由外到内,由网络到应用”的原则。不要一上来就怀疑 SRS 配置,先确认端口是不是通的。
检查端口是否正常监听:
ss -tlnp | grep -E "1935|1985|8080"如果是外部客户端,可以用 telnet 或者 nc 测试 TCP 端口:
nc -vz 服务器IP 1935UDP 端口比较特殊,可以用脚本或者直接在服务器上跑 SRS,同时用客户端发起 WebRTC 连接,通过 SRS 日志里是否出现 WebRTC 穿透日志来判断。
进入容器内部检查配置是否正确:
docker exec -it srs bash cd /usr/local/srs ./objs/srs -t-t 参数是测试配置文件的模式,如果有语法错误会直接提示。这是最方便、最安全的配置校验方式,改完配置先测试再重启,避免把自己锁在外面。
查看容器的实时日志是另一个核心手段:
docker logs -f --tail=50 srsSRS 的日志里每个连接都会记录 client 的 IP 和播放的流名,通过日志能定位很多问题。比如播放流时日志显示 “not found”,基本就是流名打错了或者推流端没有成功推到同一个应用名和流名上。
还有一个小技巧是直接查看容器的端口映射是否符合预期:
docker port srs输出会列出容器内的端口到宿主机端口的映射关系,排查映射错误时非常直观。
4.3 性能和稳定性经验谈
SRS 单机性能其实相当不错,官方数据在普通机器上可以支撑数千路并发播放。但在实际项目中,性能瓶颈往往不在 SRS 本身,而在操作系统限制和磁盘 IO 上。
第一,文件描述符限制要拉高。Linux 默认的 ulimit -n 可能只有 1024,高并发场景远远不够。在 docker run 或 compose 里要给容器设置更高的 ulimits 限制,也可以在宿主机层面修改 /etc/security/limits.conf。我一般先把宿主机和容器的 nofile 都改成 65535。
第二,日志要做轮转和限制。容器日志默认会无限增长,长时间运行可能把磁盘写满。最方便的做法是在 daemon.json 里配置 log-driver 的 max-size 和 max-file 参数:
{ "log-driver": "json-file", "log-opts": { "max-size": "20m", "max-file": "5" } }这样单个日志文件超过 20MB 就自动轮转,保留最近 5 个文件,避免磁盘被日志拖垮。
第三,HLS 切片写入会产生大量小文件,对磁盘性能要求相对较高。如果直播频次高、切片时间长,建议使用 SSD 或者给 /usr/local/srs 挂载高性能数据盘。同时可以在配置里调整 hls 的切片时长和列表长度,在兼容性和存储成本之间找平衡。
第四,如果做大规模分发,单台 SRS 终有上限。常见架构是“推流域名到 SRS,播放前加一层 CDN 或负载均衡”,把 SRS 的 HTTP-FLV/HLS 分发能力交给 CDN 边缘节点,SRS 只负责拉流和转协议。这种架构下 SRS 的稳定性会高很多,即使源站重启,边缘节点还能继续服务一段时间。
另外建议给 SRS 容器配置健康检查。在 docker-compose.yml 里可以加上:
healthcheck: test: ["CMD", "curl", "-f", "http://localhost:1985/api/v1/"] interval: 30s timeout: 5s retries: 3健康检查加上之后,监控系统可以自动感知服务的存活状态,出问题要第一时间发现并处理。
最后再说一个我从实践中得出的体会:SRS + Docker 这套组合,最大的价值不是省了一次编译时间,而是让流媒体服务的交付变得标准化。本地调试、预发验证、生产部署用的是同一套镜像和配置文件,出问题的概率大大降低。我自己现在不管项目大小,都会先把 docker-compose 配置沉淀下来,后面再要开新环境,十分钟就能复制一套,省下的时间可以拿来做真正的业务逻辑。
如果你刚上手,我建议先按文章里的步骤把 RTMP 推流和 HTTP-FLV 播放这条链路跑通,再加上 WebRTC,最后再考虑 HTTPS 和调优。一次只引入一个新变量,出了问题也容易定位。等主链路稳定了,再慢慢尝试 SRT、GB28181 这些扩展能力,这套基础架构能支持的场景远比你想的要多。