1. 为什么把SRS装进Docker,而不是直接源码编译

1.1 SRS到底能干什么,先对齐一下认知

SRS全称是Simple Realtime Server,一个开源的实时视频服务器,光看名字里的Simple就知道它设计初衷是让流媒体服务器不那么劝退。它能做的事一句话概括:接收推流端的音视频数据,经过协议转换和分发,把画面送到各种播放端。支持RTMP推流、HTTP-FLV拉流、HLS切片播放、WebRTC低延迟通话,还有SRT这种抗弱网的传输协议,基本覆盖了直播、监控、视频会议、IoT摄像头这类场景的绝大部分需求。

和商业云直播服务相比,自建SRS最大的价值在两点:一是数据完全在自己手里,推流地址、播放地址、录制文件都是自己掌控;二是成本结构清晰,一台普通云主机就能扛住不小的并发,没有按流量计费那种越用越心虚的账单。很多团队初期选择云厂商的Live服务,等量起来之后发现账单压力,又迁移回自建SRS,我见过不止一次这种情况。

但SRS的问题也明显——它的部署和调优有一定门槛。官方提供了源码编译方式,依赖库一堆,编译时间动不动半小时,而且SRS迭代很快,不同版本之间配置格式有差异,新手照着网上教程操作经常卡在版本对不上。这时候Docker的价值就体现出来了。

1.2 Docker部署和源码编译的真实差距

我最早接触SRS是在一台2核4G的云服务器上,那时候还是SRS 3.0时代,老老实实跟着官方Wiki走源码编译。CentOS 7环境下要先装gcc、g++、make、patch、unzip、python3这些基础工具,然后拉源码、执行./configure,等待编译的过程里CPU直接拉满,服务器上跑着的其他服务都跟着遭殃。编译出错还要根据报错反推缺了什么系统库,装完之后再重来一遍,那个痛苦我现在还记得。

换成Docker之后完全是另一套体验。基础镜像里已经把运行环境和依赖打包好了,拉下来直接跑,镜像本身只有几十MB级别,跟编译一顿操作后占用的磁盘相比反而更轻量。更重要的是容器隔离特性让清理变得极其简单,想升版本或者换配置,删掉容器重建就行,宿主机不会被搞乱。

版本管理方面Docker的优势更明显。SRS从4.0到5.0,配置项变化不小,很多人拿4.0的教程去配5.0的容器,结果启动报错或者配置不生效。Docker镜像通过tag锁定版本,比如说 ossrs/srs:5 ,后续升级只需要换个tag重新拉取,回滚也方便,再也不用为了版本问题头疼。

注意:SRS官方镜像地址是 ossrs/srs ,Docker Hub直接拉就行。如果服务器在国内,拉取Docker Hub镜像经常超时,建议提前配好镜像加速器,具体怎么配后面会详细说。

2. 环境准备和镜像拉取:正式操作前的关键分叉点

2.1 宿主机选型和基础条件

跑SRS的机器要求不高,但也不能太寒酸。以我个人的实测经验,一台1核1G的云主机能跑起来,但并发拉流超过几十路时CPU会吃紧,最起码也要2核4G起步。操作系统方面Ubuntu 20.04、22.04,CentOS 7、8都可以,核心是能装Docker,系统本身是什么其实无所谓。硬盘建议单独挂一块数据盘给录制文件和历史切片用,如果只是做实时转发,系统盘也就够了。

Docker安装这里有一个新手特别容易踩的坑:直接用系统自带的旧版本Docker。Ubuntu 20.04的apt源里默认带一个docker.io,版本老、更新麻烦,强烈建议用官方脚本装新版:

curl -fsSL https://get.docker.com | bash -s docker
systemctl enable --now docker
docker version

装完之后确认Client和Server都在,尤其要看Server的版本,有些机器之前装过旧版,环境变量PATH指向旧的可执行文件,导致命令操作的是老客户端、连接的却是新守护进程,这种隐藏问题最磨人。

2.2 镜像版本到底选哪个

SRS官方镜像在Docker Hub上的tag分两大类:一类是像 4.0-r0 、 5.0-r2 这种具体版本号,一类是 4 、 5 这种主版本号指向最新release。我的建议是生产环境别追最新,锁定主版本即可,比如用 ossrs/srs:5 。主版本号不会频繁变动,但会持续获得该版本线的bug修复和安全更新,比锁定小版本更实用。

这里要特别提醒:如果你翻到的教程还是让拉 ossrs/srs:3 甚至 ossrs/srs:2 ,建议果断放弃。SRS 3.0时代的Docker镜像已经很久不更新,而且4.0之后C++重构了核心模块,性能和稳定性提升非常明显。既然选择Docker就是为了省心,没必要折腾老古董。

2.3 拉取镜像和启动容器的完整命令

配置好镜像加速之后,拉镜像这一步基本不会出幺蛾子。启动一个最基础的SRS容器,官方最简单的方式是:

docker run --rm -p 1935:1935 -p 1985:1985 -p 8080:8080 \
  -v /home/srs/config:/usr/local/srs/conf \
  registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5

但我实际用下来很不推荐直接这样裸跑。几个问题: --rm 参数在容器异常退出时直接清理日志,排查问题没现场;没有 --restart 策略,服务器重启后SRS不会自动拉起;配置目录挂载了宿主机路径,可是还没有提前准备好配置文件,SRS会使用容器内默认配置,容易造成"我改了宿主机配置但没生效"的错觉。

我更建议第一步先把容器跑起来,用默认配置验证端口和服务是否正常,确认无误后再深入改配置。具体操作为:

docker run -d --name srs \
  -p 1935:1935 \
  -p 1985:1985 \
  -p 8080:8080 \
  --restart always \
  registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5

启动后用 docker ps 确认容器状态是Up,再访问 http://服务器IP:8080 ,看到SRS欢迎页就算第一步走通了。这一步值得多说一句:先别急着改任何配置,把最简链路跑通,后面出了问题就能区分是环境问题还是配置问题,排查范围小很多。

3. 推拉流链路逐个打通:RTMP、HTTP-FLV、HLS、WebRTC

3.1 RTMP推流:用FFmpeg把视频送进SRS

容器起来了,接下来最激动人心的就是测试推流能不能通。SRS默认配置里已经内置了一个叫 live 的应用,推流地址格式是 rtmp://服务器IP/live/流名称 。

准备一台有FFmpeg的电脑,拿一个MP4文件或者摄像头设备推流,命令长这样:

ffmpeg -re -stream_loop -1 -i /path/to/video.mp4 \
  -c copy -f flv rtmp://服务器IP/live/livestream

几秒钟之后命令行没有报错,说明RTMP推流已经建立。这时候打开VLC媒体播放器,选择"打开网络串流",填 rtmp://服务器IP/live/livestream ,如果能流畅播放,恭喜你,最核心的一条链路通了。

这里有一个细节值得注意: -re 参数指示FFmpeg按视频原始帧率推送,不加速也不丢帧; -stream_loop -1 是让视频循环推流,方便长时间测试。如果不加 -re ,FFmpeg会把所有帧一股脑推过去,SRS端接收到的数据是不符合真实时间线的,播放端会以极快的速度播完整个视频,初学者很容易被这个现象搞晕。

3.2 HTTP-FLV和HLS播放:浏览器直接看的两种方式

RTMP是当年Flash时代的标准,现在浏览器原生已经不支持了。要让网页端直接看直播,最常用的两种方案是HTTP-FLV和HLS。

HTTP-FLV是SRS默认就支持的,播放地址就是推流地址换个协议前缀:

http://服务器IP:8080/live/livestream.flv

用VLC或者网页里引入 flv.js 就能播放。HTTP-FLV的延迟可以做到1到3秒,比HLS低得多,适合互动直播、在线课堂这类对实时性有要求的场景。但它是长连接流式传输,对于视频平台这类大规模分发场景不够灵活,还得靠HLS。

HLS是苹果推出来的协议,原理是把视频切成一个个几秒长的TS切片,通过m3u8索引文件播放。它的延迟天然比FLV高,通常5到10秒,但好处是兼容性极好,iOS、Android、Web端都能原生播放,不需要额外插件。SRS对HLS的支持默认是关闭的,需要自己开启。修改SRS配置,找到 vhost __defaultVhost__ 节点,启用hls模块:

vhost __defaultVhost__ {
    hls {
        enabled         on;
        hls_path        ./objs/nginx/html;
        hls_fragment    5;
        hls_window      30;
    }
}

改完配置重启容器。之后推送一路RTMP流,几秒钟后访问:

http://服务器IP:8080/live/livestream.m3u8

就能看到HLS切片生成。这里需要说明: hls_path 配置了切片的存放位置,如果是Docker启动时没有挂载数据卷,切片会写在容器内部,容器删除数据就丢了。生产环境务必把HLS输出目录挂载到宿主机。

3.3 WebRTC低延迟链路:SRS 5的招牌功能

如果说RTMP、HTTP-FLV、HLS是传统的三件套,那WebRTC就是SRS 5主推的新物种。WebRTC能把延迟压到500毫秒以内,而且浏览器原生支持,不需要装任何插件,是视频会议、在线连麦、远程协作这类强互动场景的标配。

SRS启用WebRTC的配置比HLS复杂一些,因为它涉及UDP端口段、Candidate地址配置。我直接贴一份能跑通的精简配置:

rtc_server {
    enabled on;
    # 设置UDP监听端口范围,注意这里不是单端口
    listen 8000;
    # 对外通告的IP,必须改成服务器实际公网IP或内网IP
    candidate $CANDIDATE;
}

启动容器时要把UDP端口段也映射出来:

docker run -d --name srs \
  -p 1935:1935 \
  -p 1985:1985 \
  -p 8080:8080 \
  -p 8000:8000/udp \
  -p 8001:8001/udp \
  -p 8002:8002/udp \
  -e CANDIDATE=你的IP \
  --restart always \
  registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5

WebRTC测试页面SRS自带,访问 http://服务器IP:8080/players/webrtc.html 即可。这里最大的坑是 candidate 配置,SRS需要把UDP端口和可被客户端访问的IP地址告诉对方,如果机器在NAT后面或者有多个网卡,IP填错就会导致连接建立失败,而且失败现象是"一直在connecting",非常误导人。

4. 端口规划和容器网络:最容易安装时被忽略、出问题最多的地方

4.1 一张表讲清SRS要暴露哪些端口

SRS本身涉及的端口不多,但每个都有自己的位置,搞混了排查问题成本很高。我整理了一张对照表:

端口 协议 用途 说明
1935 TCP RTMP推流/拉流 核心端口,必开
1985 TCP HTTP API接口 管理接口、回调、统计信息都走这里
8080 TCP HTTP播放和Web管理 HTTP-FLV、HLS、WebRTC播放器页面都从这里出
8000-8003 UDP WebRTC媒体传输 需要按需开启,每个流会占用端口对

Docker的 -p 参数格式是 宿主机端口:容器端口 ,我建议保持两边一致,否则排查时还要来回换算很麻烦。比如 8080:8080 就是让宿主机的8080直接映射到容器的8080。

4.2 防火墙和云安全组的双重配置

端口映射配好了,容器起来了,但外面还是访问不了,这个问题十有八九出在防火墙或者云安全组。

云服务器的安全组是最容易漏的一层。阿里云、腾讯云这些平台默认只放通22、80、443端口,即便Docker端口映射做对了,安全组不放行,外部流量一样进不来。所以购买云服务器后第一件事,就是到控制台的安全组规则里把1935、1985、8080、8000-8003全部放行。

服务器本身的防火墙也要注意。CentOS 7以上默认用firewalld,Ubuntu用ufw。我自己习惯直接把SRS相关端口加入白名单,避免影响业务:

# 以CentOS firewalld为例
firewall-cmd --permanent --add-port=1935/tcp
firewall-cmd --permanent --add-port=1985/tcp
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=8000-8003/udp
firewall-cmd --reload

排查顺序建议是先从本机 curl http://127.0.0.1:8080 验证SRS服务本身正常,然后用另一台机器 telnet 服务器IP 8080 验证端口通不通,最后才考虑推流测试。这个顺序能帮你把问题快速定位到服务、网络、配置三个层面的某一层。

4.3 用Docker Compose固定环境和网络

docker run 命令在容器少的时候够用,但等SRS要挂载多个数据卷、配置多个环境变量、再旁边跑一个Nginx、一个录制服务的时候,命令变得越来越长,一不小心还容易敲错。这时候换成Docker Compose更合适,配置文件版本化管理,换机器部署也方便。

下面是一个生产可用的 docker-compose.yml 示例:

version: "3.8"

services:
  srs:
    image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
    container_name: srs
    restart: always
    ports:
      - "1935:1935"
      - "1985:1985"
      - "8080:8080"
      - "8000-8003:8000-8003/udp"
    environment:
      - CANDIDATE=${SRS_IP}
    volumes:
      - ./conf/srs.conf:/usr/local/srs/conf/srs.conf
      - ./logs:/usr/local/srs/objs/logs
      - ./hls:/usr/local/srs/objs/nginx/html
    mem_limit: 1g
    cpus: "1.0"

用Compose启动只需要 docker-compose up -d ,停止是 docker-compose down ,配置变更时修改yaml然后重新up。这个模式能让你把SRS和周边组件一起编排,后面要加Redis做鉴权缓存、加Filebeat采集日志,都可以往同一个Compose文件里扩展。

5. 配置文件的常用改动:日志、鉴权、转封装和跨域

5.1 日志级别和日志轮转

SRS默认日志输出到控制台,容器方式下会进入docker logs。默认级别是 info ,在排查问题的时候信息量不够,我习惯调到 debug 先看全貌,确认问题后调回 info 。生产环境不建议长期开 debug ,日志增长速度非常快,一两天就能吃掉几个G。

SRS日志配置在 srs.conf 的全局段:

srs_log_tank file;
srs_log_file ./objs/logs/srs.log;
srs_log_level info;
srs_log_rotate 3600;

srs_log_tank file 表示写文件, srs_log_rotate 3600 是每小时轮转一次日志。给日志单独挂载数据卷之后,配合宿主机的logrotate或者采集工具,日志管理就规整多了。

5.2 HTTP回调鉴权怎么加

流媒体服务直接裸奔肯定不行,尤其推流地址一旦被别人猜到,任何人都能往你的服务器推垃圾流。SRS提供了HTTP回调机制,在推流、播放、断流等事件发生时向业务服务器发HTTP请求,由业务方决定是否允许操作。

配置如下,在vhost节点里增加:

vhost __defaultVhost__ {
    http_hooks {
        enabled         on;
        on_publish      http://你的业务服务器/api/auth/publish;
        on_play         http://你的业务服务器/api/auth/play;
        on_unpublish    http://你的业务服务器/api/auth/unpublish;
    }
}

SRS向回调地址POST一个JSON,包含 action 、 client_id 、 ip 、 vhost 、 app 、 stream 这些字段。业务服务器收到后,根据流名称、IP、token等信息判断是否合法,返回 0 表示允许,返回非0表示拒绝。这个机制的最大优点是鉴权逻辑完全在业务侧,SRS本身不存用户数据,灵活度很高。

5.3 CORS跨域问题的处理

很多人把播放器页面和SRS放在不同域名下,结果浏览器控制台报CORS跨域错误。SRS不直接提供跨域配置项,官方推荐的做法是前置一层Nginx处理跨域头,或者让播放器请求API的时候走代理。

我自己的做法是给SRS前面加一个Nginx反代,把 /live/ 路径转发到SRS的8080端口,同时在Nginx层统一加跨域头:

location /live/ {
    proxy_pass http://127.0.0.1:8080/live/;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers '*';
}

这样不仅解决了跨域,也方便统一管理HTTPS证书。毕竟浏览器播放HTTP-FLV和WebRTC,在非local环境下都要求HTTPS,证书配置放在Nginx层比塞进SRS容器里清爽得多。

5.4 SRT等其他协议的启用

除了前面讲的RTMP、HLS、FLV、WebRTC,SRS还支持SRT、GB28181等协议。SRT是当前弱网传输的热门方案,特别适合跨地域的网络传输,官方镜像默认编译了SRT支持,需要在配置里显式开启:

srt_server {
    enabled on;
    listen 10080;
}

启动容器时映射对应端口即可。对于需要接入海康、大华等安防摄像头的项目,SRS还通过GB28181协议支持国标设备的接入和转推,不过这个配置相对复杂,涉及SIP信令服务,这里不展开,等有空再单独写一篇。

6. 集群扩展和进阶玩法:一台机器不够了怎么办

6.1 Origin + Edge 模式扛住更多拉流并发

单台SRS节点性能再强也有天花板,当拉流并发上涨时,一个常见方案是Origin + Edge集群。Origin节点负责接收推流、处理转码等耗资源的任务,Edge节点负责对外提供拉流服务,可以在前面挂多台Edge横向扩展。Edge和Origin之间通过源站回源协议交互,对播放端来说只是多一次跳转,延迟增加可以忽略。

配置上,Origin节点保持默认行为即可,Edge节点需要在配置里指定源站地址:

vhost __defaultVhost__ {
    mode remote;
    origin 192.168.1.10:1935;
}

Edge启动后,播放端访问的是Edge节点的端口,Edge实时从Origin拉取流再分发给播放端。这种架构的好处是推流链路和播放链路解耦,推流稳定性不受播放量波动影响。实际部署时Edge可以按需扩容,用前面的Docker Compose配合多台机器,效果很好。

6.2 与CDN配合的使用思路

自建SRS并不排斥CDN。常见做法是让SRS作为源站,CDN节点回源到SRS拉流,再由CDN节点把流分发到全国各地。这种混合架构下,SRS只需要承担一路来自CDN回源的请求,压力很小,而播放端从最近的CDN节点取流,体验远好于直接连源站。

实现方式是在CDN平台配置回源地址为SRS的HTTP-FLV或HLS地址。需要注意SRS源站必须有公网可访问的地址,如果机器在NAT后面,还要做好端口映射或者使用Nginx反代。这个方案的运维复杂度比纯自建高一点,但换来的是用户播放体验的大幅改善,尤其在运营商网络环境下。

6.3 容器资源限制和监控

Docker容器默认不限制资源,一个SRS进程可能吃掉宿主机全部内存。我自己的习惯是给每个容器都设置资源上限,防止某个异常流量把机器拖垮:

deploy:
  resources:
    limits:
      cpus: "1.0"
      memory: 1G

监控方面,SRS自带HTTP API接口,可以返回当前连接数、带宽占用、推拉流详情,比如:

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

这个接口返回的是一个JSON,包含 clients 、 streams 等字段。配合Prometheus等监控工具定时采集,可以在连接数异常飙升时提前收到告警,而不是等用户投诉了才发现问题。

7. 排错实战:我在部署过程遇到的几个典型问题和排查链路

7.1 容器正常启动但VLC播放黑屏

有一次我在新服务器上部署,容器状态正常,API接口能访问,FFmpeg推流也没有报错,但VLC打开RTMP地址就是黑屏,也不缓冲。排查链路是这样的:先用 docker logs 看SRS日志,发现推流事件已经记录,说明推流链路没问题。再看进程状态, docker stats 显示CPU和内存占用都很低,进一步判断问题可能在播放端到服务器的网络路径上。

用 telnet 服务器IP 1935 测试,发现TCP端口都能通,但RTMP协议层面一直没响应。后来打开云服务商控制台才发现,安全组里虽然放行了1935端口,但放行的源地址 0.0.0.0/0 被之前配置的其他规则优先拦截了。安全组规则的生效顺序是从前往后匹配,一旦前面有拒绝规则,后面的放行就不会生效。调整规则顺序后问题解决。

这个案例给我的收获是:排查网络问题时,先把安全组、防火墙、端口监听三层全部过一遍,不要想当然认为"端口通了服务就正常"。

7.2 WebRTC拉流一直connecting

WebRTC的坑比RTMP多一个数量级。之前配好RTMP之后继续配WebRTC,页面打开后一直处于connecting状态,等了十几秒也连不上。先怀疑UDP端口没映射,检查之后发现8000-8003都映射了。再看日志,发现SRS打印了一段 candidate 信息,实际通告给客户端的IP不是公网IP而是内网IP。

问题根源是容器里的SRS拿到的是容器虚拟网卡的IP,不是宿主机的公网IP。而 candidate $CANDIDATE 这个配置并没有正确读取环境变量,导致SRS认为自己的IP是不对外的内网地址。修正方法是在启动命令里显式传入环境变量,或者在配置里直接写死公网IP。同时还要确认UDP端口8000-8003在安全组里全部放行了,少一个数字都连不上。

7.3 容器重启后IP变化导致转发失效

在Kubernetes或Docker网络环境里,容器重启后IP会变化,这一点在配置Edge回源时非常坑。有一回我配置好Edge节点指向Origin的容器IP,跑了一天后Origin容器重启,IP变了,Edge全部拉流失败。

排查时一度以为Edge配置被覆盖了,后来才发现根因是容器IP不固定。解决办法有几个:最直接的方案是在docker-compose里给Origin和Edge都配置静态IP,属于同一个自定义网络内固定下来;也可以给服务加DNS别名,用服务名互相访问;如果只有一台机器跑多个容器,直接让Edge使用宿主机IP访问Origin的映射端口,也能规避IP变化问题。

这三个问题看起来各不相同,但有一个共同点:SRS本身的核心逻辑没问题,出问题的地方都在网络规划和配置细节。而Docker部署方式能帮你在排查时快速定位问题边界——先确认容器内部服务是否正常,再逐层向外排查网络,思路清晰了,问题就不难解决。

Logo

邀请您加入社区

更多推荐