wvp-GB28181-pro这个项目,我在安防行业里见过太多人卡在部署这一步了。它本身是一个基于GB28181国标协议的流媒体网关平台,核心能力是把不同厂商的监控摄像头、NVR、视频平台统一接入到一套系统里,再通过WebRTC、HLS这些方式在网页上直接播放。借助Docker部署,可以把整个环境在十几分钟内搭建起来,特别适合从0到1做原型验证,也适合刚接触国标视频接入的开发者、弱电集成商快速跑通整套流程。
我不止一次看到有人在这个项目上栽跟头,有的是MySQL起不来,有的是SIP端口不通,还有的是ZLM流媒体服务和wvp对不上密钥。其实这些坑大多不是项目本身的问题,而是对GB28181这套体系里各个服务之间的协作关系没理清。这篇文章我就用Docker把这套平台从零搭起来,把每一步的配置逻辑和排查思路都讲明白。
1. 部署前先搞懂wvp-GB28181-pro到底在做什么
1.1 GB28181是什么,wvp在这个体系里的位置
很多刚接触这个项目的人容易懵,上来就急着敲docker命令,结果MySQL、Redis、ZLM、wvp四个容器全起来了,摄像头就是注册不上。要理解为什么这么部署,得先搞清楚这个平台在安防体系里到底扮演什么角色。
GB28181是国内视频监控领域普遍采用的设备接入标准,它解决的问题是:不同厂商的摄像头、NVR、视频平台之间,怎么通过统一的信令协议和媒体传输格式互相对接。它不像海康的SDK或者大华的主动注册那样是厂商私有的,而是一套公开的标准协议。协议里边最核心的两个部分,一个是SIP信令,用来做设备注册、心跳、点播请求、云台控制;另一个是RTP媒体流,用来传输视频和音频数据。
wvp-GB28181-pro就是基于这套协议实现的一个网关服务。它内部承担两个角色:对下,它是SIP服务器,摄像头/NVR作为SIP客户端主动注册进来;对上,它提供HTTP接口和Web页面,你可以通过浏览器实时预览、回放、对讲。但wvp本身不做媒体流的转发和分发,真正的流媒体收发是交给ZLMediaKit(下文简称ZLM)去做的。两者配合的方式是:wvp收到播放请求后,通过HTTP告诉ZLM“帮我拉某路流”,ZLM再通过GB28181协议去摄像头那边拉流,拉回来之后做转封装,然后给web端播放器。
所以整套系统必须有数据库存设备信息和录像索引,必须有缓存存会话状态,必须有流媒体服务处理音视频,还必须有一个核心信令服务把这一切串起来。一个docker-compose文件拉起来多个容器,就是这个原因。
1.2 为什么用Docker而不是直接源码部署
wvp-GB28181-pro是Java项目,本身打包好就是一个jar包,理论上装个JDK加两个中间件就能跑。但实际部署时你会发现坑很多:JDK版本要求17以上,用的是ZGC垃圾回收器;Maven打包时依赖下载慢,尤其在国内网络环境下经常卡住;ZLM是用C++写的,编译安装要装一堆依赖库,如果系统版本不对甚至会编译失败。
Docker把这些环境差异都屏蔽掉了。你不需要在宿主机上装JDK、Maven、GCC、CMake,所有依赖都在镜像里。我在生产环境里维护这套系统,最省心的方式就是固定镜像版本,然后通过环境变量和配置文件去改业务参数,恢复和迁移都非常快。这也是我推荐所有人第一次部署wvp时直接走Docker路线的原因,先把业务跑通,再去研究源码细节,效率高得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:Docker安装、镜像加速、目录与网络规划
2.1 宿主机准备和Docker安装
先在宿主机上把Docker装好。我这边是Linux环境,CentOS和Ubuntu都试过。以Ubuntu 20.04/22.04为例,最稳妥的方式是用官方脚本安装,装完是当前最新的社区版:
bash复制curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
如果用的是CentOS 7,需要注意内核版本和存储驱动的问题。CentOS 7自带的3.10内核在跑Docker时,默认的存储驱动可能是overlay2,建议确认一下:
bash复制docker info | grep "Storage Driver"
如果输出不是overlay2,说明系统可能需要升级,或者你需要换一台内核更新的机器。CentOS 7的老内核跑新版本的Docker会有不少边缘问题,特别是之前遇到过容器内时间漂移和网络状态异常的情况。
Windows上部署则用Docker Desktop,安装包官网直接下就行。安装前建议先在控制面板里确认虚拟化功能已开启,否则装完启动时会报“virtualization support wasn't detected”或者“failed to start”。这类报错的原因通常是BIOS里的VT-x/AMD-V没打开,或者Windows的Hyper-V/WSL2功能没启用。打开方式:在“启用或关闭Windows功能”里勾选“Hyper-V”和“适用于Linux的Windows子系统”,重启电脑。Docker Desktop如果装的是新版本,建议用WSL2后端,比Hyper-V后端省内存,启动也快,实测下来资源占用表现更好。
另外我还是建议单独准备一台Linux服务器来跑wvp。因为后面ZLM要开放大量UDP端口给RTP媒体流,Windows的防火墙拦截和网络转发效率都不如Linux,真到了几十路摄像头并发预览的时候,差距会很明显。
2.2 镜像拉不下来怎么办
Docker本身装好只是第一步,国内网络环境下最常见的坑是镜像拉取超时。docker hub在国内访问很不稳定,几GB的镜像经常拉到一半就断。最直接的解决办法是给Docker配置国内镜像加速器。以Linux为例,编辑 /etc/docker/daemon.json:
json复制{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
]
}
改完以后执行:
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
Docker Desktop用户直接在Settings -> Docker Engine里编辑JSON配置,同样加上registry-mirrors字段,然后Apply & Restart。
配置好之后建议先拉一个小镜像测试加速效果:
bash复制docker pull hello-world
能很快拉下来说明加速器生效了。需要说明的是,镜像加速器只对Docker Hub官方镜像生效,如果后续用的是自建私有仓库,那需要在docker login里单独配置认证信息。另外加速服务有时候会调整域名,万一某个源挂了,换一个即可,不用纠结。
2.3 目录结构和网络模式怎么选
正式部署前,先在宿主机上创建好目录结构。我一般习惯把wvp相关的所有数据放在一个目录下,方便备份和迁移:
bash复制mkdir -p /opt/wvp/{mysql,redis,zlm,wvp/sql,wvp/conf}
各个子目录的用途后面会讲到。目录建好以后,有一个很重要的决策点需要提前想清楚:网络模式。
wvp和ZLM这两个服务之间有很多端口交互,不只是TCP,还有大范围的UDP端口用于媒体流传输。Docker的网络模式有两种选择:
第一,host模式,也就是容器直接共享宿主机网络,性能最好,端口不用做映射,容器间通过127.0.0.1或宿主机IP访问。Linux上用这种方式最省心,但Docker Desktop在Windows和macOS上对host网络的支持有限,经常起不来。
第二,bridge模式,也就是容器映射端口宿主机,用自定义网络内的服务名互相访问。跨平台兼容性最好,Windows和Mac上也能跑。
我这篇文章的步骤是按bridge模式写的,因为适用面更广。如果是Linux纯环境,你可以在部署完成后自行切到host模式,配置文件逻辑一样,只是IP和端口写法有变化。
我习惯于先创建自定义网络,而不是使用默认的bridge网络。自定义网络的好处是,容器之间可以用服务名称直接解析IP,比如wvp容器里可以通过 mysql:3306 访问MySQL容器,这样配置文件中写的地址不依赖具体的IP:
bash复制docker network create wvp-net
3. 用docker-compose一次性拉起MySQL、Redis、ZLM、wvp
3.1 MySQL和Redis的基础配置
wvp的数据库默认用的MySQL,版本建议8.0以上。Redis用来存缓存和会话信息,版本可以用7.x。
MySQL部署时有一个细节非常关键:首次启动时的自动初始化。wvp-GB28181-pro在首次启动前需要初始化几张基础表,官方源码的sql目录下提供了初始化脚本。Docker的MySQL镜像支持 /docker-entrypoint-initdb.d 目录挂载,容器第一次创建时会自动执行这个目录下的 .sql 文件。所以你只需要提前把官方SQL脚本放到 sql 目录下,然后在compose里挂载上去就行。
Redis这边我习惯开启AOF持久化,虽然wvp的缓存数据丢了也能重新注册恢复,但如果设备很多,重新注册会刷一大片告警,开了AOF可以避免这个尴尬。我的目录挂在宿主机上,容器重建后数据还在。
下面是我用的MySQL和Redis配置片段:
yaml复制mysql:
image: mysql:8.0
container_name: wvp-mysql
restart: always
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wvp
ports:
- "3306:3306"
volumes:
- /opt/wvp/mysql/data:/var/lib/mysql
- /opt/wvp/sql:/docker-entrypoint-initdb.d
networks:
- wvp-net
redis:
image: redis:7.0
container_name: wvp-redis
restart: always
environment:
TZ: Asia/Shanghai
ports:
- "6379:6379"
volumes:
- /opt/wvp/redis/data:/data
command: redis-server --appendonly yes
networks:
- wvp-net
MySQL容器第一次启动时,/docker-entrypoint-initdb.d 里的SQL文件自动执行,执行完成以后这个目录再挂内容也不会重新执行,所以如果改了SQL后悔了,需要把mysql数据目录里的数据清掉再重建容器。
3.2 ZLM流媒体服务的配置细节
ZLM是整个平台里最吃性能也最需要精细配置的组件。它负责接收摄像头的RTP流,再把流转成RTSP、RTMP、HLS、WebRTC这些浏览器和播放器能直接消费的格式。wvp通过HTTP hook和ZLM交互,ZLM在收到或关闭流时会回调wvp的接口,wvp据此更新设备的在线状态。
我用的ZLM镜像是社区常用的 648540858/zlm。以bridge模式部署时,一定要把媒体流相关的端口暴露出来。下面是我线上用的ZLM容器定义:
yaml复制zlm:
image: 648540858/zlm:latest
container_name: wvp-zlm
restart: always
environment:
TZ: Asia/Shanghai
ports:
- "1935:1935" # RTMP
- "554:554" # RTSP
- "80:80" # HTTP访问流媒体状态
- "10000-10200:10000-10200/tcp"
- "10000-10200:10000-10200/udp"
volumes:
- /opt/wvp/zlm:/opt/media/conf
networks:
- wvp-net
这里有个容易踩的坑:ZLM的RTP媒体端口范围要覆盖足够大,尤其是要支持语音对讲的时候。如果只映射几个端口,摄像头并发上来时,端口不够用就会随机取流失败。我一般设置200个端口起步,也就是10000到10200。后续如果设备很多、并发观看多,就需要调大这个范围,可以在ZLM的config.ini里修改 port_range 配置。
ZLM的配置文件有两种方式处理,一种是在容器启动后用默认配置,另一种是把配置文件挂载出来手动改。官方镜像默认会在 /opt/media/conf/config.ini 里生成配置,我建议启动后先把配置拷贝到宿主机上再挂载,这样后续改端口范围、日志级别都方便。首次启动时可以不用挂载配置文件,直接让容器生成:
bash复制docker run -d --name wvp-zlm-init --network wvp-net 648540858/zlm:latest
docker cp wvp-zlm-init:/opt/media/conf/config.ini /opt/wvp/zlm/config.ini
docker rm -f wvp-zlm-init
这种方式先初始化配置再停止,之后启动正式容器时把配置文件挂进去即可。
3.3 wvp服务自身的配置
wvp的容器配置相对复杂,因为它要同时连MySQL、Redis、ZLM三个服务,还要开放SIP信令端口给摄像头注册。
wvp默认的配置文件是 application.yml 和 application-druid.yml,官方文档里面有完整的样例。我基于实际部署经验,重点讲几个必须改的配置项。
先看 application-druid.yml,主要改数据库连接。在自定义网络里,数据库地址可以直接写容器名:
yaml复制spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql:3306/wvp?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
username: root
password: 123456
再看 application.yml,里面有几个关键配置:
yaml复制server:
port: 8080
sip:
ip: 192.168.1.100
port: 5060
domain: 34020000002000000001
id: 34020000002000000001
password: 12345678
media:
ip: 192.168.1.100
port: 10000
secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
sdp-ip: 192.168.1.100
stream-ip: 192.168.1.100
hook-ip: wvp
hook-port: 8080
redis:
host: redis
port: 6379
这里面最核心的是 sip 和 media 两个段。
sip.ip 填宿主机对外的IP地址,摄像头、NVR都是通过这个IP来注册的。sip.id 和 sip.domain 是SIP服务器ID,正常来说填一个20位数字的国标编码,比如 34020000002000000001。摄像头上配置的SIP服务器ID要与这里一致。
media.secret 是wvp和ZLM之间的认证密钥,必须和ZLM的config.ini里配置的secret一致。这里我习惯用uuid生成器生成一个随机字符串,这样至少有安全保障,不会被猜测。很多人直接复制文档里的默认密钥,两个服务之间倒是能通,但安全性上不太讲究。
media.hook-ip 指向wvp的容器名,这样ZLM能回调到wvp的 /index/hook 接口。如果用的是host模式,这里填 127.0.0.1 即可。
docker-compose里wvp的服务定义是这样的:
yaml复制wvp:
image: 648540858/wvp_pro:latest
container_name: wvp
restart: always
depends_on:
- mysql
- redis
- zlm
environment:
TZ: Asia/Shanghai
ports:
- "8080:8080"
- "5060:5060/udp"
- "5060:5060/tcp"
volumes:
- /opt/wvp/wvp/conf:/root/config
networks:
- wvp-net
SIP的UDP端口映射非常关键,摄像头的注册信令走的是UDP 5060。漏配 5060/udp 会导致设备永远注册不上。这几乎是所有GB28181对接失败里最常见的原因,没有之一。
3.4 完整的docker-compose.yml示例
把上面几个部分拼在一起,完整的编写方式如下。我加了少量注释,方便直接拷贝改参数:
yaml复制version: '3.8'
networks:
wvp-net:
driver: bridge
services:
mysql:
image: mysql:8.0
container_name: wvp-mysql
restart: always
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wvp
ports:
- "3306:3306"
volumes:
- /opt/wvp/mysql/data:/var/lib/mysql
- /opt/wvp/sql:/docker-entrypoint-initdb.d
networks:
- wvp-net
redis:
image: redis:7.0
container_name: wvp-redis
restart: always
environment:
TZ: Asia/Shanghai
ports:
- "6379:6379"
volumes:
- /opt/wvp/redis/data:/data
command: redis-server --appendonly yes
networks:
- wvp-net
zlm:
image: 648540858/zlm:latest
container_name: wvp-zlm
restart: always
environment:
TZ: Asia/Shanghai
ports:
- "1935:1935"
- "554:554"
- "80:80"
- "10000-10200:10000-10200/tcp"
- "10000-10200:10000-10200/udp"
volumes:
- /opt/wvp/zlm:/opt/media/conf
networks:
- wvp-net
wvp:
image: 648540858/wvp_pro:latest
container_name: wvp
restart: always
depends_on:
- mysql
- redis
- zlm
environment:
TZ: Asia/Shanghai
ports:
- "8080:8080"
- "5060:5060/udp"
- "5060:5060/tcp"
volumes:
- /opt/wvp/wvp/conf:/root/config
networks:
- wvp-net
需要注意 /opt/wvp/wvp/conf 这个目录下至少要放 application.yml 和 application-druid.yml 两个文件,否则wvp容器启动时会因为找不到配置而退出。启动命令如下:
bash复制cd /opt/wvp
docker-compose up -d
第一次启动时,MySQL初始化SQL脚本需要一定时间,wvp可能会在MySQL还没就绪时报错退出。这很正常,因为 restart: always 会自动重启,等MySQL初始化完成后wvp会自动恢复。如果想要更稳妥,可以在compose里给wvp加一个健康检查依赖,但实际生产中靠restart策略也能扛住。
4. 启动、登录与第一台摄像头接入
4.1 如何确认四个容器全部就绪
启动完成后,第一步先看容器状态:
bash复制docker ps
正常状态是四个容器都在Up。如果某个容器不断重启,用下面的命令看日志:
bash复制docker logs -f wvp
docker logs -f wvp-mysql
docker logs -f wvp-zlm
wvp启动成功的标志是日志里出现类似 “Welcome to wvp-GB28181-pro” 或Tomcat started的提示,同时没有抛数据库连接异常。如果看到 Access denied for user 说明数据库密码配错了;看到 Unknown database 说明SQL初始化没执行成功。
ZLM启动成功的标志是日志里出现HTTP端口监听和RTSP端口监听的提示。wvp和ZLM的密钥不匹配时,wvp日志会有一堆hook认证失败的信息。
确认各容器正常后,访问wvp控制台:
code复制http://宿主机IP:8080
默认账号admin,密码admin。第一次登录后强烈建议马上改密码,这个项目默认密码是公开的,如果暴露到公网,分分钟被人登进去看摄像头。
4.2 国标设备如何配置和接入
控制台能打开,接下来就是接入摄像头了。以某厂商的IPC为例,进入设备的GB28181配置页面,需要填的字段主要有:
- SIP服务器IP:填wvp所在宿主机的IP
- SIP服务器端口:5060
- SIP服务器ID:填
34020000002000000001 - SIP用户ID:填设备的国标编码,比如
34020000001320000001 - SIP用户认证ID:一般填设备的国标编码
- 密码:设备密码,要和设备本地认证密码保持一致
- 通道编码:设备通道ID,如
34020000001320000001或 13位的通道号
保存后,设备会主动向wvp发起注册。此时在wvp控制台的“国标设备”页面,应该很快看到新设备出现在列表里。如果设备状态显示在线,说明SIP信令已经通了。
设备状态一直离线的话,多半是防火墙挡住了UDP 5060端口。Linux下用firewalld就放行一下:
bash复制firewall-cmd --permanent --add-port=5060/udp
firewall-cmd --permanent --add-port=10000-10200/udp
firewall-cmd --reload
如果是云服务器,还需要在安全组里增加对应规则。我见过一个案例,费了半天劲,结果发现是阿里云安全组只放行了TCP,UDP 5060没放行。
4.3 点播预览和语音对讲的调通
设备上线后,在wvp的国标设备列表里展开通道,点击“播放”,页面会调用wvp的接口向设备发起实时点播请求。设备收到INVITE后,会通过RTP协议把视频流推给ZLM,ZLM收到流后转成WebRTC或HLS推给浏览器。这里能播放成功,说明信令链路和媒体链路全通了。
第一次播放如果黑屏或转圈,不要急着怀疑ZLM,先看wvp日志。日志里会明确告诉你步骤卡在哪,比如 sendRtpPort 超时、rtp receive timeout、hook timeout 等。
rtp receive timeout 说明设备发起了INVITE,但媒体流没到ZLM。排查顺序是:
- 确认宿主机的UDP端口范围已映射且防火墙放行
- 确认摄像头侧没有开启“NAT穿透”或“流媒体加密”
- 确认WVP配置里的
media.ip是摄像头能访问得到的地址
如果摄像头和wvp不在同一网段,media.sdp-ip 字段会导致摄像头把流推到错误的IP,这个字段必须填摄像头能访问到的流媒体服务IP。
语音对讲需要wvp控制台里点“语音对讲”,浏览器会采集麦克风音频,通过WebRTC传给ZLM,ZLM再把音频用GB28181协议推给摄像头。语音对讲调不通时,优先检查摄像头的音频编码格式,部分老设备只支持PCMA/PCMU,浏览器默认的OPUS音频需要转码后才能推给设备。ZLM新版本已经支持音频转码,但需要在config.ini里打开音频转码开关。我在实测过程中发现,语音对讲对网络抖动非常敏感,如果设备是跨公网接入,建议优先用有线网络并在中间网络设备上做好QoS,否则声音会出现严重的卡顿和断续。
5. 常见问题与生产环境避坑实录
5.1 问题排查速查表
我在多个项目里部署过wvp,遇到过的问题反复就那几类,整理成表格供直接对照排查:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 摄像头注册不上,设备离线 | UDP 5060被防火墙拦截,或SIP服务器ID不匹配,或设备填的IP不对 | 在宿主机抓包 tcpdump -i any udp port 5060,看有没有SIP包过来 |
| wvp容器反复重启 | MySQL连接不上,或SQL未初始化,或配置目录没挂载 | 看日志确认异常类型,手动进入MySQL容器执行 show tables 验证 |
| 控制台能登录,但点播黑屏 | ZLM密钥不匹配,RTP端口范围未映射,或 sdp-ip 不可达 |
先看ZLM日志有没有收到流,再在wvp日志里搜 rtp receive timeout |
| 视频卡顿,不停缓冲 | 网络带宽不足,或RTP端口范围太小,或设备推流码率过高 | 用ZLM的web页面看推流速率,尝试降低摄像头码率 |
| 语音对讲只有自己声音没有对方 | 音频编码格式不匹配,或ZLM音频转码未开启 | 查看ZLM配置里音频转码开关,确认摄像头支持G711 |
| Docker Desktop启动失败 | 虚拟化未开启,或WSL2未安装,或Windows版本过低 | 检查BIOS虚拟化,执行 wsl --status 确认WSL2 |
5.2 数据库和缓存相关的坑
wvp的数据库连接配置里,serverTimezone 一定要设置成 Asia/Shanghai。如果不设置,第一次启动时可能会因为时区问题直接报错,即使能启动,录像回放的时间也会差八个小时。这个坑我踩过,数据库里查录像索引一看时间全是乱的,排查了半天。
还有一点是MySQL 8.0的认证插件问题。wvp的数据库连接池用的是Druid,在某些版本上遇到MySQL 8.0默认的 caching_sha2_password 认证会报 Public Key Retrieval is not allowed。解决办法是给wvp数据库用户指定用 mysql_native_password。虽然新版本Druid已经兼容了这个认证插件,但如果你用的是老镜像,遇到这个报错时不要慌,改一下MySQL的用户加密方式就行:
sql复制ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
Redis方面,如果wvp启动后大量设备掉线,或者云台控制指令时灵时不灵,可以检查一下Redis内存是否满了,以及wvp配置的Redis密码是否和容器一致。Redis没设密码时,wvp配置里不要写 password 字段,写了反而会导致认证失败。
5.3 ZLM媒体流的几个隐蔽问题
ZLM这个组件表面上启动很简单,但很多隐蔽问题藏得很深。
第一个是端口范围不连续导致的随机失败。Docker映射端口时,虽然可以写 10000-10200:10000-10200,但如果宿主机防火墙只放行了其中一小段,摄像头多路并发时就会随机出现某一路拉流失败。我建议防火墙直接放行整段RTP端口。
第二个是ZLM的hook串号问题。如果同一台机器上跑了多套wvp环境,每个ZLM必须对应独立的hook地址和独立的secret。我在一个项目中同时跑开发和生产两套环境,结果ZLM的hook都指向了同一个wvp实例,导致生产环境的设备状态显示错乱。
第三个是ZLM日志级别的调整。默认日志可能只记录ERROR级别,某些逐帧异常看不到。排查问题时可以把日志级别临时调到DEBUG,但生产环境不建议长期开DEBUG,日志量会非常大,肉眼根本看不完,还拖慢性能。我踩过几次坑之后,现在的做法是遇到问题先抓包确认信令是否正常,再决定是否开DEBUG。
5.4 容器重启策略和数据备份
restart: always 在绝大多数场景下是够用的,但它解决不了“配置改错”这种人为问题。docker-compose是很方便,但在改完配置之后需要 docker-compose up -d 重新创建容器,而不是 docker restart。因为卷挂载和端口映射只在容器创建时生效,restart只是重启进程,不会重新加载这些配置。
数据备份这件事,我吃过亏。某次升级wvp版本时,我把整个 /opt/wvp/mysql/data 目录误删了,设备注册信息和录像索引全没了。后来养成了每天定时备份数据库和配置文件的习惯,比如用cron每小时把 /opt/wvp 目录打包到另一块磁盘。恢复的流程也很简单:把备份复制回去,重新启动compose即可。
ZLM的config.ini里还有一个容易被忽略的字段,record 相关配置。如果开启录像存储,建议把录像目录单独挂一个大容量磁盘,不要让docker的数据目录和录像目录共用一块盘。录像文件都是大文件,时间长了很占空间,我曾经配过录像存储到了系统盘,最后把系统盘写满了,整个服务全挂了。
5.5 公网部署要注意的几个点
如果你的摄像头需要跨公网注册到wvp,有几个地方需要额外注意。
一是SIP信令的NAT穿透。公网设备注册到内网部署的wvp时,SIP响应里的Contact头可能携带内网IP,导致设备无法收到响应。wvp的 sip.ip 字段要填公网可达的IP,最好在路由器上把5060的UDP端口映射到宿主机。
二是RTP流的接收。设备通过公网向ZLM推流时,sdp-ip 字段决定了设备往哪里推流,必须填一个公网可达的IP。如果ZLM部署在内网,需要在路由器上映射大范围的UDP端口,并且ZLM的 rtp.port_range 要和路由器映射的端口范围一致。
三是安全问题。wvp控制台和管理接口不建议直接暴露到公网,我用Nginx反代加了一个访问认证,只开放需要的路径。SIP端口虽然不得不暴露给设备,但可以在防火墙上做白名单,只允许已知摄像头IP访问5060端口。
6. 一些我在实际运维中的体会
这套平台用Docker部署确实把复杂度降了一个数量级,但我后来发现,真正决定系统稳不稳的,不是docker命令敲得熟不熟,而是对GB28181信令流程和媒体流的理解。我见过有人能把四个容器全部正常启动,但摄像头注册不进来,一查是SIP的domain配置和摄像头不一致;也见过点播正常但录像回放失败,原因是ZLM的录像存储目录没有权限。这些配置问题,光看日志很难一眼定位,得把SIP的注册流程、INVITE流程、RTP接收流程一步步拆开理解,才能快速缩小排查范围。
另外,如果你在Windows的Docker Desktop上按这篇文章操作,需要特别注意端口映射的UDP配置和防火墙放行。Windows上配置UDP端口范围映射不是不行,但对大范围UDP端口的处理效率确实一般。我自己后来专门搞了一台Ubuntu机器来跑wvp,Docker Desktop只用来做开发和验证,生产环境还是建议Linux宿主机。
版本升级这个事也提醒一句:wvp-GB28181-pro迭代速度很快,不同版本的配置文件字段有差异。如果你用的是最新镜像,建议先看对应版本的官方文档,不要拿旧版配置文件生搬硬套。社区里流传的配置文件很多是旧版的,照抄会出现新的报错。我前阵子升级时,发现新版把部分配置从 application.yml 挪到了数据库表里,一开始没注意,折腾了半小时才定位到问题。
最后再分享一个小技巧:每次改完配置之前,先把当前的配置文件和镜像版本记录下来,写一个简单的部署文档放在项目目录里。这看起来麻烦,但当你需要回滚版本或者在新机器上复现环境时,这份记录能省下大量时间。我现在的做法是在 /opt/wvp 目录下建一个 CHANGELOG.md,每改一次配置就追加一行。这个习惯在维护多个现场项目时尤其好用,毕竟现场环境不能随便去试错,每一步都必须有据可查。
