ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用Docker快速部署SRS流媒体服务器实战

用Docker快速部署SRS流媒体服务器实战 做视频服务的同学应该都听过 SRSSimple Realtime Server江湖人称“世界上最简单的流媒体服务器”。我大概在四五年前第一次接触到它当时为了给一个在线课堂做直播分发对比过 Nginx-RTMP、ZLMediaKit、MediaMTX 等一堆方案最后落地的还是 SRS。为什么因为它在“开箱即用”和“功能强大”之间找到了一个非常舒服的平衡点——既能像玩具一样三分钟跑起来又能扛住生产环境的并发和复杂协议需求。而这两年再把 SRS 和 Docker 组合起来部署难度直接降到“一条命令服务就起来”的地步连编译的功夫都省了。这篇文章不搞那些花里胡哨的包装直接把我的实操过程、配置细节、踩坑记录全部摊开。无论你是刚入门想搭一个测试环境还是准备在生产环境里切一套真正的流媒体服务照着往下做都能得到一个能跑、能推流、能播放、能查问题的完整 SRS 实例。废话不多说先从选型和原理聊起知道了“为什么”后面的“怎么做”才有底气。在开始之前先说一下我的运行环境后面所有命令和配置我都验证过一台 Ubuntu 22.04 服务器内核 5.15Docker 版本 24.0Docker Compose v2.20。SRS 镜像用的是官方ossrs/srs:v5.0这是目前最稳的 5.x 系列版本。1. 为什么选 SRS Docker先搞懂这套组合到底解决了什么1.1 SRS 的核心能力和适用场景SRS 是一个用 C 写的高度模块化流媒体服务器它最核心的定位就是“接入流、转发流、分发流”。它天生支持 RTMP 推流能输出 RTMP、HTTP-FLV、HLS、WebRTC、SRT 等多种拉流协议所以你在浏览器里看到的低延迟直播在手机上用微信扫码看的 HLS 视频在 OBS 里推出去的 RTMP 流都可以由一个 SRS 实例承担。这种多协议聚合能力是它在同类开源方案里脱颖而出的关键原因。从我的实际项目经验来看SRS 最适合这几类场景一是低延迟直播比如在线教育、电商带货、视频会议补充链路WebRTC 的延迟可以压到 200~500 毫秒二是移动端兼容性要求高的场景HLS 是 Apple 生态的亲儿子几乎所有手机浏览器都能直接播三是企业内部做视频分发SRS 的单机性能足够支撑几千路并发观看而且社区活跃、文档齐全真出了问题也不愁找不到人讨论。1.2 为什么用 Docker 而不是直接编译部署SRS 早期最劝退新手的就是编译安装。它依赖 OpenSSL、FFmpeg、st 库等一堆组件不同系统上编译还能遇到各种神奇的链接错误。我记得第一次在 CentOS 7 上编译 SRS 4.0光./configure的参数就研究了半天中间还因为 GCC 版本太旧导致编译失败。后来虽然官方提供了 release 压缩包二进制包在 GLIBC 版本不同的机器上又可能跑不起来。Docker 完美解决了这个痛点。所有依赖、动态库、可执行文件都被封装在镜像里宿主机只需要有一个 Docker 环境一条docker run命令就完成了“编译安装启动”全过程。而且版本升级特别干净旧容器直接删掉新容器拉起来配置和数据通过挂载目录保留不会有残留文件污染系统。对于团队协作来说一个 docker-compose 文件提交到 Git 仓库新同事克隆下来一键启动开发环境和生产环境保持一致这类隐性收益在排障时尤其明显。1.3 使用 Docker 部署时的注意事项Docker 不是万能的用容器跑 SRS 有一些细节必须提前知道。首先是网络模式SRS 涉及 UDP 端口WebRTC 使用Docker 默认的桥接网络是 NAT 模式需要手动映射 UDP 端口如果漏掉就会导致 WebRTC 无法推拉流。其次是性能问题虽然容器本身几乎不损耗性能但如果宿主机网络层配置不当比如未开启内核调优参数高并发下 UDP 消息处理会有丢包风险。另外SRS 的配置文件路径在/usr/local/srs/conf/srs.conf容器使用时一般不会去改镜像内的文件而是把宿主机上的配置目录挂载进去。这样做的目的是让配置“可编排、可追踪”不会因为容器删除配置就丢失。初始化镜像时官方也提供环境变量覆盖一些简单参数但复杂场景还是建议直接用挂载的配置文件后面我也会演示具体做法。2. 部署前的准备给 SRS 腾好“房子”2.1 Docker 环境检查与安装如果你还没有安装 Docker先花两分钟把基础环境准备好。我推荐使用 Docker 官方安装脚本或者使用 apt 仓库安装。这里假设你用的是 Debian/Ubuntu 系先更新软件包索引然后安装apt-transport-https、ca-certificates等依赖添加 Docker 官方 GPG 密钥和仓库源最后安装 docker-ce 和 docker-compose-plugin。装完后用docker version验证客户端与服务端都在正常工作。我自己踩过的一个坑是刚装完 Docker 后需要在 root 用户下才能执行 docker 命令普通用户会报权限不足。解决办法很简单把当前用户加入 docker 用户组然后重新登录即可sudo usermod -aG docker $USER newgrp docker之后执行docker run hello-world能看到“Hello from Docker!”就说明环境完全 OK。这里额外提醒一句如果服务器前面还有防火墙UFW 或者云厂商安全组要记得提前把 SRS 需要的端口放行不然后面推流失败会排查得怀疑人生。2.2 选择什么版本的 SRS 镜像SRS 官方镜像仓库是 Docker Hub 上的ossrs/srs最新的稳定大版本是 5.x也有 4.0 版本在继续维护。我自己在生产环境用的是ossrs/srs:v5.0因为 SRS 5 改进了 WebRTC over TCP 的支持而且增强了鉴权和集群功能。如果你追求绝对稳定也可以用ossrs/srs:v4.0它是经过多年验证的“老黄牛”社区案例最多。有一个细节SRS 镜像的 tag 分为v5.0、v5.0.xx这类精确版本以及v5、latest这类浮动版本。我建议线上环境锁死精确版本比如ossrs/srs:v5.0.142避免哪天拉取最新版后行为发生变化。开发环境可以用ossrs/srs:v5方便测试。你可以在 Docker Hub 页面查看可用的 tag 列表选择适合自己时间节点的版本。2.3 端口规划和目录结构SRS 默认会监听好几类端口部署前最好统一规划特别是出现端口冲突的时候你才知道为什么要这样分配。端口协议用途1935TCPRTMP 推流/拉流1985TCPHTTP API、WebRTC 信令8080TCPHTTP 服务、HTTP-FLV、HLS、控制台8000UDPWebRTC 媒体面SRTP8001UDPWebRTC 媒体面备用3868TCPSRT 推流可选8085TCPHTTPS 服务可选目录方面我会单独创建/opt/srs/作为 SRS 的宿主机目录下面分conf、logs、www三个子目录。conf 放自定义的srs.conflogs 挂载给容器里的日志目录www 用来存放 HLS 切片和网页文件。这样数据与容器生命周期解耦容器怎么折腾都不会丢数据。3. 开始部署两种方式从快速验证到生产固化3.1 第一种方式一分钟拉起默认配置的 SRS如果你的目标只是“先跑起来看看”那什么都不用配置直接执行下面的命令docker run --rm -d \ --name srs \ -p 1935:1935 \ -p 1985:1985 \ -p 8080:8080 \ -p 8000:8000/udp \ -p 8001:8001/udp \ ossrs/srs:v5.0这里的参数我解释一下-d表示后台运行--rm表示容器停止后自动删除文件系统层适合测试。-p把宿主机端口映射到容器端口注意 WebRTC 用的是 UDP所以必须写成8000:8000/udp这样的格式漏掉 /udp 或者漏映射端口后面 WebRTC 保准抓瞎。启动后可以验证一下容器状态docker ps看到srs容器处于 Up 状态后访问http://服务器IP:8080/会看到 SRS 自带的欢迎页和测试播放器。此时 SRS 已经具备完整的直播能力默认配置启用了 RTMP 接入、HTTP-FLV、HLS、WebRTC 分发。你可以直接打开它的演示页面http://服务器IP:8080/players/srs_player.html里面会自动生成一个内置的流地址测试播放。3.2 第二种方式挂载自定义配置精确控制默认配置适合体验但真要接入自己的业务就必须使用自定义配置。首先创建宿主机目录和配置文件mkdir -p /opt/srs/{conf,logs,www}然后编辑/opt/srs/conf/srs.conf我贴一份精简但带核心功能的配置。注意SRS 配置格式类似 nginx每条指令以分号结尾遇到不认识的指令是会导致启动失败的listen 1935; max_connections 1000; daemon off; srs_log_tank console; http_api { enabled on; listen 1985; } http_server { enabled on; listen 8080; dir /usr/local/srs/objs/nginx/html; } rtc_server { enabled on; listen 8000; protocol udp; # 多 IP 时建议显式指定候选 IP candidate $CANDIDATE; } vhost __defaultVhost__ { tcp_nodelay on; min_latency on; play { gop_cache on; queue_length 10; } hls { enabled on; hls_path /usr/local/srs/objs/nginx/html; hls_fragment 2; hls_window 10; } http_remux { enabled on; mount [vhost]/[app]/[stream].flv; } rtc { enabled on; } }这里有几个地方需要解释都是新手容易踩的坑daemon off必须保留因为在 Docker 容器里进程必须是前台运行的如果以 daemon 模式启动容器启动后立刻就会退出。srs_log_tank console让日志输出到标准输出这样可以用docker logs srs直接查看非常方便调错。http_server.dir和hls_path默认指向容器内的/usr/local/srs/objs/nginx/html这个目录是镜像自带的静态文件目录。如果你想把 HLS 切片输出到宿主机目录这里应该改为容器内的挂载点路径比如/data/www然后在docker run时把宿主机/opt/srs/www挂载到/data/www。WebRTC 的candidate是一个容易忽视的参数。在云服务器上跑的时候SRS 需要识别本机的外网 IP这时可以把它显式配成candidate 服务器公网IP否则客户端可能拿不到正确的 ICE 候选地址。我在下面的启动命令里用了环境变量CANDIDATE来动态传入。准备好配置后用挂载方式启动容器docker run --rm -d \ --name srs \ -e CANDIDATE$(hostname -I | awk {print $1}) \ -v /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf \ -v /opt/srs/www:/data/www \ -v /opt/srs/logs:/usr/local/srs/objs/logs \ -p 1935:1935 \ -p 1985:1985 \ -p 8080:8080 \ -p 8000:8000/udp \ -p 8001:8001/udp \ ossrs/srs:v5.0这里我把自定义配置挂载到了容器内 SRS 的默认配置文件路径把宿主机/opt/srs/www挂载到/data/www但配置里还没改目录你切换到 HLS 时注意同步。如果你想正式使用 HLS需要把上面配置里的http_server.dir和hls_path都改为/data/www。这种“配置外置、日志外置、切片外置”的方式是我在多次生产事故后沉淀下来的标准做法升级镜像时只要docker rm旧容器再拉起新容器所有数据依然保留。3.3 使用 docker-compose 固化部署单条 docker run 命令在复制粘贴时容易漏参数而且团队成员不易对齐所以我强烈建议你用 docker-compose。在/opt/srs/下创建docker-compose.ymlversion: 3.8 services: srs: image: ossrs/srs:v5.0 container_name: srs restart: unless-stopped environment: - CANDIDATE${CANDIDATE} ports: - 1935:1935 - 1985:1985 - 8080:8080 - 8000:8000/udp - 8001:8001/udp volumes: - /opt/srs/conf/srs.conf:/usr/local/srs/conf/srs.conf - /opt/srs/www:/data/www - /opt/srs/logs:/usr/local/srs/objs/logs然后提前在/opt/srs下创建一个.env文件CANDIDATE你的服务器公网IP最后一句命令就能启动整套服务cd /opt/srs docker compose up -drestart: unless-stopped是生产环境的关键配置服务器重启后 Docker 会自动把 SRS 拉起来省去手动干预。日志查看方式也和单容器一致用docker compose logs -f srs即可。这种管理方式非常符合现代运维规范你的“部署脚本”就是这一个 yml 文件和一份 conf可审计、可回滚、可复制。4. 测试闭环推流、拉流、WebRTC 全链路验证4.1 用 FFmpeg 推流到 SRS服务跑起来后第一步就是验证推流是否正常。先在服务器上准备一个小视频文件作为测试源没有现成素材的话可以直接用 FFmpeg 的 testsrc 源生成。安装 FFmpegapt install ffmpeg -y生成一个 5 秒的测试视频或使用本地准备好的 mp4ffmpeg -f lavfi -i testsrcsize1280x720:rate30 -f lavfi -i sinefrequency440:sample_rate44100 -t 30 -c:v libx264 -preset ultrafast -c:a aac /tmp/test.mp4然后执行推流命令将视频推送到 SRS 的 RTMP 端口ffmpeg -re -i /tmp/test.mp4 -c copy -f flv rtmp://127.0.0.1/live/livestream如果终端没有任何报错持续滚动音视频帧信息说明 RTMP 推流成功。这里的/live是应用名livestream是流名你可以在拉流地址里自由替换。我遇到过的常见问题就是推流命令报Connection refused这基本是端口映射或防火墙问题等下第 5 节统一排查。如果报Failed to update header或者Broken pipe则一般是 SRS 那边主动断开了连接最常见的原因是带宽不足或者推流时间过长超过了max_connections。4.2 拉流验证HTTP-FLV 与 HLS推流成功以后我们的注意力转到“播放”。SRS 最有价值的地方就是可以同一条流用多种协议拉取。HTTP-FLV 是浏览器低延迟播放的首选地址规则是http://服务器IP:8080/live/livestream.flv把这条地址直接塞到 VLC 的“打开网络串流”里或者浏览器中用 flv.js 播放立刻就能看到画面。HLS 则把 RTMP 流转成 m3u8 切片地址规则是http://服务器IP:8080/live/livestream.m3u8用 VLC 打开也能播放。HLS 是分段传输的所以会先缓冲几秒这是 CDN 时代的折中方案延迟比 FLV 高但兼容性最好。验证时我还习惯在 SRS 的 API 里查一下流状态。直接访问 HTTP APIcurl http://127.0.0.1:1985/api/v1/streams返回值是一串 JSON里面有流名、协议连接数、创建时间等信息。看到里面有name: livestream这样的记录说明流已经被 SRS 登记在册了。这个 API 在排查“为什么播放器连不上”时非常好使。4.3 打开浏览器体验 WebRTC 亚秒级延迟SRS 5.x 默认启用了 WebRTC 推拉流支持这也是它区别于传统 RTMP 服务器的最大亮点。我们用浏览器测试一下 WebRTC 拉流在服务器上打开 SRS 自带的播放器页面。http://服务器IP:8080/players/rtc_player.html在页面地址栏输入webrtc://服务器IP/live/livestream注意协议名是 webrtc点击播放。WebRTC 播放延迟通常在 500ms 以内画质和流畅度和浏览器原生能力直接挂钩。前提是 8000/8001 的 UDP 端口必须放行并且我们将 SRS 的 candidate 配置成了正确的公网 IP否则浏览器和 SRS 之间的 ICE 协商会失败。如果你想测试 WebRTC 的推流可以用rtc_publisher.html页面它会调用浏览器麦克风或者摄像头并把流推到 SRS。这些年 WebRTC 在直播里越来越重要很多互动连麦场景都已经从 RTMP 转向 WebRTCSRS 的这个能力绝对值得上手验证。4.4 通过 API 检查服务器健康状态最后再推荐一个实用 API/api/v1/versions可以查看 SRS 版本和编译选项/api/v1/summaries可以看当前的连接数、带宽、CPU 占用等统计信息。这两个接口是我写监控脚本时的主力接口比如定时抓取 summaries 数据当并发连接数超过阈值就告警。作为运营指标SRS 比很多商业流媒体服务透明得多。5. 部署过程中最常见的坑与排查方法5.1 端口映射或防火墙导致的推流失败症状FFmpeg 推到rtmp://服务器IP/live/livestream时提示连接超时或被拒绝。排查顺序建议是先在服务器本机执行docker ps确认容器在运行再在本机执行curl http://127.0.0.1:1985/api/v1/versions确认 API 能通。如果本机通、外网不通问题大概率出在防火墙或者云安全组。别忘了检查 Docker 端口映射是否完整docker port srs这会列出所有已映射端口。我之前就有过一次“只映射了 TCP 端口忘记映射 8000/udp”的经历导致 WebRTC 一直连不上而用 RTMP 推流却完全正常排查了快一个小时才想到是 UDP 映射缺失。5.2 播放黑屏或卡顿的排查如果推流正常API 也查得到流但播放黑屏首先要区分是协议兼容性问题还是转封装问题。比如用 VLC 播 HTTP-FLV 卡顿可以换个播放方案测试用浏览器 flv.js 对比如果浏览器正常LVC 异常多半是 VLC 对 FLV 的缓冲设置不够灵活。如果是 HLS 播放延迟很大可以调整配置里的hls_fragment和hls_window。我一般把 fragment 设为 2 秒window 设为 10 秒在延迟和文件数量之间取个平衡。还有一种情况是 GOP 缓存导致首屏慢。默认gop_cache on播放器接入时会从关键帧开始缓存好处是秒开坏处是延迟增加如果对延迟极其敏感可以把gop_cache off但代价是首屏可能等很久。5.3 看日志是定位问题最快的方式SRS 的日志格式很规范直接查看容器日志docker logs -f --tail 200 srs日志里能看到 RTMP 连接建立、流发布、流播放、WebRTC 会话建立等事件。比如以下这行[2025-xx-xx 10:00:00.000] [info] RTMP client ip1.2.3.4:12345 accept, fd16说明有 RTMP 客户端连进来了。如果日志出现error级别关键字比如invalid config那就直接定位到配置文件哪一行写错。SRS 有个特别贴心的设计配置出错时会打印出具体的指令和行号不像某些服务直接退出连个屁都不放。5.4 常见问题速查表现象可能原因解决办法容器启动后立刻退出配置里 daemon off 缺失确保daemon off; srs_log_tank console;RTMP 推流超时1935 端口未映射或防火墙未放行检查docker port放行 1935/TCPHTTP-FLV 可以播放WebRTC 失败8000/8001 UDP 未映射或 candidate 不对放行 UDP 端口、配置公网 IPAPI 返回 403启用了 HTTP API 鉴权但没配置 token检查 http_api 配置或临时关闭鉴权HLS 播放 404hls_path 与 http_server.dir 不一致让两者都指向同一个挂载目录播放卡顿延迟越来越大GOP 缓存或带宽不足调低质量、开启 gop_cache或增加队列长度这张表是我平时排查问题的第一参考基本上能覆盖 80% 的 SRS 部署难题。剩下那种“重启解决 99% 问题”的玄学情况docker restart srs也不妨一试。6. 进阶能力鉴权、HTTPS 与集群扩展6.1 用回调接口给 SRS 加上鉴权很多业务场景下你不能让任何人都能往你的服务器推流也不能让任何人都随意拉流。SRS 提供了 HTTP 回调机制在推流/播放等事件发生时通知你自己的后端服务由后端决定“放行”还是“拒绝”。配置一个简单的推流鉴权回调vhost __defaultVhost__ { http_hooks { enabled on; on_publish http://你的后端地址/api/auth/publish; on_play http://你的后端地址/api/auth/play; } }你的后端收到 POST 请求后返回0表示拒绝返回 HTTP 200 且 body 为整数0以外的值表示允许。更直观的方式是返回 JSON{code: 0}拒绝{code: 1}通过。这里要特别留意SRS 的 hook 请求是异步的它需要等待后端返回如果后端处理超时SRS 也会拒绝连接。所以回调接口的超时时间要尽量短。我建议在 on_publish 里校验推流密钥类似?tokenxxx这样的参数后端根据 token 判断是否为合法推流端。播放鉴权则可以根据用户 ID 和流名之间的关系判断访问权限。这套方案已经帮我扛住了多个线上活动的防盗链压力非常可靠。6.2 给 SRS 配置 HTTPS 和 443 端口随着浏览器对权限 API 的收紧获取摄像头、麦克风或者使用 WebRTC 都要求页面必须是 HTTPS 环境。SRS 自带 HTTP Server 可以加载 SSL 证书从而对外提供 HTTPS 拉流和 WebRTC 访问。准备证书文件可以是 Let‘s Encrypt 签发的免费证书挂载到容器里然后配置 HTTPShttp_server { enabled on; listen 8080; dir /usr/local/srs/objs/nginx/html; https { enabled on; listen 8085; key /usr/local/srs/conf/server.key; cert /usr/local/srs/conf/server.crt; } }启动时多映射一个 8085 端口。浏览器访问https://服务器IP:8085/players/rtc_player.html只要证书是合法的就不会再有混合内容的拦截问题。域名解析到服务器 IP 后你也可以用 Nginx 反代把 443 端口转发到 8085让最终对外地址更加标准。注意把证书文件放到/opt/srs/conf/下并挂载到容器内这样升级容器时证书不会丢。6.3 用 Origin Edge 集群模式扩展分发能力当单台 SRS 的带宽成为瓶颈时就可以上集群了。SRS 提供了 Origin Edge 的标准集群方案Origin 节点接收推流做转封装和存储Edge 节点分布在多个地域从 Origin 拉流后向终端用户提供服务。Origin 节点配置基本不变只需确保 Edge 能通过内部地址访问到 Origin 的 1935 端口。Edge 节点配置如下listen 1935; max_connections 1000; daemon off; srs_log_tank console; vhost __defaultVhost__ { mode edge; origin origin_node_ip:1935; rtc { enabled on; } }这样终端用户连接 Edge 节点时Edge 会向 Origin 请求同一路流并缓存转发。我当时做了一个模拟测试两个 Edge 节点同时拉取 Origin 上的一路 RTMP 流再各自分发 HTTP-FLV整体延迟增加不到 100ms效果符合预期。集群化后扩容就是“多启动一个 Edge 容器然后加入负载均衡”的事比在一台机器上不停压榨性能要优雅得多。6.4 其它值得关注的小功能SRS 还有几个很容易被忽略的好用功能一是内置的 HTTP 文件服务可以直接托管你的 Web 页面省掉额外 Nginx二是支持录制推上来的 RTMP 流可以直接存成 FLV 或 MP4 文件三是支持 SRT 推流某些采集设备和公网弱网环境下SRT 的稳定性比 RTMP 好很多。如果你把 SRS 做成“全能流媒体中枢”它的价值会进一步放大。写在最后一点实战体会这套 Docker SRS 的组合我在几台服务器上已经稳定跑了一两年。最大的体会是“容器化让流媒体服务器变成了一个可复制的标准件”换机器、升级版本都只是十几秒的事真正需要花心思的反而在业务侧比如鉴权策略、集群规划和网络调优。如果你打算在生产环境上正式使用我建议先从单节点配置回调鉴权开始把推流、播放、监控跑顺了再逐步引入集群和 HTTPS。不要一上来就搞大而全的架构流媒体服务的问题往往出在你不注意的细枝末节——一个防火墙规则、一个 UDP 端口漏映射都可能折腾你一晚上。最后送各位一句话把 SRS 当工具但别只当工具多看看它的设计思路很多直播系统的架构难题都能从中找到灵感。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进