很多人第一次接触 SRS,都是因为在某个讨论组里看到同事甩出一条 rtmp://xxx/live/xxx 的推流地址,或者是在公司内部想搭一套能支撑直播、连麦、摄像头接入的实时音视频流媒体平台,结果发现 Nginx 那一套方案要么撑不住高并发,要么在 WebRTC 和低延迟这块绕来绕去。我自己最开始也是从 Nginx-RTMP 模块入门的,后来被项目逼着接入 WebRTC 和 SRT,才彻底转投 SRS。如果你也正在纠结“用什么方案自建流媒体服务”,又希望部署过程足够省心,那 Docker + SRS 这套组合可以说是我目前用下来最顺手的方案。这篇文章会把我的选型思路、完整的部署过程、实测遇到的坑和进阶配置一起梳理出来,希望能帮你少走几步我走过的弯路。

做这套部署之前,先说清楚目标场景:我这里要搭建的是一个支持 RTMP、HTTP-FLV、HLS 和 WebRTC 四类协议共存的实时音视频流媒体平台,前端用 OBS 推流,浏览器和 VLC 都能拉流播放,后面还要留出鉴权和转码的升级空间。文章里的每一步操作都基于这个目标展开,不涉及复杂的集群和高可用,适合中小型项目、内网服务和个人开发者参考。

1. 为什么是 SRS + Docker,而不是其他组合

1.1 SRS 在流媒体服务器里的定位

SRS 全称是 Simple Realtime Server,中文名叫“简单实时服务器”,由国内开发者主导开源,目前在 GitHub 上有非常活跃的社区,文档的中文支持也做得很好。它在流媒体服务器里的定位,简单说就是“一套服务,把推流、拉流、协议转换、低延迟传输全包了”。相比 Nginx-RTMP 需要自己拼凑插件和模块,SRS 原生就支持 RTMP、HLS、HTTP-FLV、WebRTC、SRT、GB28181 等一长串协议,这种多协议支持能力让它的应用场景一下子被拉开:直播平台可以用它做源站,在线教育能用它支撑低延迟互动,安防项目还能直接对接国标摄像头。

我当初选 SRS 还有一个很现实的原因:WebRTC 的支持做得很顺手。Nginx-RTMP 对 WebRTC 几乎是无能为力的,如果只做传统 RTMP 直播倒无所谓,但一旦要做网页低延迟播放、视频连麦这些功能,SRS 基本上是一条捷径。它的内置 WebRTC 网关能直接接收 WHIP 推流,也能通过内置播放器做 RTC 拉流,这在同类开源方案里非常少见。

1.2 Docker 解决的最大痛点

SRS 本身是一个 C++ 写的服务,官方也提供了编译安装的方式。但如果你像我一样,需要频繁切换版本、在不同服务器上重复部署,又或者只是想在本地 Mac/Windows 上快速跑一个测试环境,那编译安装就有点让人头疼了——依赖环境、编译参数、版本兼容性,随便一个都能耗掉你半个下午。

Docker 在这里解决的最大痛点,就是 把 SRS 和它的运行时环境打包成了一个不可变单元 。本地跑起来不过十几秒,切换到生产服务器也只是 docker pull 一下的事,不用关心目标机器上缺了哪个库、glibc 版本够不够。这一点对后续升级也特别重要:SRS 版本从 4.0 升到 5.0 的时候,我只需要改一个镜像 tag,然后重启容器,就完成了整个软件的升级,配置文件和宿主机上的其他服务完全不受影响。

另外一个容易被忽略的优势是 资源隔离 。流媒体服务一旦并发上来,CPU 和带宽占用都很猛,Docker 的容器化让它可以被限制在可控的 CPU、内存范围内,不至于因为某一路异常流把整台服务器拖垮。

2. 部署前的网络规划与镜像准备

2.1 端口清单:一次想清楚,后面少返工

SRS 涉及的协议和端口比较多,部署前一定要先把端口映射的规划想明白。我个人习惯是先把一张端口清单列出来贴在终端旁边,后面启动容器和配置防火墙的时候对照着看,能省掉很多低级错误。

端口 协议 用途 是否必开
1935 TCP RTMP 推拉流 必须要开
1985 TCP HTTP API 接口 建议开启
8080 TCP HTTP 服务(播放器页面、FLV 拉流) 建议开启
8000 UDP WebRTC 信令媒体传输 WebRTC 场景必开
8001 UDP WebRTC 媒体传输备份端口 WebRTC 场景必开

大多数人在配置阶段最容易犯的错,就是只放行了 TCP 端口,结果 WebRTC 怎么都打不开。WebRTC 的音视频数据走的是 UDP,只听 TCP 是不够的。另外,RTP 协议族的端口范围比较大,如果严格做了防火墙白名单,建议把 UDP 10000-20000 也可能涉及到的范围一并考虑进去,具体取决于你后面的 SRS 配置方式。

2.2 镜像拉取与加速配置

SRS 官方 Docker 镜像有两个地址,一个是 Docker Hub 的 ossrs/srs ,另一个是阿里云的 registry.cn-hangzhou.aliyuncs.com/ossrs/srs 。如果你在国内的云服务器上操作,我强烈建议直接用阿里云镜像地址,别跟 Docker Hub 的超时问题死磕。我的服务器在香港和国内都有测试过,国内节点直接拉 Docker Hub 经常需要几分钟甚至直接超时,换成阿里云地址后基本秒级完成。

注意:拉取镜像时建议显式指定版本 tag,不要用 latest 。SRS 版本升级引入的行为变化不少,一旦拉到一个意外的新版本,可能你的配置文件就不兼容了。我目前使用的是 :5 这个 tag,对应 SRS 5.0 系列。

如果只想拉镜像先看看,可以执行这条命令:

docker pull registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5

如果你使用的服务器已经配置了私有镜像仓库,或者在离线环境部署,也可以用 docker save / docker load 的方式迁移镜像,这个属于基础操作,不再展开。

3. 手写配置与一键启动:完整部署过程

3.1 配置文件目录结构与 srs.conf 详解

SRS 的配置逻辑不算复杂,核心是一个 srs.conf 文件,所有协议、虚拟主机、录制、转码、鉴权策略都在这个文件里定义。用 Docker 部署的时候,我习惯把宿主机上的一个目录挂载进容器,这样改配置文件以后,只需要重启容器就能生效,不用重新构建镜像,非常方便。

我的工作目录长这样:

/opt/srs
├── conf
│   └── srs.conf
└── docker-compose.yml

下面是我实际在用的 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__ {
    rtc {
        enabled     on;
    }
    http_remux {
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
    }
    hls {
        enabled         on;
        hls_path        ./objs/nginx/html;
        hls_fragment    2;
        hls_window      10;
    }
}

逐段拆解一下:

  • listen 1935 :SRS 主监听端口,RTMP 协议专用。
  • daemon off + srs_log_tank console :让 SRS 以前台模式运行,日志直接打到控制台。在 Docker 里,这个配置几乎是必须的——容器要靠前台进程保活,日志也要通过 stdout 交给 Docker 的日志系统。
  • http_api :开启 1985 端口的 HTTP API,通过它就可以查询在线流、踢流、获取服务器状态。测试链路的时候特别有用。
  • http_server :开启 8080 端口的 HTTP 文件服务,SRS 自带的 WebRTC 播放器页面就靠它来承载。
  • rtc_server :WebRTC 的媒体面入口。 candidate 这个参数是后面最容易踩坑的地方,测试环境可以配 127.0.0.1 ,但生产环境一定要改为服务器公网 IP 或域名,否则客户端无法把媒体包发回来。
  • vhost __defaultVhost__ :FRS 的虚拟主机概念,类似 Nginx 的 server 块。默认虚拟主机接受任意 Host 头,适合简单部署。
  • rtc :开启该虚拟主机下的 WebRTC 支持。
  • http_remux :把 RTMP 流实时转换成 HTTP-FLV 流,浏览器里的播放器就是通过这个协议做低延迟播放的。
  • hls :开启 HLS 切片输出。 hls_fragment 是每个切片的时长(秒), hls_window 是窗口里保留的切片数量。这个功能适合需要兼容 iOS Safari 等不能直接播放 FLV 的场景。

3.2 docker-compose.yml 与启动命令

接下来是 docker-compose.yml 。我使用 compose 而不是裸的 docker run ,原因很简单:端口映射、目录挂载、重启策略这些配置可以写进代码,换一台机器部署时直接拷贝文件即可,不会因为忘记了某个参数导致环境不一致。

services:
  srs:
    image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
    container_name: srs
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./conf/srs.conf:/usr/local/srs/conf/srs.conf
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

这里有一个很关键的选型: network_mode: host 。我在前两轮用 bridge 模式部署过,结果 WebRTC 的 UDP 端口映射和 candidate 配置都要额外处理,经常出现能推不能拉的情况。后来改成 host 网络,容器直接共享宿主机的网络栈,端口映射的问题就全部消失了,SRS 看到的 IP 就是宿主机 IP,candidate 配置也简单直接。

注意:host 网络模式在 Docker Desktop(Windows 和 macOS)上支持不完整,如果你只是在本地做测试,建议在 Linux 服务器上实践这套配置。这也是为什么我推荐把设计好的 Compose 文件直接拿到云服务器或内网 Linux 主机上运行,而不是依赖本地虚拟机。

启动服务:

cd /opt/srs
docker compose up -d

查看状态:

docker ps | grep srs
docker logs -f srs

如果配置没问题,日志里应该会出现类似这样一行:

SRS is up. You can play the following streams:

此时基础服务已经跑起来了。

4. 从推到拉:用 OBS 和播放器验证整套链路

4.1 OBS 推流配置,关键参数别选错

服务端起来了,接下来就是用实际流来验证链路。我用 OBS Studio 做推流端,因为它在推流参数上暴露得足够清楚,方便排查问题。

OBS 里打开“设置 — 直播”:

  • 服务:自定义
  • 服务器: rtmp://你的服务器IP/live
  • 推流码: test

这里有一个容易踩的坑:推流码不要带前缀斜杠,直接写 test 。有些教程会写成 /test ,在某些版本下虽然也能通,但流的 app 和 stream 参数解析会和你预期的不同,导致播放地址对不上。

关于视频编码,如果你的网络带宽充足,建议先用 H.264 + 普通的 CBR 码率;音频建议先用 AAC。WebRTC 播放端对 H.264 + AAC 的兼容性总体最好,如果你一上来就推 HEVC 或者 Opus 音频,后面拉流很容易在某个环节莫名其妙黑屏/无声。

4.2 三种方式拉流验证

OBS 点击“开始推流”之后,我们可以从三个角度验证流是否正常。

第一种,用 VLC 拉 RTMP 流。打开 VLC,选择“打开网络串流”,地址填:

rtmp://你的服务器IP/live/test

如果能正常播放,说明 RTMP 链路通了。

第二种,用浏览器拉 HTTP-FLV。SRS 在 8080 端口内置了测试播放器页面,直接访问:

http://你的服务器IP:8080/players/srs_player.html

在播放地址框里填:

http://你的服务器IP:8080/live/test.flv

点播放,如果浏览器能流畅出画面,说明 HTTP-FLV 链路正常。这个方式对低延迟直播特别有用,延迟可以控制在 1 到 3 秒。

第三种,验证 WebRTC。访问:

http://你的服务器IP:8080/players/rtc_player.html

地址填:

webrtc://你的服务器IP/live/test

这里有两个大坑点,我后面会单独展开讲。如果 WebRTC 播放页能正常看到画面和声音,说明 UDP 媒体通路和 candidate 配置都没问题,整套链路就基本打通了。

4.3 用 HTTP API 确认流状态

播放验证没问题后,还可以通过 HTTP API 再做一次“体检”:

curl http://127.0.0.1:1985/api/v1/streams

正常会返回一个 JSON 数组,里面包含当前在线的流列表以及每个流的 app 、 stream 、 vhost 、 create 时间等元数据。这个接口在后面的自动化运维和监控告警里非常有用,也是流是否真正稳定在线的权威凭证。

5. 翻车现场记录:部署和试用中的真实坑点

5.1 我在内网环境第一次启动时的完整排查链路

说一个我真实经历过的翻车现场,因为很有代表性:内网服务器第一次部署完后,OBS 能正常推流,VLC 也能拉到 RTMP 流,但办公室同事的浏览器打不开 WebRTC 播放页面,一直转圈。

我当时的第一步是检查 SRS 日志, docker logs -f srs 里能看到 WebRTC 推流或拉流的握手记录。日志里在拉流请求的位置抛出了 parse candidates error 之类的信息。

第二步,确认 candidate 配置。我在 rtc_server 里把 candidate 配成了 127.0.0.1 ,服务器自己认为自己是本机回环地址,发给浏览器的 ICE candidate 当然就包含 127.0.0.1 。浏览器拿到一个指向本机的 candidate,怎么可能连到内网服务器。把配置改成服务器的内网 IP 之后,WebRTC 拉流才能正常。

第三步,检查 UDP 端口通不通。直接在服务器上执行:

nc -uvz 你的服务器IP 8000

以及

nc -uvz 你的服务器IP 8001

如果返回 Connected 说明端口可达。如果网络管理员没有放行 UDP 8000/8001,浏览器就可能一直卡在 ICE 连接超时,和 candidate 配置错误的现象高度相似。

整个排查链路走下来,我才意识到 WebRTC 这个环节的“网络 — 配置 — 防火墙”三者必须同时装对,少一个都不行。后来我把这套排查顺序整理成了固定流程: 先看日志 → 再查 candidate → 最后查防火墙 ,再也没在 WebRTC 这个问题上卡过超过十分钟。

5.2 音频编码引发的无声玄学

另一个容易被忽略的坑是音频编码。SRS 5.0 里对音频的兼容逻辑有一定调整,如果你的推流端用的是带 Opus 编码的 WebRTC 推流,而拉流端用的是 HLS 或者 RTMP 播放器,声音偶尔会出现无法播放或延迟异常的情况。这在混合协议场景下很常见——RTMP 和 HLS 对音频的要求是 AAC 为主,WebRTC 则偏爱 Opus。

如果你的直播场景里有网页低延迟互动,又有传统的 RTMP/HLS 分发需求,最省心的办法是推流端统一用 AAC 音频编码,并在 SRS 配置里把 WebRTC 到 RTMP 的转封装逻辑确认好。视频编码尽量用 H.264(而不是 H.265/HEVC),因为 HEVC 在 WebRTC 的浏览器兼容性上至今还有不少坑。

注意:Windows 录屏或游戏直播场景中,OBS 默认音频设备可能是 48kHz 的立体声,这本身没问题。但如果你在采集设备上启用了“音频增强”或“降噪”功能,推上去的音频在某些播放器里可能出现爆音或静音,建议推流测试时把音频滤镜全部关掉。

5.3 Docker 与宿主机的时区问题

还有一个很小但很容易让人误判的问题:时区。SRS 日志默认使用 UTC 时间,而国内服务器的本地时间通常是 UTC+8。排查问题时你可能会发现,日志里的时间“慢了 8 小时”,如果不够细心,很容易误认为系统有延迟或者服务重启过。

解决办法是在 Compose 文件里注入系统时间:

environment:
  - TZ=Asia/Shanghai

添加这一行后重启容器,日志时间就和本地时间对齐了。这个配置在后续排障、对比推流时间时能节省不少心力。

6. 让 SRS 真正可用:鉴权、HTTPS 和转码扩展

6.1 推流鉴权:防止陌生人往你的服务器上推流

基础链路通了以后,首先要考虑的就是安全。SRS 如果不做任何鉴权,任何知道你的服务器地址的人都可以往 live 应用里推流或者拉流,这在公网环境里非常危险,很快就有被人塞满垃圾流口的风险。

SRS 支持通过 HTTP 回调做推流鉴权,原理是:有推流请求进来时,SRS 会向你的业务服务器发送一个 HTTP 请求,带上推流的 app 、 stream 、 param 等信息,你的业务服务器返回 0 就允许推流,返回非 0 就拒绝。

配置片段示例:

vhost __defaultVhost__ {
    http_hooks {
        enabled         on;
        on_publish      http://你的业务服务器地址/api/srs/on_publish;
        on_play         http://你的业务服务器地址/api/srs/on_play;
    }
}

这种鉴权方式的灵活度很高,你可以根据推流码中的 token 来判断是否有权限,也可以对接自己的用户体系。实测中需要注意:回调接口必须要快速响应,建议控制在 100 毫秒以内,否则推流端会感觉“卡了一下才推上去”。

6.2 HTTPS 与 WSS:跑公网无法回避的问题

如果你需要把流媒体平台暴露到公网,且网站本身用的是 HTTPS,那 8080 端口上的 HTTP 播放器页面就会出现“混合内容”问题,浏览器会禁止播放 HTTP 的流。此时你需要给 SRS 配置 HTTPS/WSS。

SRS 的 TLS 配置不复杂,需要准备证书文件(或者使用反向代理做 TLS 终结)。我自己的习惯是前面放了一层 Nginx 反代,终止 HTTPS 和 WSS,然后转发到 SRS 的 1985/8080 端口,这样 SRS 本身不需要管理证书,证书续期也统一到 Nginx 上处理。

如果你不想引入 Nginx,也可以直接在 SRS 里配置 TLS,需要在 http_server 和 rtc_server 里分别开启 HTTPS/WSS,并把证书路径挂载进容器内:

http_server {
    enabled         on;
    listen          8080;
    https {
        enabled     on;
        listen      8443;
        key         ./conf/server.key;
        cert        ./conf/server.crt;
    }
}

改完配置后重启容器即可生效。公网环境下,WSS 是 WebRTC 拉流的基本要求,纯 WebSocket 是不被浏览器接受的。

6.3 转码、录像与更多玩法

SRS 本身不侧重转码,它对标的是流媒体服务,转码能力有限。如果你有转码需求,比较常见的套路是让 SRS 把流回调给 FFmpeg 或专门的转码服务,或者直接用 FFmpeg 在推流端做二次推流。这个属于进阶玩法了,我这篇主要讲部署,先点到为止。

录像功能倒是 SRS 原生支持的。在 vhost 里开启 dvr 模块:

vhost __defaultVhost__ {
    dvr {
        enabled         on;
        dvr_path        ./objs/nginx/html/record/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
        dvr_plan        session;
    }
}

dvr_plan session 表示每次推流会话生成一个独立的录像文件,录像文件路径支持按时间戳自动生成目录,方便后续做回看管理。容量管理上要特别留意,录像文件增长很快,建议挂载一块独立的数据盘并配置周期清理策略。

7. 我这一路用下来的体会与延伸建议

部署这条链路到现在,我最大的体会是: 流媒体服务最难的不是把服务跑起来,而是让各种不同协议之间能真正协作起来。 Docker 帮我们解决了系统环境的一致性问题,SRS 帮我们解决了很多协议兼容问题,但剩下的——网络规划、编码选型、安全策略——仍然需要自己动手去理解和测试。

如果你准备在生产环境推进,我最后再分享三个建议:

第一,无论服务多么简单,建议从一开始就启用 HTTP API 的访问日志记录。SRS 的 API 接口非常方便做监控,自己写一个定时任务去拉取 api/v1/streams 并记录在线流数量,就能对你平台的实际使用情况有一个持续的量化认知。

第二,推流端的编码参数尽量提前统一。我经历过一次线上事故,就是某个主播用 HEVC 推流,而平台大部分播放器都只支持 H.264,结果他的直播间在网页端直接黑屏。后来我们把推荐编码写进了主播端的开播设置里,从源头避免这类兼容性事故。

第三,容器版本升级前,一定先在生产环境之外做一次完整回归。SRS 官方文档里有详细的版本迁移说明,不要想当然地认为小版本升级不会破坏现有配置。我有一次从 4.0 升到 5.0 时,配置文件里 HTTP 回调的字段格式有变化,导致鉴权短暂失效,后来对照官方文档逐一核对才恢复正常。

Docker + SRS 这套方案的坑基本都在这里了。按照这篇文章的顺序来部署,正常情况下你两个小时之内就能拥有一个支持 RTMP、HTTP-FLV、HLS 和 WebRTC 的实时音视频流媒体平台。后续的录像、鉴权、转码、监控等需求,也都可以在这个基础架构上稳步展开。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐