资讯动态

WVP-Pro Docker部署实战:GB28181信令与流媒体配置全解析

发布时间:2026/10/21 10:20:56 来源:尧图企业网站定制
做国标 GB28181 视频接入的项目绕不开 WVP-Pro。它本质上是一套负责信令控制和业务管理的国标视频平台光靠它自己跑不起来必须搭配流媒体服务、数据库和缓存一起工作。我以前在项目里是用裸 jar 包部署的后来切到 Docker 编排看起来就是把启动命令换成 compose实际操作时才发现坑全藏在配置联动里SIP 端口、流媒体收流端口、回调地址任何一个环节填错摄像头就只在设备列表里躺着怎么点都不出画面。这篇就把我从零到一用 Docker 部署 WVP-Pro 的完整过程、踩过的坑和最终稳定运行的配置一起整理出来给要接国标摄像头、做智慧园区或视频汇聚平台的开发者做参考。1. 先认清现实WVP-Pro 不是一个容器能跑完的1.1 信令、收流、落库三方各司其职先讲透一件事WVP-Pro 在主流的部署模型里至少需要四个容器一起跑缺一个都不行。WVP-Pro 本体Java 进程只负责国标 28181 的 SIP 信令处理和业务逻辑。设备注册、目录查询、云台控制、录像查询这些打电话、发通知的动作全部由它完成ZLMediaKit后面统称 ZLM真正接收和转发媒体流的服务。设备被平台拉流时把 RTP 媒体流推到 ZLMZLM 负责把 RTP 转成 RTSP、RTMP、HLS、HTTP-FLV 这些前端播放器能拉的格式MySQL保存设备目录、通道信息、用户账号、权限、录像计划等所有元数据Redis做缓存和会话状态WVP 对 Redis 的依赖很实不是可选项。我习惯拿前台电话和楼下收件处来类比。WVP 像前台接线员所有摄像头开机后先给前台登记SIP REGISTER你想看某一路画面时前台给那台设备打个电话SIP INVITE告诉它把视频送到楼下的收件处这个收件处就是 ZLM。MySQL 是登记册Redis 是手边备忘便签。任何一个角色没就位业务链条就断了。1.2 国标接入的基本流程设备上线可以简化成四步摄像头配置好 SIP 参数后向 WVP 的 5060 端口发起 REGISTER 注册WVP 校验设备 ID 和密码返回 200 OK设备显示在线你在页面点播放WVP 给摄像头发 INVITE携带 SDP 信息告诉设备媒体流推到哪个 IP 的哪个 UDP 端口设备向 ZLM 的 UDP 10000 端口推 RTP 流ZLM 收流后转封装播放器从 ZLM 拉流出画面。后面所有排查本质都是在这四步里定位是哪一环断了。我在部署前会先在脑子里把这条链路过一遍端口才会映射完整配置时也才知道每一项是给谁看的。1.3 版本搭配镜像别乱挑WVP-Pro 版本迭代很快不同版本的配置字段有差异尤其是 media 段。我踩过一次手里拿的 WVP 镜像比较新配置文件还是看旧文档写的启动后日志一直在报 ZLM 相关的错反复排查才发现是字段名对不上。所以落地时注意三点镜像 tag 固定别用 latest 直接上生产WVP 和它配套的 ZLM 版本要匹配版本差太多时ZLM 的 REST API 返回字段解析会失败表现是 WVP 调用 ZLM 拉流、关流全部异常数据库尽量用 MySQL 8.0 系列Redis 用 6.x 以上老版本对某些命令的兼容性容易出问题。我见过不少新手直接把官网的 compose 抄下来结果只起了 WVP 一个容器MySQL、Redis、ZLM 全没跑。界面照样能登录但设备列表空转、播放报错日志里全是连接异常。下面这节先讲怎么把地基铺好再上编排。2. 开工前的地基主机、目录和配置文件里的几个必改项2.1 宿主机的底线要求先交代一下我这边验证过的部署环境系统Ubuntu 22.04 或 CentOS 7.9 以上内核 4.x 起Docker 20.10docker compose v2内存4G 起步8G 更稳。WVP 的 Java 进程留 1G 左右ZLM 看并发路数10 路以内 2G 足够磁盘录像、抓图、日志都往持久化目录写建议单独分一个数据盘至少 100G时区先把宿主机时区设成 Asia/Shanghai容器内部也统一设置 TZ。国标设备对时间敏感设备和服务器时间差太多时SIP 摘要认证可能直接失败这个问题我当初排查了很久才发现是时区。这些都是前置条件不是可选项。尤其是时区很多镜像默认 UTC跑在国标场景下会出现各种灵异现象。2.2 目录规划我习惯把 WVP 全家桶放在 /opt/wvp 下面每个服务独立目录互不干扰mkdir -p /opt/wvp/{sql,conf} mkdir -p /opt/wvp/data/{mysql,redis,zlm}/opt/wvp/sql数据库初始化脚本目录挂载给 MySQL 第一次启动用/opt/wvp/confWVP 的 application.yml 和 ZLM 的 config.ini/opt/wvp/data/mysqlMySQL 数据文件/opt/wvp/data/redisRedis 持久化文件/opt/wvp/data/zlmZLM 的日志、录像、web 资源目录。为什么要把数据目录全部挪到宿主机容器是一次性组件镜像升级、容器重建时如果数据写在容器内部层基本等于直接蒸发。这是 Docker 部署有状态服务的第一原则WVP 项目也不例外。配置文件也必须挂载出来改别进容器改否则容器一重建改动全没。2.3 WVP 配置文件里必须先改的几个字段WVP 的配置集中在 application.yml。我建议把它放到 /opt/wvp/conf 下挂载进容器。核心字段大致如下字段名因版本略有差异关键看语义spring: datasource: url: jdbc:mysql://mysql:3306/wvp?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: wvp_root_2024 redis: host: redis port: 6379 password: wvp_redis_2024 server: port: 18080 sip: ip: 192.168.10.10 # 对外可达的 SIP IP摄像头注册时填这个 port: 5060 domain: 3402000000 media: ip: 192.168.10.10 # 下发给设备的媒体 IP必须能被设备路由到 sdp-ip: 192.168.10.10 # SDP 里出现的 IP stream-ip: 192.168.10.10 # 拉流地址里的 IP host: zlm # 容器网络内访问 ZLM 的地址 port: 8080 # ZLM 的 HTTP API 端口 secret: your-zlm-secret # 和 ZLM config.ini 里的 api.secret 一致 port: 10000 # RTP 收流端口这一段是整个部署里最容易出错的地方。我见过最多的翻车案例是 media.ip 还留着默认的 127.0.0.1。WVP 在回给摄像头的 INVITE 里会把 media.ip 写进 SDP设备照着这个 IP 推流。如果摄像头拿到的是 127.0.0.1流就推到了设备自己身上平台自然黑屏。所以这个 IP 必须填摄像头网络能路由到的宿主机 IP。再强调一下 media.secret 和 ZLM config.ini 的一致性。ZLM 的 REST API 带鉴权secret 对不上时WVP 调 ZLM 拉流、关流全部失败表现就是流不存在hook 回调失败。提示部署前把两份配置逐字对一遍。WVP 的 media.secret、ZLM 的 api.secret 必须完全一致WVP 的 media.host 要能在容器里访问到 ZLM建议写容器名而不是 127.0.0.1。3. Compose 编排实战一条命令拉起整套平台3.1 完整的 docker-compose 文件先给出我环境里在用的编排文件。MySQL、Redis、ZLM、WVP 四个服务全在这里直接复制后替换镜像名和密码就能用version: 3.8 services: mysql: image: mysql:8.0 container_name: wvp-mysql environment: - MYSQL_ROOT_PASSWORDwvp_root_2024 - MYSQL_DATABASEwvp - TZAsia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci volumes: - /opt/wvp/data/mysql:/var/lib/mysql - /opt/wvp/sql:/docker-entrypoint-initdb.d:ro healthcheck: test: [CMD, mysqladmin, ping, -h, 127.0.0.1, -uroot, -pwvp_root_2024] interval: 10s timeout: 5s retries: 10 restart: always networks: [wvp] redis: image: redis:7.0 container_name: wvp-redis command: redis-server --appendonly yes --requirepass wvp_redis_2024 volumes: - /opt/wvp/data/redis:/data restart: always networks: [wvp] zlm: image: your-registry/zlmediakit:your-tag # 换成你拉取并验证过的 ZLM 镜像 container_name: wvp-zlm environment: - TZAsia/Shanghai volumes: - /opt/wvp/conf/config.ini:/opt/media/conf/config.ini - /opt/wvp/data/zlm/log:/opt/media/bin/log - /opt/wvp/data/zlm/media:/opt/media/bin/www ports: - 1935:1935 - 554:554 - 10000:10000/udp - 8080:80 restart: always networks: [wvp] wvp: image: your-registry/wvp-pro:your-tag # WVP 镜像可自行构建或使用验证过的社区镜像 container_name: wvp-pro depends_on: mysql: condition: service_healthy redis: condition: service_started zlm: condition: service_started environment: - TZAsia/Shanghai volumes: - /opt/wvp/conf/application.yml:/opt/wvp/config/application.yml - /opt/wvp/data/logs:/opt/wvp/logs ports: - 18080:18080 - 5060:5060/tcp - 5060:5060/udp restart: always networks: [wvp] networks: wvp: driver: bridge说明几点。镜像名我特意没有写死因为不同来源的 WVP 镜像和 ZLM 镜像 tag 差别很大你按自己拉取的镜像名填即可如果官方工程给了 Dockerfile自己构建一次更可控。ZLM 容器内部的目录布局不同镜像有差异挂载路径以镜像说明为准我这里的路径是参考常见默认布局。日志显示路径报错时先看容器内实际目录再改挂载。ZLM 的 8080 映射到宿主 8080只给 WVP 和排查用建议不要直接暴露到公网。3.2 数据库初始化第一次启动决定一生MySQL 容器的 /docker-entrypoint-initdb.d 目录只在数据目录为空时执行一次。也就是说只有 MySQL 第一次启动那一天会建表。两个要点先把 WVP 的 SQL 脚本放到 /opt/wvp/sql 里确保脚本是 UTF-8 编码表名、字段名不要改如果 MySQL 已经启动过但表没建全脚本不会自动补跑。这时候最简单的办法是停掉 MySQL 容器备份数据目录后清空重新启动让它再初始化。这个方法只适合刚部署阶段等系统里存了设备数据后再重置就亏大了。另外WVP 不同版本的初始化方式有差异。有些镜像集成了自动建表启动日志里能看到建表信息有些只提供 SQL 脚本必须人工导入。稳妥做法是启动 WVP 后看一眼日志如果报表不存在或 SQL 异常再手动走导入流程。别一上来就把两套初始化方案都挂进去冲突时反而更难排查。3.3 启动后的三层自检第一次启动建议这样跑docker compose up -d docker compose logs -f日志稳定后按三层来验证容器层docker compose ps四个服务全部 Up退出码是 0restarting 状态绝对不能继续往下走接口层先确认 ZLM API 通curl http://127.0.0.1:8080/index/api/getServerConfig?secretyour-zlm-secret能返回 JSON 说明 ZLM 活着且 secret 正确再访问 WVP 页面http://127.0.0.1:18080能看到登录页联动层看 WVP 日志里有没 ZLM 握手成功的记录同时确认 ZLM 日志里有没有收到来自 WVP 的 hook 注册请求。三层全过平台自身才算就绪。接下来就是重头戏接摄像头。4. 摄像头接入实录从在线到出画面要过的三道关4.1 第一关REGISTER 一直 401/404设备上线前先在平台界面添加设备录入 20 位国标编号和密码。摄像头的 SIP 配置里服务器地址填宿主机 IP端口填 5060域填平台的 sip.domain设备 ID 和密码和平台保持一致。配置完后盯着 WVP 日志看docker logs -f wvp-pro | grep -i sip常见三种现象日志里没有任何 REGISTER 包到达多半是 UDP 5060 没通。检查防火墙和云安全组从外部做一次 UDP 端口探测一直回 401 Unauthorized密码不对最常见。国标 28181 的摘要认证对密码和算法都敏感设备和平台两端都检查一遍。如果连续多台设备都是 401怀疑平台侧添加设备时生成的密码和摄像头配置对不上回 404 Not Found平台里没有这个设备 ID或者设备 ID 填错。国标 ID 是 20 位数字前 8 位是行政区划编码别拿 32 位编码来填。我处理过一个现场一百多台摄像头全部注册 401后来发现是平台里批量添加设备时重新生成了密码摄像头里还是出厂默认密码。批量接入时密码怎么生成、怎么下发、怎么固化到设备配置里一定要有统一方案否则后期排查极痛苦。4.2 第二关设备在线但预览黑屏注册过了设备在界面显示在线但点预览一直转圈。这时候问题几乎都在流媒体链路。按顺序排查点播放的同时看 ZLM 日志里有没收到 RTP 流的提示。ZLM 完全没收到包进入下一步在宿主机抓包tcpdump -i eth0 udp port 10000 -c 20点播放后观察有没有来自摄像头 IP 的 UDP 包完全没包SDP 里下发的 IP 不对回到 application.yml 检查 media.ip 和 sdp-ip改成宿主机的业务网卡 IP然后重启 WVP 容器有包但 ZLM 日志报丢包或解复用失败重点检查 UDP 10000 端口映射是否正常以及 ZLM 的收流配置有包也正常播放器还是拉不到流检查 ZLM 的 554、1935、8080 端口从播放器侧是否可达。很多前端页面默认走 HTTP-FLV8080 没放行页面一样黑屏。预览黑屏这类问题一定要和前端播放方式一起看。WVP 页面播放默认用的协议和端口不同版本不一样有的走 HLS延迟几秒有的走 HTTP-FLV接近秒开。前端解析出来的流地址端口如果和实际放行端口对不上黑屏是必然结果。4.3 第三关多网卡服务器的 IP 选择不少服务器有两张网卡一张管理网、一张业务网摄像头走业务网。WVP 和 ZLM 的 IP 配置在这种环境里最容易翻车。我的建议sip.ip、media.ip、sdp-ip、stream-ip 统一填业务网卡的 IP容器走 bridge 网络不影响这些 IP 的正确性因为下发给设备的始终是宿主机业务网 IP不是容器 IP如果摄像头和平台不在同一网段中间还有 NAT那还要在摄像头侧做端口映射WVP 里填的也是 NAT 前的公网地址。这种情况务必先在内网环境验证通了再叠公网链路否则一次引入两个变量出问题根本没法定位。提示改完 application.yml 必须重启 wvp 容器才生效。我犯过的错误是改了文件后只刷新页面以为保存就行结果设备拿到的 SDP 还是旧 IP平台一直报推流超时。5. 端口、域名和 HTTPS上线前最容易漏的几件事5.1 端口映射清单与防火墙放行把常用的端口整理成一张表照着放行就行服务端口协议用途WVP 页面18080TCP平台 Web 与业务 APISIP 信令5060TCP/UDP国标注册与控制RTP 收流10000UDP摄像头推流ZLM RTSP554TCP拉流ZLM RTMP1935TCP拉流ZLM HTTP8080TCPHTTP-FLV、HLS、APIMySQL3306TCP仅容器内互访Redis6379TCP仅容器内互访生产环境建议不要把 3306 和 6379 映射到宿主机或者只绑 127.0.0.1。我见过把 MySQL 直接暴露到公网的案例密码又是弱口令被扫描后数据库直接被清空。容器网络里服务之间走容器名互访物理机根本不需要开这两个端口。5.2 回调地址和反向代理里的 websocketWVP 和 ZLM 之间靠 hook 回调协同。ZLM 收到流、关闭流时会回调 WVP 做业务处理。hook 地址配错会出现画面有流但录像不生成播放结束后流不回收这类问题。在 compose 网络里WVP 的地址直接用容器名 wvp端口 18080ZLM 和 WVP 不在同一 compose 网络时hook 地址就要填宿主机 IP同时确保防火墙放行。很多人习惯用 Nginx 反代 WVP 的 18080 做成域名访问。这本身没问题但 WVP 页面里的视频预览前端播放器走的是 websocketNginx 反代必须配置 Upgrade 头location / { proxy_pass http://127.0.0.1:18080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; }不配 Upgrade 头页面能打开视频预览一直转圈浏览器控制台里全是 426 Upgrade Required。这个坑我踩过一次说出来让后来人少绕一圈。5.3 HTTPS 域名下面的混合内容拦截如果对外上了 HTTPS 域名还要检查页面协议和流协议是否配套。有些部署在做 HTTPS 后出现了登录成功但播放器发请求被浏览器拦截成 mixed content。我的经验是WVP 页面本身不强依赖 HTTPS内网部署用 HTTP 完全够非要上 HTTPS优先用反向代理终结 TLS后端 WVP 保持 HTTP代理负责把访问全部转成 HTTPSZLM 的流地址要跟着页面协议走。HTTP 页面拉 HTTP-FLVHTTPS 页面拉 HTTPS-FLV否则浏览器混合内容拦截会让视频框直接空白。这属于上线配置里最容易被忽略的一层我一般会在切 HTTPS 之后手动点开一路视频验证一下再放用户进来。6. 跑起来之后资源管理、日志和升级的三件套6.1 内存占用高怎么查部署完跑了一周最常见的问题是宿主机内存慢慢涨满。先用docker stats看哪个容器最吃内存。按我的经验WVP 是 Java 进程镜像默认堆大小可能很高可以在启动参数里限制-Xmx1g避免物理内存被整台吃干ZLM 的内存和并发路数、分辨率强相关跑 4K 摄像头时一路流就很占内存商用场景要按路数估算容量MySQL 的内存增长通常是缓冲池变大一般不用管但系统内存不足时它最容易先被 OOM 杀掉。如果某个容器持续 90% 以上优先排查是不是录像并发写入同一个磁盘目录I/O 阻塞表现出来也是内存缓存堆积。6.2 日志和磁盘别等报警才处理WVP 和 ZLM 都会产生日志跑久了磁盘很快爆。两个处理思路容器层面ZLM 日志目录挂载到宿主可以加到 logrotate 按天切割WVP 的日志配置支持按大小滚动改完挂载的配置文件重启生效数据层面ZLM 的录像和抓图默认写持久化目录需要定期清理过期录像。没有合规要求的话保留最近 7 天录像足够。磁盘炸掉最典型的连锁反应是 MySQL 写入失败、WVP 接口报错、摄像头集体下线。我会在部署完成后加一条定时任务监控数据盘使用率超过 80% 自动清理最老的录像文件。6.3 备份与升级的正确姿势备份分两块数据库MySQL 数据目录在 /opt/wvp/data/mysql冷备份可以直接 tar 这个目录但建议用 mysqldump 做逻辑备份恢复更灵活配置/opt/wvp/conf 下的两份配置文件是整个部署的核心资产我会单独备份到一个代码仓库里改坏任何配置随时回滚。升级 WVP 镜像时我的固定操作流程先docker compose down注意不要带 -v数据目录留在宿主机上把配置目录打包留底拉新镜像改 tag 后docker compose up -d启动后看日志重点确认 WVP 与 ZLM 是否握手成功、MySQL 表结构有没有自动迁移。WVP 升级最常见的翻车点是镜像内默认配置变了而你挂载的旧配置文件还在用旧字段。每次升级前扫一眼新版本 release notes 里的配置变更比启动失败后再去翻日志省时间得多。最后讲几点我个人的体会。WVP-Pro 这套 Docker 部署难点从头到尾都不是装上去而是让信令、媒体、存储三层对得上。我处理过几个现场问题几乎都归结为 IP 填错、端口没映射、secret 不一致这三类排查时先盯这三样比翻代码高效得多。实战里我习惯在宿主机常备两条命令docker logs wvp-pro --tail 200 -f盯信令tcpdump -i eth0 udp port 5060 or udp port 10000盯媒体包。日志里出现什么不代表什么信令和媒体两张日志对着看箭头指向哪问题就在哪。这套环境我跑了半年多中途升级过一次镜像数据没丢摄像头在线率和播放成功率都稳定。你要是正打算把 WVP-Pro 容器化照着我这个流程走应该能少折腾两个通宵。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑