ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

Docker部署SRS流媒体服务器:从零搭建直播与低延迟分发平台

Docker部署SRS流媒体服务器:从零搭建直播与低延迟分发平台 如果你在公司内部搞过直播、录课、或者接过一个“帮我们搭个能看视频的服务器”的需求大概率会碰到这样一个尴尬局面手里有现成的推流端和播放器但缺一个能稳定收流、转协议、分发的服务端。市面上的商业方案又贵又封闭开源项目里能打的就那么几个SRS就是其中一个而且是国内社区活跃度最高、文档最全的那个。SRS全称是Simple Realtime Server定位就是高性能、高可用的实时流媒体服务器。它原生支持RTMP、HTTP-FLV、HLS、WebRTC、SRT这些主流协议既能做直播也能做录播、连麦、低延迟音视频通话。以前想跑起来一个SRS得自己下载源码、装依赖、编译、配置系统服务光环境就够折腾半天的。现在有了Docker事情就简单太多了——拉个镜像写个compose文件启动完事。这篇文章就按我实际部署的经验从零开始一步步把SRS用Docker跑起来并说明每一步的选型和坑点适合所有被“部署流媒体服务”折磨过的开发者。1. 为什么说Docker是部署SRS的最优选择1.1 先搞清楚SRS解决了什么问题在开始敲命令之前先花几分钟把SRS的定位捋清楚不然你后面配置的时候会一头雾水。SRS本质上是一个流媒体网关它夹在推流端和播放端中间负责把视频流收下来转成不同协议再分发给观众。举一个最常见的场景你用OBS推流到服务器OBS默认走RTMP协议。但观众那边不一定都能直接播放RTMP有人用浏览器浏览器原生不支持RTMP有人用手机AppApp里可能更习惯用HTTP-FLV或者HLS。这时候SRS就起作用了它把RTMP流接收进来之后自动帮你转换成HTTP-FLV、HLS、WebRTC等格式不同终端的观众都能顺利看到画面。这套机制听起来不复杂但自己用Nginx搞过RTMP模块的人都知道原生Nginx只能做RTMP转发想转成HLS得自己写切片脚本想支持WebRTC几乎不可能。SRS把这些能力全部打包在一起了这也是它能在开源流媒体服务器里脱颖而出被很多企业生产环境采用的原因。我们在测试环境验证过SRS单机扛几千路并发观看没有问题配合CDN做边缘分发跑大型直播活动也够用。1.2 源码编译与Docker部署的取舍SRS官方提供了源码编译方式也提供了现成的Docker镜像。两种方式我都用过直接说结论除非你有改C源码的需求否则都用Docker不要自己编译。为什么因为SRS是用C写的从源码编译意味着你要装编译器、装依赖库、处理各种兼容性问题。我第一次在CentOS 7上编译SRS 4.0光等编译就等了快二十分钟中间还因为openssl版本太旧报错过一次。如果你用的是macOS或者Windows还得先搞一套Linux环境或者虚拟机那体验更酸爽。Docker方式最大的优势是环境一致性。官方把编译好的二进制和运行环境都打包进了镜像你拉下来之后就一定能跑起来不会遇到“我明明按教程做的为什么报错”这种玄学问题。另外Docker的隔离特性也很实用SRS要占用1935、1985、8080等固定端口跟宿主机上其他服务冲突时Docker可以轻松做端口映射调整而不用去改SRS本身的配置。1.3 这套方案适合什么人和什么场景如果你属于下面任何一类情况这篇文章的部署方式就很适合你公司内部需要一套培训直播或视频会议录制系统不想购买商业流媒体服务。个人开发者想做一个自己的直播间、在线课程网站或游戏直播平台。只需要在测试环境快速验证SRS功能评估它是否满足业务需求。想学习流媒体协议需要一个本地环境不断推拉流测试。这套方案对机器配置要求不高1核2G的云服务器就能跑得很欢本地一台普通电脑也没问题。操作系统方面Linux、Windows、macOS都支持Windows下建议开启WSL2后使用Docker Desktop性能比Hyper-V模式更好后面常见问题里会单独讲。2. 部署前的准备环境、镜像与端口规划2.1 Docker和Docker Compose的安装检查这一步看起来基础但真的很多人卡在这里。你先执行下面的命令确认Docker环境是否正常docker --version docker compose version如果你还没有安装Docker分平台处理。Ubuntu和Debian这类系统直接用官方脚本最省事curl -fsSL https://get.docker.com | bash sudo systemctl enable --now dockerCentOS和RHEL系列建议先配置阿里云镜像源再安装不然官方源在部分网络环境下很慢sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin sudo systemctl enable --now dockerWindows用户就直接装Docker Desktop但安装前一定要在BIOS里确认虚拟化已开启。很多人装完后启动Docker Desktop弹窗提示“virtualization support not detected”或者“failed to start because virtualisation support wasn’t detected”十有八九是BIOS里Virtualization TechnologyVT-x/AMD-V没打开进BIOS开启后重启就好了。另外Windows家庭版建议先装WSL2再装Docker Desktop因为这个版本对Hyper-V支持不完整直接用Hyper-V模式很容易翻车。Docker装好后顺手确认一下Compose插件是否可用。现在Docker Desktop和官方源安装的Docker都自带compose插件直接docker compose version即可验证。如果你的环境还在用老版本的docker-compose命令也没关系把下文所有docker compose替换为docker-compose即可。2.2 SRS镜像选择stable标签还是5.0镜像是部署的关键SRS官方在Docker Hub上维护了ossrs/srs仓库。打开终端先看一眼有哪些可用版本docker search srs docker pull ossrs/srs:5 docker images | grep srs我建议直接拉取ossrs/srs:5这个标签也就是SRS 5.0系列版本。相比4.0SRS 5.0对WebRTC的支持完善了很多内置了更好的SFU能力弱网表现也更好官方还做了不少性能和稳定性优化。如果你的业务对GB28181国标设备接入、SRT协议支持这些场景有要求5.0版本也原生支持4.0在这些方面明显没有5.0成熟。有一点要注意不要在部署文档里看到latest标签就直接拉取。latest确实指向最高版本但SRS还在持续迭代偶尔会有配置项调整。我遇到过几次用户照着一个写好的compose文件部署因为latest版本升级后某配置语法变化导致启动失败。生产环境建议锁定大版本测试环境随意。2.3 端口规划看清楚哪些口子必须留出来端口规划是SRS部署里最容易出问题的一步。SRS启动后会占用多个端口不同协议走不同端口部署之前最好把端口归属理清楚。默认情况下需要关注以下端口端口协议用途1935TCPRTMP推流/拉流最传统的流媒体协议1985TCPHTTP API用于管理后台、接口鉴权、监控查询8080TCPHTTP服务HTTP-FLV、HLS、WebRTC等基于HTTP的拉流都走这里8000UDPWebRTC媒体传输端口9000UDPSRT协议的媒体传输端口先检查这些端口是否被占用sudo lsof -iTCP:1935 -sTCP:LISTEN sudo lsof -iTCP:8080 -sTCP:LISTEN sudo netstat -tulnp | grep -E 1935|1985|8080|8000|9000如果某个端口已经被占用了你需要在docker run或者compose文件里做宿主机端口与容器端口的映射调整。比如宿主机8080端口被Nginx占用就可以改成8081:8080把宿主机8081映射到容器8080。后面配置播放地址时只需要把端口改为映射后的值就行。2.4 目录规划日志、配置与录制文件持久化在使用Docker部署SRS时还要考虑数据持久化问题。容器是个临时环境容器一旦删除内部所有数据都会丢失。对于SRS来说需要持久化的有两类数据一是SRS的配置文件二是录制或切片产生的音视频文件。我习惯在一开始就创建一个专门的目录比如/data/srs或/opt/srs然后在这个目录下放配置文件、日志目录和录制文件目录。这样后期升级镜像、迁移服务器、排查问题都方便。后面写compose文件时会通过volume把这些目录挂载到容器里。3. 手把手用Docker Compose搭建SRS服务3.1 编写docker-compose.yml文件我强烈推荐用Docker Compose管理SRS。相比一长串的docker run命令compose文件即是配置也是文档团队其他人接手你的环境时看一个compose文件就知道整套服务是怎么跑的。下面是经过我实测的SRS 5.0部署配置version: 3.8 services: srs: image: ossrs/srs:5 container_name: srs-server restart: unless-stopped ports: - 1935:1935 - 1985:1985 - 8080:8080 - 8000:8000/udp - 9000:9000/udp volumes: - ./srs.conf:/usr/local/srs/conf/srs.conf:ro - ./logs:/usr/local/srs/objs/logs - ./data:/usr/local/srs/objs/nginx/html environment: - TZAsia/Shanghai逐项说说我为什么这样写restart: unless-stopped保证服务器重启后SRS自动拉起不需要人工干预。端口映射把容器内部端口暴露到宿主机。TCP的1935、1985、8080是基础8000和9000的UDP端口是WebRTC和SRT的媒体通道必须有否则WebRTC推拉流会失败。配置文件的挂载用了只读模式:ro防止容器内部误改配置也提醒自己配置只能从宿主机改。TZAsia/Shanghai设置时区不然后续日志和录制文件的命名时间不对。写完后在srs.conf文件所在目录执行docker compose up -d看到Started状态后再确认一下容器运行情况docker compose ps docker logs -f srs-server首次启动日志里会打印当前SRS的版本号以及各个协议的监听地址。如果看到类似rtmp: listen tcp://0.0.0.0:1935、http: listen tcp://0.0.0.0:8080这样的输出说明服务已经正常起来了。3.2 编写srs.conf核心配置配置文件的完整内容如下这个是SRS官方示例配置的精简版本我加了少量生产环境的必要项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; # WebRTC over TCP, optional tcp_enabled on; # protocol for WebRTC playback protocol udp; } srt_server { enabled on; listen 9000; maxbw 1000000000; } vhost __defaultVhost__ { rtmp { enabled on; } http_remux { enabled on; mount [vhost]/[app]/[stream].flv; } hls { enabled on; hls_path ./objs/nginx/html; hls_fragment 2; hls_window 6; } webrtc { enabled on; } srt { enabled on; } }几个关键点解释一下daemon off配合srs_log_tank console让日志直接输出到stdout这样docker logs才能看到日志方便在容器模式下排查问题。如果不加这两项SRS会以守护进程方式运行但容器里看不到任何输出。http_remux开启后RTMP流可以自动通过HTTP-FLV播放这是低延迟播放的主要手段。hls相关配置里hls_fragment 2表示每2秒生成一个切片文件hls_window 6表示只保留最近6个切片也就是大约12秒的窗口。这样配置的优势是直播延迟不会太高且不会在磁盘堆积太多无用的切片文件。如果要做直播回放或点播则需要把hls_window调大并把切片文件存储到持久化目录。rtc_server除了UDP监听我开了tcp_enabled on。有些企业网络会限制UDP传输WebRTC走TCP可以兼容更多网络环境。3.3 用ffmpeg快速验证推流链路服务启动后先别急着上OBS用ffmpeg推一个本地视频文件验证整条链路最方便。你可以不用真的准备一个mp4文件用ffmpeg自带的testsrc测试源就能模拟视频流ffmpeg -re -f lavfi -i testsrcsize1280x720:rate30 \ -f lavfi -i sinefrequency1000:sample_rate44100 \ -vcodec libx264 -preset veryfast -tune zerolatency \ -acodec aac -ar 44100 -ac 2 \ -f flv rtmp://127.0.0.1:1935/live/test推流命令运行后不要关闭窗口在另一个终端检查SRS日志看有没有出现类似new client和stream publish的记录。出现之后你的流就已经进到SRS里了。这里的推流地址rtmp://127.0.0.1:1935/live/test中live是应用名test是流名。可以随便取但后面拉流地址要和推流地址保持一致否则找不到这个流。3.4 三种方式验证拉流播放流推上去之后用下面三种方式分别验证就可以确认SRS的协议转换能力是否正常。第一种先用VLC播放RTMP流这是最标准也最不容易出错的验证方式rtmp://127.0.0.1:1935/live/test第二种验证HTTP-FLV播放。SRS开启http_remux后可以直接用浏览器播放也可以使用支持HTTP-FLV的播放器比如flv.js播放http://127.0.0.1:8080/live/test.flv第三种验证HLS播放。因为SRS配置了HLS切片等待几秒后访问下面的m3u8地址http://127.0.0.1:8080/live/test.m3u8我实测的经验是RTMP和HTTP-FLV的延迟差不多在1~3秒之间HLS因为切片机制的原因延迟会更高一些大约在5~10秒之间。如果你做的是在线互动直播首选HTTP-FLV或者WebRTC如果是做视频录播课或者点播场景HLS更合适因为它的切片文件可以直接被CDN缓存大规模分发成本最低。3.5 WebRTC低延迟播放验证SRS 5.0对WebRTC的支持已经非常成熟了。如果你想要毫秒级延迟WebRTC是首选方案。官方提供了一个现成的WebRTC播放页面只要把SRS服务的IP和端口改一下就能直接在Chrome浏览器里播放WebRTC流。播放地址格式如下webrtc://127.0.0.1/live/test注意这里没有写端口。浏览器访问WebRTC播放器页面时会通过HTTP API默认1985端口获取流信息然后自动走8000的UDP通道传输媒体数据。所以在访问WebRTC播放页面之前请确保SRS的1985端口和8000 UDP端口都可以从外网访问否则会卡在“正在连接”阶段。如果你用的是云服务器还需要在安全组或防火墙里放行UDP 8000端口。很多人在云服务器上WebRTC连不上查来查去发现就是安全组没放行UDP端口RTMP能通、HTTP-FLV能通一到WebRTC就卡住就是这个原因。4. 进阶配置防盗链、监控与真实场景部署4.1 给流媒体服务加上简单的鉴权SRS默认情况下是完全开放的任何人只要知道你的服务器IP和推流地址就能往你的服务器推流也能拉到你的流。这在公网环境是非常危险的事情轻则被塞入垃圾流重则被当成免费CDN用来做转播。我建议至少做一层简单的鉴权。SRS提供了HTTP回调机制可以在推流和播放时请求外部接口完成鉴权。以on_publish为例开启后SRS在收到推流请求时会向指定的HTTP接口发送POST请求你在这个接口里校验密码或签名返回0表示允许返回非0表示拒绝vhost __defaultVhost__ { http_hooks { enabled on; on_publish http://127.0.0.1:8080/api/auth/publish; on_play http://127.0.0.1:8080/api/auth/play; on_unpublish http://127.0.0.1:8080/api/auth/unpublish; } }如果你不想写接口只是临时防一下熟人滥用也可以用SRS的token机制在推拉流地址里加上固定的token字段SRS启动时加载预设的密钥进行校验。但token机制功能比较简单生产环境建议还是用HTTP回调实现完整的鉴权逻辑。4.2 把SRS接入Prometheus监控服务跑起来之后你就得关心它的运行状态。SRS从4.0版本开始原生支持Prometheus协议暴露metrics数据开启方法是在srs.conf的http_api模块里加一行配置http_api { enabled on; listen 1985; prometheus on; }重启SRS后访问http://127.0.0.1:1985/api/v1/prometheus/metrics就能看到SRS暴露的指标数据。里面包含当前连接数、推流数、带宽、内存占用等信息。配合Prometheus和Grafana可以做一个非常漂亮的监控大屏实时掌握服务器的健康状况。我自己的线上环境就是用的这个方案Grafana面板里能看到RTMP连接数、HTTP-FLV请求数、HLS切片数哪天流量异常上涨一眼就能看出来。这套监控部署起来不复杂Prometheus和Grafana同样可以用Docker Compose部署跟SRS的compose文件放同一个目录即可。4.3 录制直播流实现回放和点播很多场景下直播结束之后还要能回看SRS的录制功能可以解决这个问题。SRS可以把推上来的RTMP流转存成FLV或MP4文件。配置起来也很简单在vhost下加一个dvdvr模块vhost __defaultVhost__ { dvr { enabled on; dvr_apply all; dvr_path ./objs/nginx/html/record/[stream].[timestamp].flv; dvr_plan session; dvr_duration 0; dvr_wait_keyframe on; } }dvr_plan session表示一个推流会话生成一个文件适合录制完整直播。如果想要分段录制可以把dvr_plan改为segment再设置dvr_duration指定每段时长比如3600表示每小时一个文件。录制生成的文件会存储在compose文件里volume挂载的./data目录下你可以配合Nginx或OSS做后续的点播分发。这里有一个经验分享dvr_wait_keyframe on这个参数值得开启。它会让SRS等到关键帧I帧到达时才真正开始写入文件避免生成的视频文件开头是花屏或者黑屏。第一个关键帧可能在推流后几秒才出现所以录制的视频会比实际直播晚几秒开始但这是值得的因为花屏的视频文件没有保留价值。4.4 集群与边缘化部署的思路单个SRS节点在并发不高时性能很好但当并发增长到几千甚至上万时就需要考虑集群部署了。SRS官方支持Origin和Edge两种角色。Origin负责接收推流Edge负责向Origin拉流并分发类似于CDN的边缘节点模式。用Docker部署SRS Edge节点也很简单。在Edge节点的配置文件中vhost下加一行origin配置即可vhost __defaultVhost__ { mode remote; origin rtmp://origin-server-ip:1935/live; }部署多个Edge节点后可以通过负载均衡比如Nginx或云负载均衡器把用户的播放请求分发到不同Edge节点。这样即使某个Edge节点宕机其他节点依然可以提供服务整体可用性大幅提升。当然集群方案涉及的内容比较多这里先提供一个思路。对大多数中小型场景来说一台SRS节点加CDN分发已经完全够用了不建议一开始就上集群复杂度会成倍增加。5. 常见问题排查与实测避坑指南5.1 端口占用导致的启动失败SRS最容易遇到的第一个问题就是端口占用。启动时如果日志里出现bind failed: Address already in use说明有进程占用了SRS要监听的端口。用下面命令找出占用端口的进程然后按需处理sudo lsof -i:1935 sudo netstat -tulpn | grep 1935如果占用端口的是其他业务建议把SRS的端口映射改成其他端口。比如在compose文件里把8080改成8081:8080。改完之后记得同步更新播放地址中的端口不然会出现“推流成功但拉流404”的怪问题。5.2 Docker Desktop虚拟化检测失败Windows用户比较大概率碰到这个问题。Docker Desktop启动时弹窗提示虚拟化支持未检测到然后整个Docker服务无法使用。这类问题我在Windows上帮人排查过很多次原因基本都是以下一种BIOS里关闭了Intel VT-x或AMD-V需要重启进BIOS开启。Windows功能里的“Hyper-V”和“适用于Linux的Windows子系统”没开启去“启用或关闭Windows功能”里勾选。装了某些安全软件干扰了Hyper-V的启动。还有一种情况是电脑开了第三方虚拟机软件比如VirtualBox、VMware后和Hyper-V产生冲突。如果不常用这些虚拟机建议直接卸载Docker Desktop改用WSL2 Docker Engine的方案。WSL2方案对Windows 10/11家庭版更友好实测性能也更接近原生Linux。5.3 推流成功但播放端黑屏或卡顿推流成功SRS日志里有stream publish但播放端黑屏或者画面不动这个问题可以从几个方向排查。先看播放端日志确认播放器是否成功与SRS建立连接。如果连接成功但画面不动可能是推流端编码参数和播放端解码能力不匹配。我用OBS推流时遇到过这个问题后来把OBS的视频编码器从硬件编码改为x264软件编码问题就消失了。建议推流时统一使用H.264编码不要用H.265因为很多浏览器和播放器不支持H.265解码。如果播放HTTP-FLV延迟超过5秒可以检查一下推流端是否开启了缓冲或缓冲设置过大。OBS的“输出延迟”和播放器的“缓冲时长”都会增加延迟。SRS本身的转发链路能在毫秒级完成绝大部分延迟都来自于端到端的缓冲设置。5.4 WebRTC连不上先查UDP端口WebRTC连接不上是SRS部署中一个高频问题。现象是播放器一直显示“connecting”过一会儿超时失败。排查思路按以下顺序来第一步确认8000端口UDP映射正确。用nc -u测试一下UDP端口连通性nc -uvz your-server-ip 8000第二步确认云服务器安全组放行了UDP 8000端口。很多云平台默认只放行TCP端口UDP端口需要额外配置安全组规则。这一步最容易被忽略。第三步确认客户端和服务器的系统时间一致。WebRTC的ICE协商过程对时间敏感如果服务器时间严重偏移会导致DTLS握手失败。部署时已经设置了TZAsia/Shanghai但还需要确认系统时间本身是准确的建议配置NTP自动同步时间。5.5 Docker常用命令速查表最后整理一份平时管理SRS容器最常用的命令直接抄作业用操作命令启动服务docker compose up -d停止服务docker compose down查看实时日志docker logs -f srs-server进入容器内部docker exec -it srs-server bash重启服务docker compose restart查看容器状态docker ps -a | grep srs查看资源占用docker stats srs-server完全清理并重建docker compose down -v docker compose up -d注意docker compose down -v会删除volume中的数据。如果录制文件或切片文件已经保存在宿主机挂载目录中不受影响但如果日志文件只写在容器内且没有挂载出来就会被清除。使用前务必确认。写在最后一点实战中的个人体会SRS用Docker部署真的是目前最轻松的方案了从拉镜像到跑通推拉流熟练之后十分钟以内就能完成。我自己第一次部署的时候因为不熟悉WebRTC的UDP端口机制折腾了大半天才找到原因后来把端口规划做成了标准流程再也没出过问题。所以这篇里端口和防火墙的部分建议大家部署前就对照检查一遍能省很多事。另外给新手一个建议第一次测试时不要急着接公司正式业务。先用ffmpeg推测试源把本地环境全部跑通再切换到真实推流设备。这样能把环境问题和业务问题分开排查心态会稳很多。SRS本身没有想象中那么复杂把RTMP、HTTP-FLV、HLS、WebRTC这四条链路都验证一遍你对整个流媒体系统的理解会提升一个台阶。
RELATED READING

延伸阅读

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