云计算百科
云计算领域专业知识百科平台

SRS视频服务器Docker部署:端口映射不一致导致WebRTC播放失败的排查与解决

1. 从一次深夜的“直播事故”说起

那天晚上,我正给一个内部团队做技术分享,准备用自己搭建的SRS服务器演示一个实时互动的WebRTC应用。推流端一切正常,OBS软件显示推流码率稳定,但到了播放端,那个熟悉的播放器窗口却一直转圈圈,最后弹出一个冷冰冰的“播放失败”。你能想象那种尴尬吗?台下几十号人等着,我这边却卡在了最基础的流播放上。更诡异的是,我检查服务器日志,发现RTMP推流是成功的,甚至我配置的视频录制功能都在后台默默工作,把流存成了MP4文件。这就好比外卖小哥把餐送到了你家门口(推流成功),还顺手帮你把垃圾倒了(录制成功),但就是死活敲不开你家的门(播放失败)。

事后我花了两个多小时排查,最终定位到问题根源:Docker端口映射不一致。这听起来是个低级错误,但在实际部署SRS,尤其是需要修改默认端口时,踩坑的人不在少数。SRS(Simple Realtime Server)作为一个优秀的开源流媒体服务器,对WebRTC的支持非常强大,但它的工作方式有点“认死理”。它内部用于生成WebRTC连接所需的SDP(会话描述协议)信息时,会严格使用配置文件里写的那个listen端口号。如果你用Docker启动时,图省事或者没注意,把宿主机的8001端口映射到了容器的8000端口,但在配置文件里写的还是listen 8000,那么SRS就会告诉播放器:“嘿,来8000端口找我拿数据。” 播放器很老实,就去连接宿主机的8000端口,结果发现这个端口根本没开放(因为实际映射的是8001),连接自然失败。

所以,这篇文章就是把我踩过的这个坑,以及背后的原理、完整的排查思路和正确的配置方法,掰开揉碎了讲给你听。无论你是刚开始接触流媒体开发的初学者,还是正在为生产环境部署SRS的工程师,希望这些经验能帮你省下那几个小时的调试时间。

2. 为什么端口映射不一致会让WebRTC“罢工”?

要彻底理解这个问题,我们得先搞明白WebRTC播放一个流,和传统的RTMP或HTTP-FLV播放有什么根本不同。你可以把RTMP播放想象成去图书馆借一本已经写好、放在固定书架上的书(流已经存在,客户端直接拉取)。而WebRTC播放,更像是一次电话会议:双方(服务器和客户端)需要先互相交换一下“联系方式”和“会议规则”,这个交换过程就是通过SDP和ICE(交互式连接建立)协议完成的。

关键就在这里:SRS服务器在回应客户端的播放请求时,会生成一份SDP描述文件。这份文件里,会明确告诉客户端:“我的RTC数据通道在哪个IP地址的哪个端口上等着你。” 这个端口信息从哪里来?就是直接从你配置文件里rtc_server块下的listen指令读取的。它不会智能地去检测:“哦,我虽然配置了8000,但我的容器实际上被映射到了宿主机的8001端口,所以我应该告诉客户端连8001。” 它没这么聪明,它就是照章办事。

让我们来看一个典型的错误配置组合,这也是我最初翻车的地方:

  • Docker运行命令:-p 8001:8000/udp
    • 意图:把宿主机的8001端口映射到容器内的8000端口。
  • SRS配置文件 (conf/rtmp2rtc.conf):rtc_server {
    enabled on;
    listen 8000; # 这里仍然写的是8000
    candidate $YOUR_PUBLIC_IP;
    }

  • 这个组合会导致一个致命的“信息错位”。当客户端通过SRS的HTTP-API(通常是1985端口)发起WebRTC播放请求时,SRS计算出的候选地址(candidate)会是:candidate:1 1 UDP 2130706431 $YOUR_PUBLIC_IP 8000 typ host。客户端拿到这个信息,就会径直去连接公网IP的8000端口。然而,你的防火墙或云服务商安全组只开放了8001端口(因为你以为映射的是这个),8000端口是关闭的。于是,客户端无法建立UDP数据通道,播放也就失败了。

    相反,RTMP推流和HTTP回调这类功能为什么不受影响?因为它们是“单向告知”或“内部通信”。RTMP推流时,推流客户端是主动连接到你指定的宿主机端口(比如1936),再由Docker转发到容器的1935。这个连接路径是客户端发起时就确定的。HTTP回调是容器内部进程去访问host.docker.internal这样的宿主机地址,走的是容器网络到宿主机的路由,不涉及外部客户端反向连接容器特定端口。所以,只有像WebRTC这种需要服务器在协议中“主动告知”客户端连接地址和端口的场景,才会被端口不一致的问题精准打击。

    3. 手把手排查:当WebRTC播放失败时你应该看哪里?

    遇到播放失败,别慌,按照一个清晰的路径去排查,能快速缩小问题范围。我自己总结了一个“从外到内,从现象到配置”的四步排查法。

    第一步:检查最明显的网络与端口连通性

    首先,确认基础网络是通的。在播放端所在的机器,用telnet或nc命令测试一下SRS服务器的公网IP和关键端口(如HTTP-API的1985端口)是否能通。这能排除防火墙或安全组彻底阻断的问题。接着,重点检查UDP端口。WebRTC的数据传输主要用UDP。很多人习惯用telnet测TCP端口,却忘了测UDP。在Linux上,你可以用nc -u -z -v 你的公网IP 8001(假设你的RTC端口是8001)来测试UDP端口的可达性。在Windows上,可能需要借助一些网络工具或PowerShell脚本。如果UDP端口不通,那WebRTC数据通道肯定建立不起来。

    第二步:查看SRS服务器日志,寻找蛛丝马迹

    SRS的日志是宝藏。确保你的配置文件中srs_log_tank设置为console,这样日志会输出到标准输出,方便用docker logs查看。运行:

    docker logs -f 你的容器名

    然后尝试播放。关注日志里有没有关于RTC的报错。一个健康的、收到WebRTC播放请求的日志,通常会看到类似这样的信息:

    [INFO] [rtc_server] RTC: serve api, client_id=1, request: /rtc/v1/play/
    [INFO] [rtc_server] RTC: create session, client_id=1, stream_url=webrtc://…/live/stream

    如果根本没看到/rtc/v1/play/的请求记录,那可能是前端播放器根本没请求对地址,或者1985端口没通。如果看到了请求但后续有错误,比如[ERROR] ICE connection failed,那可能就是端口或候选地址(candidate)配置有问题。

    第三步:深入SDP交换过程,直击问题核心

    这是定位端口映射问题的“杀手锏”。在浏览器的开发者工具(F12)中,打开“网络”(Network)选项卡,过滤/rtc/v1/play/请求。找到这个POST请求,查看它的响应体(Response)。里面就是SRS生成的SDP答案(Answer)。

    你需要找到类似这样的一行:

    a=candidate:1 1 UDP 2130706431 203.0.113.10 8000 typ host

    或者

    a=candidate:2 1 UDP 1694498815 203.0.113.10 8000 typ srflx

    请死死盯住最后那个端口号(例子里的8000)。这个端口号,必须和你宿主机实际映射并对外开放的UDP端口号完全一致。如果这里显示8000,但你的Docker命令是-p 8001:8000/udp,且安全组开的是8001,那么问题100%就出在这里。SDP里告诉客户端去连8000,客户端当然会失败。

    第四步:核对“三重奏”:Docker命令、配置文件、防火墙规则

    经过前面三步,你心里大概有数了。最后一步就是进行系统性核对:

  • Docker运行命令:确认-p参数,例如-p 外部端口:内部端口/udp,记住这里的“外部端口”是对外服务的。
  • SRS配置文件:确认rtc_server下的listen指令后面的端口号,必须和上面Docker命令中的内部端口一致吗?不!这里是个关键认知转变:在端口映射场景下,listen指令应该填写Docker容器的“内部端口”,但这个“内部端口”必须和Docker命令中“外部端口”映射的那个“内部端口”是同一个值吗?逻辑有点绕,我们看正确配置。
  • 正确逻辑:为了杜绝混淆,最稳妥的做法是让“外部端口”、“内部端口”、“配置文件listen端口”三者统一。即,你想让公网用8001访问RTC服务,那么配置就是:-p 8001:8001/udp,同时配置文件里写listen 8001;。这样SDP里生成的端口号就是8001,内外完美对应。
  • 4. 正确的配置方案:从零开始部署一个“健康”的SRS

    说了那么多坑,现在我们来一步步搭建一个正确配置的SRS Docker服务。假设我们的需求是:RTMP端口用1936(默认1935可能被占),WebRTC端口用8001(默认8000可能被占),其他API和管理端口保持不变。

    4.1 编写一份“内外统一”的Docker启动命令

    我们放弃“外部端口:内部端口”这种不一致的映射,采用“外部端口:相同外部端口”的映射方式。这样,容器内监听的端口号就是我们知道的外部端口号,配置文件里也写这个端口号,彻底杜绝混乱。

    docker run -d –name srs-webrtc \\
    –restart=always \\
    -p 1936:1936 \\ # RTMP推拉流端口
    -p 1985:1985 \\ # HTTP-API端口,用于WebRTC信令
    -p 8080:8080 \\ # SRS自带Web管理页面端口
    -p 8001:8001/udp \\ # WebRTC数据传输UDP端口,关键!内外一致。
    -v /your/data/path:/usr/local/srs/static/DVR-video \\
    registry.cn-hangzhou.aliyuncs.com/ossrs/srs:4 \\
    ./objs/srs -c /path/to/your/rtmp2rtc.conf

    注意,我们把1935改成了1936,8000/udp改成了8001/udp,并且映射关系是1936:1936和8001:8001。

    4.2 配置一份“心口如一”的SRS配置文件

    现在,我们需要修改配置文件,让SRS知道它应该在容器内监听1936和8001端口。你可以通过docker exec进入容器修改,更推荐的做法是在宿主机上维护配置文件,然后通过-v映射进容器。这里给出关键部分的配置:

    # conf/rtmp2rtc-webrtc.conf
    listen 1936; # RTMP监听端口,改为和Docker映射的外部端口一致
    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 8001; # WebRTC监听端口,改为和Docker映射的外部端口一致
    candidate 你的公网IP地址; # 这里务必填写服务器的公网IP
    }

    vhost __defaultVhost__ {
    rtc {
    enabled on;
    rtmp_to_rtc on;
    rtc_to_rtmp on;
    }
    http_remux {
    enabled on;
    mount [vhost]/[app]/[stream].flv;
    }
    # … 其他配置(如dvr、hooks)根据你的需要保持原样
    }

    重中之重:listen 1936; 和 listen 8001; 这两行,必须和Docker命令中-p参数后面的第一个端口号(宿主机端口) 严格对应。同时,candidate字段必须填写你服务器的公网IP,SRS会用这个IP去构造SDP中的候选地址。

    4.3 验证:如何测试你的WebRTC服务真的工作了?

    配置好后,启动容器。首先用docker logs查看日志,确保没有报错,并且看到SRS成功监听了1936和8001端口。

    然后进行功能测试:

  • RTMP推流:用OBS或其他推流软件,推流到 rtmp://你的服务器IP:1936/live/streamtest。观察OBS是否显示推流成功,同时查看SRS日志是否有推流接入信息。
  • WebRTC播放:打开SRS自带的演示页面 http://你的服务器IP:8080/,进入播放器页面。在播放地址栏输入 webrtc://你的服务器IP:1936/live/streamtest,点击播放。如果配置正确,你应该能很快看到视频画面。
  • 检查SDP:在播放时,按F12打开开发者工具,找到对/rtc/v1/play/的请求,查看响应内容,确认其中的candidate行里的IP和端口(应该是8001)是否正确。
  • 如果一切顺利,恭喜你,你已经成功部署了一个端口配置正确的SRS WebRTC服务。这个“三位一体”(Docker命令、配置文件、防火墙端口)的统一法则,是保证复杂网络环境下服务可用的关键。

    5. 进阶话题:在更复杂网络环境下的注意事项

    在实际生产环境中,情况可能比简单的单机Docker更复杂。比如服务器放在NAT网关后面、使用Docker Compose编排、或者部署在Kubernetes中。这些场景下,端口映射问题的本质不变,但表现形式略有不同。

    场景一:服务器位于NAT/路由器后,有内网IP
    这是家庭宽带或某些企业内网常见情况。你的服务器有一个内网IP(如192.168.1.100),公网IP在路由器上。此时,candidate字段不能填内网IP,必须填公网IP。同时,需要在路由器上设置端口转发(Port Forwarding),将公网IP的1936、8001(UDP)等端口,转发到内网服务器的对应端口上。Docker和SRS配置本身仍遵循“内外端口一致”的原则,只是网络流量多了一层转发。

    场景二:使用Docker Compose编排
    在docker-compose.yml文件中,端口映射的写法是ports字段。确保其配置与单一docker run命令一致:

    services:
    srs:
    image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:4
    container_name: srs-webrtc
    restart: always
    ports:
    – "1936:1936"
    – "1985:1985"
    – "8080:8080"
    – "8001:8001/udp"
    volumes:
    – ./data:/usr/local/srs/static/DVR-video
    – ./conf/rtmp2rtc.conf:/usr/local/srs/conf/rtmp2rtc.conf
    command: ["./objs/srs", "-c", "/usr/local/srs/conf/rtmp2rtc.conf"]

    注意,这里我们把宿主机本地的配置文件./conf/rtmp2rtc.conf映射到了容器内的固定路径,这样方便在宿主机上直接修改配置,而无需进入容器。

    场景三:云服务器与安全组
    在阿里云、腾讯云等云平台上,除了系统防火墙,还有一个更外层的“安全组”或“防火墙规则”。你必须在那里显式地添加入站规则,允许TCP协议的1936、1985、8080端口,以及UDP协议的8001端口。很多同学配置了Docker和系统防火墙,却忘了云平台的安全组,导致外部依然无法访问。

    关于candidate的自动获取:SRS支持自动获取公网IP作为candidate。你可以将配置写为 candidate *;。SRS会尝试自动探测。但在某些复杂的多网卡或特定Docker网络模式下,自动探测可能会失败,拿到的是容器内部网卡的IP。因此,对于生产环境,我仍然建议在能明确知道公网IP的情况下,手动配置candidate 你的公网IP;,这样最稳妥。

    折腾流媒体服务就像解一道复杂的立体几何题,需要同时考虑多个维度的配置是否对齐。端口映射不一致这个问题,看似简单,却因为涉及Docker的网络抽象、WebRTC的信令协议、以及SRS服务器的内部逻辑,成了新手甚至有一定经验开发者的一道坎。我最深的体会就是,在云原生环境下做音视频开发,一定要有“端口意识”,任何一个端口的映射关系,都要在脑子裡清晰地过一遍:它在宿主机是多少?在容器内是多少?在配置文件里写的又是多少?防火墙是否放行?把这几个点连成一条线,并且保证这条线是直的,服务才能畅通无阻。下次当你再遇到WebRTC播放失败而推流正常时,不妨第一个就检查这份“端口一致性清单”,很可能问题就迎刃而解了。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » SRS视频服务器Docker部署:端口映射不一致导致WebRTC播放失败的排查与解决
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!