30 KiB
部署指南(物理机)
一、环境信息
| 项目 | 值 |
|---|---|
| 机器 | Acer TravelMate P249-G2-MG(物理机) |
| OS | Ubuntu 22.04.5 LTS |
| 内核 | 6.8.0-124-generic |
| 磁盘 | 387GB(/dev/sda6),可用 356GB |
| 内存 | 16GB |
| 网络 | 192.168.1.4(物理网卡 enp4s0)+ 100.83.103.1(WireGuard VPN wt0) |
| SSH | pan@100.83.103.1 密码 pan |
与 WSL2 部署的关键区别:无需 portproxy、无需 WSL2 IP 修复、文件系统不会变只读、Ceph 需从头配置。
二、服务架构
浏览器
│
├── http://100.83.103.1:5174 → 前端 (Node.js server.cjs)
├── http://100.83.103.1:3000 → Go 后端 API
├── http://100.83.103.1:7480 → Ceph S3 RGW
├── http://100.83.103.1:18080 → WVP 管理界面
│
▼
物理机 (100.83.103.1)
├── sshd :22 SSH 服务
├── Docker
│ ├── polaris-wvp :18080/:18978/:8116 GB28181 信令(18080管理界面/18978HTTP API/8116 SIP)
│ ├── polaris-media :8081/:10000 ZLMediaKit 流媒体
│ ├── polaris-mysql :3306 WVP 数据库
│ ├── polaris-redis WVP 缓存(Docker 内部网络)
│ └── silk-redis :6379 Go 后端缓存(密码 pan)
├── Ceph
│ ├── Monitor (a)
│ ├── Mgr (a)
│ ├── OSD 0/1 (loop device, 各 50GB, 共 100GB)
│ └── RGW :7480 S3 兼容存储
├── PostgreSQL 16 :5432 业务数据库
├── IoTDB 2.0.8 :18081 时序数据库
├── VerneMQ :1883 MQTT broker
├── Go 后端 :3000 silk-server-go
├── Go 录制服务 :9090 recorder-go
├── AI 推理服务 :8000 ai-service(FastAPI + ONNX Runtime,mock/onnx)
└── 前端 :5174 server.cjs
三、全新部署步骤
1. 安装系统依赖
# SSH 到服务器
ssh pan@100.83.103.1
# 更新 apt
sudo apt update
# 基础工具
sudo apt install -y curl wget git
# ffmpeg(视频转码)
sudo apt install -y ffmpeg
# PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# JDK 17(IoTDB 依赖)
sudo apt install -y openjdk-17-jdk-headless
# Docker(阿里云镜像源)
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.aliyun.com/docker-ce/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Docker 镜像加速
sudo mkdir -p /etc/docker
cat << 'EOF' | sudo tee /etc/docker/daemon.json
{
"registry-mirrors": ["https://docker.1panel.live", "https://hub.rat.dev"]
}
EOF
sudo systemctl restart docker
# 将 pan 加入 docker 组
sudo usermod -aG docker pan
# Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Go 1.23
cd /tmp
wget https://golang.google.cn/dl/go1.23.4.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
2. 拉取 Docker 镜像
sudo docker pull mysql:8
sudo docker pull redis:latest
sudo docker pull zlmediakit/zlmediakit:master
3. 启动 Docker 容器
# 创建网络
sudo docker network create media-net
# MySQL(WVP 数据库)
sudo docker run -d --name polaris-mysql --network media-net --restart unless-stopped \
-e MYSQL_DATABASE=wvp -e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_USER=wvp_user -e MYSQL_PASSWORD=wvp_password -e TZ=Asia/Shanghai \
-v /home/pan/silk/wvp/volumes/mysql/data:/var/lib/mysql \
-p 3306:3306 \
mysql:8 --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --default-time-zone=+8:00 --lower-case-table-names=1
# Redis(WVP 缓存,Docker 内部网络)
sudo docker run -d --name polaris-redis --network media-net --restart unless-stopped \
-e TZ=Asia/Shanghai \
redis:latest redis-server --appendonly yes --requirepass root
# ZLMediaKit
mkdir -p /home/pan/silk/wvp/media /home/pan/silk/wvp/volumes/video /home/pan/silk/wvp/logs/media
cat > /home/pan/silk/wvp/media/config.ini << 'EOF'
[api]
apiDebug=1
secret=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
[http]
port=80
charSet=utf-8
[rtsp]
port=5540
[rtmp]
port=10935
[rtp_proxy]
port=10000
[record]
appName=record
sampleMS=100
[hls]
segDur=2
segNum=3
[hook]
enable=0
EOF
sudo docker run -d --name polaris-media --network media-net --restart always \
-e TZ=Asia/Shanghai \
-p 8081:80 -p 5540:5540 -p 10935:10935 -p 10000:10000 -p 10000:10000/udp \
-v /home/pan/silk/wvp/volumes/video:/opt/media/bin/www/record/ \
-v /home/pan/silk/wvp/logs/media:/opt/media/log/ \
-v /home/pan/silk/wvp/media/config.ini:/conf/config.ini \
zlmediakit/zlmediakit:master MediaServer -c /conf/config.ini -l 0
# Redis(Go 后端缓存,端口 6379,密码 pan)
sudo docker run -d --name silk-redis --restart always \
-p 6379:6379 \
redis:latest redis-server --requirepass pan --appendonly yes
4. 配置 PostgreSQL
sudo service postgresql start
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'pan';"
sudo -u postgres psql -c "CREATE DATABASE silk;"
5. 安装 Ceph
添加APT密钥
要安装 release.asc 密钥,请执行以下操作
wget -q -O- 'https://download.ceph.com/keys/release.asc' | sudo tee /etc/apt/trusted.gpg.d/ceph.asc
添加软件源
将 Ceph 软件包仓库添加到系统的 APT 源列表中。对于较新版本的 Debian/Ubuntu,在命令行上调用 lsb_release -sc 以获取短代号,并替换以下命令中的 {codename}
sudo apt-add-repository 'deb https://download.ceph.com/debian-tentacle/ {codename} main'
安装
# 安装 Ceph(Ubuntu 默认源,Quincy 17.2)
sudo apt install -y ceph ceph-common radosgw ceph-volume
# 生成配置
FSID=$(uuidgen)
sudo bash -c "cat > /etc/ceph/ceph.conf << EOF
[global]
fsid = $FSID
mon initial members = a
mon host = 127.0.0.1:6789
osd pool default size = 1
osd pool default min size = 1
osd crush chooseleaf type = 0
mon allow insecure global id reclaim = true
auth_cluster_required = none
auth_service_required = none
auth_client_required = none
[mon.a]
host = localhost
mon addr = 127.0.0.1:6789
[mgr.a]
host = localhost
[client.rgw.a]
host = localhost
rgw frontends = beast port=7480
EOF"
# 创建 Monitor
sudo ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon "allow *"
sudo ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon "allow *" --cap osd "allow *" --cap mds "allow *" --cap mgr "allow *"
sudo ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring
sudo monmaptool --create --add a 127.0.0.1:6789 --fsid $FSID /tmp/monmap
sudo mkdir -p /var/lib/ceph/mon/ceph-a
sudo chown -R ceph:ceph /var/lib/ceph/mon/ceph-a
sudo ceph-mon --mkfs -i a --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring
sudo ceph-mon -i a
sleep 10
# 创建 Manager
sudo mkdir -p /var/lib/ceph/mgr/ceph-a
sudo chown -R ceph:ceph /var/lib/ceph/mgr/ceph-a
sudo bash -c 'ceph auth get-or-create mgr.a mon "allow profile mgr" osd "allow *" mds "allow *" > /var/lib/ceph/mgr/ceph-a/keyring'
sudo ceph-mgr -i a
sleep 3
# 创建 OSD(loop device 方式)
for i in 0 1; do
sudo fallocate -l 50G /var/lib/ceph/osd-data-$i
LOOP_DEV=$(sudo losetup -f --show /var/lib/ceph/osd-data-$i)
sudo pvcreate -f $LOOPfd _DEV
sudo vgcreate ceph-osd-vg-$i $LOOP_DEV
sudo lvcreate -l 100%FREE -n osd-$i ceph-osd-vg-$i
sudo ceph-volume lvm prepare --bluestore --data ceph-osd-vg-$i/osd-$i
done
sudo ceph-volume lvm activate --all
sleep 15
# 启动 RGW
sudo mkdir -p /var/lib/ceph/radosgw/ceph-rgw.a
sudo chown -R ceph:ceph /var/lib/ceph/radosgw/ceph-rgw.a
sudo bash -c 'ceph auth get-or-create client.rgw.a osd "allow rwx" mon "allow rw" -o /var/lib/ceph/radosgw/ceph-rgw.a/keyring'
sudo bash -c 'nohup radosgw -n client.rgw.a -k /var/lib/ceph/radosgw/ceph-rgw.a/keyring --rgw-frontends "beast port=7480" > /var/log/ceph/radosgw.log 2>&1 &'
sleep 5
# 创建 S3 用户
sudo radosgw-admin user create --uid=silk-app --display-name="Silk App" \
--access-key=silk-app --secret-key=Silk-App-Secret-2026!
# 验证
sudo ceph -s
curl -s -o /dev/null -w "%{http_code}" http://localhost:7480/ # 应返回 200
Ceph 关键点:
- BlueStore 需要块设备,不能直接用目录
- 用
fallocate+losetup创建 loop device,再用ceph-volume lvm prepare创建 OSDauth_*_required = none简化认证(单节点测试环境)osd pool default size = 1适配单节点(默认 3 副本会导致 PG 一直 inactive)
6. 安装 VerneMQ
# 上传 deb 包
# pscp -pw pan e:\silk\tools\vernemq-1.13.0.jammy.x86_64.deb pan@100.83.103.1:/home/pan/
sudo dpkg -i /home/pan/vernemq-1.13.0.jammy.x86_64.deb
# 接受 EULA
sudo sed -i 's/^accept_eula.*/accept_eula = yes/' /etc/vernemq/vernemq.conf
sudo bash -c 'echo "accept_eula = yes" >> /etc/vernemq/vernemq.conf'
# 启动
sudo vernemq start
7. 安装 IoTDB
# 上传 IoTDB(含已有数据)
# pscp -r -pw pan e:\silk\tools\iotdb\apache-iotdb-2.0.8-all-bin pan@100.83.103.1:/home/pan/iotdb/
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
# 清理旧锁文件(从 Windows 上传的文件可能需要)
find /home/pan/iotdb/apache-iotdb-2.0.8-all-bin/data -name "*.lock" -delete
# 设置 REST 端口为 18081(避免与 WVP 的 18080 冲突)
IOTDB_CONF=/home/pan/iotdb/apache-iotdb-2.0.8-all-bin/conf/iotdb-system.properties
grep -q "rest_service_port" $IOTDB_CONF && sed -i 's/^rest_service_port.*/rest_service_port=18081/' $IOTDB_CONF || echo "rest_service_port=18081" >> $IOTDB_CONF
# 启动
nohup /home/pan/iotdb/apache-iotdb-2.0.8-all-bin/sbin/start-standalone.sh > /home/pan/iotdb.log 2>&1 < /dev/null &
Valkey 不可用:
valkey-8.1.2-noble-x86_64.tar.gz编译于 Ubuntu 24.04(GLIBC 2.38),Ubuntu 22.04 只有 GLIBC 2.35。用 Docker Redis 替代。
8. 构建 WVP Docker 镜像
# 克隆 WVP 源码
cd /home/pan/silk/wvp
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git wvp-src
cd wvp-src
# 使用自定义 Dockerfile(带 Maven 阿里云镜像加速)
# 从 Windows 上传: pscp -pw pan e:\silk\wvp\Dockerfile pan@100.83.103.1:/home/pan/silk/wvp/wvp-src/Dockerfile
sudo docker build -t docker-polaris-wvp:latest .
# 导入 WVP 数据库初始化 SQL
sudo docker exec -i polaris-mysql mysql -uroot -proot wvp < "数据库/2.7.4/初始化-mysql-2.7.4.sql"
# 授予 MySQL 权限
sudo docker exec polaris-mysql mysql -uroot -proot -e "GRANT ALL PRIVILEGES ON *.* TO 'wvp_user'@'%'; FLUSH PRIVILEGES;"
9. 配置并启动 WVP
# 创建 WVP 配置
mkdir -p /home/pan/silk/wvp/wvp/wvp /home/pan/silk/wvp/logs/wvp
cat > /home/pan/silk/wvp/wvp/wvp/application.yml << 'EOF'
spring:
application:
name: wvp
servlet:
multipart:
max-file-size: 10GB
max-request-size: 10GB
cache:
type: redis
data:
redis:
host: polaris-redis
port: 6379
password: root
timeout: 10000
database: 6
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://polaris-mysql:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: wvp_user
password: wvp_password
hikari:
connection-timeout: 20000
maximum-pool-size: 30
minimum-idle: 5
server:
port: 18080
sip:
ip: 0.0.0.0
port: 8116
domain: 3402000000
id: 34020000002000000001
password: 12345678
media:
id:
ip: polaris-media
http-port: 80
secret: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
rtp:
enable: true
port-range: 30000,30500
send-port-range: 30000,30500
record-assist-port: 18081
sdp-ip: 100.83.103.1
stream-ip: 100.83.103.1
user-settings:
record-push-live: true
auto-apply-play: true
push-authority: true
use-push-url: false
use-wvp-host: true
enable-fmp4-fallback: true
record-sip: true
enable-audio: true
logging:
level:
root: INFO
EOF
# 启动 WVP 容器
sudo docker run -d --name polaris-wvp --network media-net --restart always \
-p 18080:18080 -p 8116:8116/tcp -p 8116:8116/udp \
-v /home/pan/silk/wvp/wvp/wvp:/opt/ylcx/wvp/ \
-v /home/pan/silk/wvp/logs/wvp:/opt/wvp/logs/ \
-e TZ=Asia/Shanghai \
-e Stream_IP=100.83.103.1 \
-e SDP_IP=100.83.103.1 \
-e ZLM_HOST=polaris-media \
-e ZLM_SERCERT=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf \
-e REDIS_HOST=polaris-redis \
-e DATABASE_HOST=polaris-mysql \
-e SIP_ShowIP=100.83.103.1 \
docker-polaris-wvp:latest
WVP 端口变更:WVP 2.7.4 的
server.port是 18080(不是之前的 18978)。Go 后端.env中WVP_API_BASE需改为http://localhost:18080。
10. 部署 Go 后端
# 在 Windows 上交叉编译
# cd e:\silk\server-go
# $env:GOOS="linux"; $env:GOARCH="amd64"; go build -o silk-server-go-linux ./cmd/server/
# pscp -pw pan silk-server-go-linux pan@100.83.103.1:/home/pan/
chmod +x /home/pan/silk-server-go-linux
# 创建 .env
cat > /home/pan/silk/.env << 'EOF'
PORT=3000
PG=postgresql://postgres:pan@localhost:5432/silk
REDIS=redis://:pan@localhost:6379
MQTT=mqtt://pan:pan@localhost:1883
IOTDB_URL=http://127.0.0.1:18081
S3_ENDPOINT=http://100.83.103.1:7480
S3_ACCESS_KEY=silk-app
S3_SECRET_KEY=Silk-App-Secret-2026!
S3_BUCKET_EVENTS=silk-video-events
S3_BUCKET_ARCHIVE=silk-video-archive
WVP_API_BASE=http://localhost:18080
WVP_USERNAME=admin
WVP_PASSWORD=admin
ZLM_API_BASE=http://localhost:8081
ZLM_SECRET=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
INTERNAL_API_KEY=silk-internal-2026
RECORDER_API_BASE=http://localhost:9090
EOF
# 启动
cd /home/pan/silk
nohup /home/pan/silk-server-go-linux > server-go.log 2>&1 < /dev/null &
11. 部署 Go 录制服务
# 在 Windows 上交叉编译
# cd e:\silk\wvp\recorder-go
# $env:GOOS="linux"; $env:GOARCH="amd64"; go build -o recorder-go-linux .
# pscp -pw pan recorder-go-linux pan@100.83.103.1:/home/pan/
chmod +x /home/pan/recorder-go-linux
nohup /home/pan/recorder-go-linux > /home/pan/recorder.log 2>&1 < /dev/null &
12. 部署前端
# 在 Windows 上构建
# cd e:\silk\web; npm run build
# tar -czf dist.tar.gz dist
# pscp -pw pan dist.tar.gz pan@100.83.103.1:/home/pan/
# pscp -pw pan e:\silk\wvp\recorder-go\server.js pan@100.83.103.1:/home/pan/silk/web/server.cjs
mkdir -p /home/pan/silk/web
cd /home/pan/silk/web
tar xzf /home/pan/dist.tar.gz
# 启动
nohup node /home/pan/silk/web/server.cjs > /home/pan/silk/web/web.log 2>&1 < /dev/null &
server.cjs 命名:
package.json中"type": "module"会导致.js文件被当作 ES Module 解析。server.js使用require()(CommonJS),需重命名为server.cjs。
13. 部署 ai-service(2026-08 新增)
# 上传源码 tar 包(本机 scripts/devssh.py --upload ai-service.tar.gz /home/pan/)
mkdir -p /home/pan/ai-service
tar xzf /home/pan/ai-service.tar.gz -C /home/pan
cd /home/pan/ai-service
# 前置:python3-venv(如缺失)
sudo apt install -y python3.10-venv
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt # 服务器 pip 已配置清华镜像
cat > start.sh << 'EOF'
#!/bin/bash
cd /home/pan/ai-service
export MODEL_MODE=mock
export MOCK_CLASS=healthy
nohup .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 > ai-service.log 2>&1 < /dev/null &
echo STARTED
EOF
chmod +x start.sh && bash start.sh
sleep 4
curl -s http://localhost:8000/health # {"status":"ok","model":"mock"}
curl -s http://localhost:8000/metrics # 含请求量/延迟/GPU(nvidia-smi)
训练恢复后:放置
models/best.onnx并把MODEL_MODE改为onnx重启即可(接口不变)。Go 后端通过AI_SERVICE_BASE(默认http://localhost:8000)对接。
四、验证
# 检查所有端口
ss -tlnp | grep -E '22|3000|5174|6379|1883|7480|8081|9090|18080|18081|8116'
# Go 后端
curl -s http://localhost:3000/api/v1/health
# 应返回 {"status":"ok"}
# 录制服务
curl -s http://localhost:9090/health
# 应返回 {"status":"ok"}
# 前端
curl -s -o /dev/null -w '%{http_code}' http://localhost:5174/
# 应返回 200
# ZLMediaKit
curl -s -o /dev/null -w '%{http_code}' http://localhost:8081/
# 应返回 200
# WVP
curl -s -o /dev/null -w '%{http_code}' http://localhost:18080/
# 应返回 404(正常,API 在具体路径下)
# Ceph S3
curl -s -o /dev/null -w '%{http_code}' http://localhost:7480/
# 应返回 200
# Docker 容器
sudo docker ps --format "{{.Names}}: {{.Status}}"
# 应显示 5 个容器全部 Up
# Ceph 集群
sudo ceph -s
# 应显示 2 osds: 2 up, 2 in
五、关键配置文件
Go 后端 .env (/home/pan/silk/.env)
PORT=3000
PG=postgresql://postgres:pan@localhost:5432/silk
REDIS=redis://:pan@localhost:6379
MQTT=mqtt://pan:pan@localhost:1883
IOTDB_URL=http://127.0.0.1:18081
S3_ENDPOINT=http://100.83.103.1:7480
S3_ACCESS_KEY=silk-app
S3_SECRET_KEY=Silk-App-Secret-2026!
WVP_API_BASE=http://localhost:18080
ZLM_API_BASE=http://localhost:8081
ZLM_SECRET=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
RECORDER_API_BASE=http://localhost:9090
WVP application.yml (/home/pan/silk/wvp/wvp/wvp/application.yml)
关键配置:
sip.port: 8116sip.domain: 3402000000sip.id: 34020000002000000001media.ip: polaris-media(Docker 内部网络)media.sdp-ip/media.stream-ip: 100.83.103.1(VPN IP,摄像头需要访问)server.port: 18080
Ceph (/etc/ceph/ceph.conf)
auth_*_required = none(单节点简化认证)osd pool default size = 1(单副本)
六、与 WSL2 部署的差异
| 项目 | WSL2 | 物理机 |
|---|---|---|
| 文件系统 | ext4 on VHDX(可能变只读) | ext4 on /dev/sda6(稳定) |
| 网络 | 需要 portproxy 转发 | 直连,无需 portproxy |
| IP | 172.18.127.247(可能变化) | 100.83.103.1(WireGuard 固定) |
| WVP 端口 | 18978 | 18080 |
| 缓存 | Valkey(裸机安装) | Docker Redis(Valkey GLIBC 不兼容) |
| Ceph | 已配置(可直接启动) | 需从头配置(loop device OSD) |
| WVP 镜像 | 已构建 | 需从 Gitee 克隆源码构建 |
七、重启后恢复
物理机重启后,systemd 管理的服务(Docker、PostgreSQL、VerneMQ)会自动启动。需要手动启动的服务:
# 1. Ceph(systemd 管理,但 loop device 需要手动激活)
sudo ceph-mon -i a
sudo ceph-mgr -i a
# loop device 需要重新挂载
for i in 0 1; do
sudo losetup /dev/loop$((i+8)) /var/lib/ceph/osd-data-$i
sudo ceph-volume lvm activate --all
done
# RGW
sudo bash -c 'nohup radosgw -n client.rgw.a -k /var/lib/ceph/radosgw/ceph-rgw.a/keyring --rgw-frontends "beast port=7480" > /var/log/ceph/radosgw.log 2>&1 &'
# 2. IoTDB
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
nohup /home/pan/iotdb/apache-iotdb-2.0.8-all-bin/sbin/start-standalone.sh > /home/pan/iotdb.log 2>&1 < /dev/null &
# 3. Go 后端
cd /home/pan/silk
nohup /home/pan/silk-server-go-linux > server-go.log 2>&1 < /dev/null &
# 4. Go 录制服务
nohup /home/pan/recorder-go-linux > /home/pan/recorder.log 2>&1 < /dev/null &
# 5. 前端
nohup node /home/pan/silk/web/server.cjs > /home/pan/silk/web/web.log 2>&1 < /dev/null &
摄像头注册信息
摄像头 GB28181 注册参数:
| 参数 | 值 | 说明 |
|---|---|---|
| SIP 服务器 IP | 100.83.103.1 |
WireGuard VPN IP |
| SIP 服务器端口 | 8116 |
TCP/UDP,Docker 映射 -p 8116:8116/tcp -p 8116:8116/udp |
| SIP 域 | 3402000000 |
|
| SIP 服务器 ID | 34020000002000000001 |
|
| SIP 密码 | 12345678 |
摄像头需能访问
100.83.103.1(WireGuard VPN)。若摄像头不在 VPN 网络,改用物理网卡 IP192.168.1.4,同时修改 WVP 配置中的sip.ip、media.sdp-ip、media.stream-ip。
八、常见问题
1. Valkey GLIBC 不兼容
现象:valkey-cli: /lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC_2.38' not found
原因:valkey-8.1.2-noble-x86_64.tar.gz 编译于 Ubuntu 24.04(Noble),需要 GLIBC 2.38,Ubuntu 22.04 只有 2.35。
解决:用 Docker Redis 替代(docker run -d --name silk-redis -p 6379:6379 redis:latest redis-server --requirepass pan)。
2. Ceph OSD 创建失败
现象:bluestore _read_bdev_label failed to open /var/lib/ceph/osd/ceph-0/block: (2) No such file or directory
原因:BlueStore 需要块设备,不能直接用目录。
解决:用 fallocate 创建文件 + losetup 创建 loop device + ceph-volume lvm prepare --bluestore --data <vg/lv>。
3. WVP 启动报 Could not resolve placeholder 'media.id'
原因:application.yml 中 media 和 sip 应在顶层,不是 user-settings 下。
解决:参考步骤 9 的 application.yml 模板。
4. WVP 报 Failed to obtain JDBC Connection
原因:MySQL 未导入 WVP 初始化 SQL,或 wvp_user 权限不足。
解决:
docker exec -i polaris-mysql mysql -uroot -proot wvp < "数据库/2.7.4/初始化-mysql-2.7.4.sql"
docker exec polaris-mysql mysql -uroot -proot -e "GRANT ALL PRIVILEGES ON *.* TO 'wvp_user'@'%'; FLUSH PRIVILEGES;"
5. server.js 报 Cannot use import statement outside a module
原因:package.json 中 "type": "module" 导致 .js 文件按 ES Module 解析,但 server.js 使用 CommonJS (require())。
解决:将 server.js 重命名为 server.cjs。
6. Docker 镜像拉取失败
现象:could not fetch content descriptor ... not found
解决:配置 Docker 镜像加速(/etc/docker/daemon.json 中设置 registry-mirrors)。
7. Ceph OSD 重启后不自动激活
原因:loop device 重启后需要重新挂载。 解决:参考"七、重启后恢复"中的 Ceph 部分。
九、前端测试
-
地址:
http://100.83.103.1:5174/ -
用户名:
admin -
密码:
admin123 -
WVP 管理: http://100.83.103.1:18080/
十、实时视频"等待视频流"问题排查与修复
现象
前端 /videos 页面点击查看实时视频,一直显示"等待视频流...",控制台报 POST /api/v1/video/cameras/1/live 503。点击"开始录制"同样报 503。
根因(共 4 个问题,需全部修复)
| # | 问题 | 原因 |
|---|---|---|
| 1 | Go 后端调用 WVP 端口错误 | config.go 中 envDefault:"http://localhost:18978",Go 后端用 caarlos0/env 读 OS 环境变量而非 .env 文件,.env 未被加载 |
| 2 | ZLMediaKit hooks 指向 127.0.0.1:18080 |
WVP 每次启动时会重置 ZLMediaKit hooks,用 127.0.0.1(容器自身)而非 polaris-wvp(Docker 网络主机名) |
| 3 | ZLMediaKit mediaServerId 为空 |
WVP 配置 media.id: 为空,推流时 on_publish hook 报"找不到对应的mediaServer" |
| 4 | 流 URL 含 Docker 内部主机名 | WVP 返回的播放地址用 polaris-media:80,fixZlmPort 只替换端口不替换主机名,浏览器无法解析 polaris-media |
修复方法
1. Go 后端加载 .env 环境变量
Go 后端用 caarlos0/env 库读取 OS 环境变量,不自动读 .env 文件。启动时必须先 source .env:
cd /home/pan/silk
set -a
source .env
set +a
nohup /home/pan/silk-server-go-linux > server-go.log 2>&1 < /dev/null &
确保 .env 中 WVP_API_BASE=http://localhost:18080(不是 18978)。
2. 修复 ZLMediaKit hooks(每次 WVP 重启后需重新执行)
WVP 启动时会将 ZLMediaKit hooks 重置为 127.0.0.1:18080,需要手动改为 polaris-wvp:18080:
SECRET="su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf"
HOOK_BASE="http://polaris-wvp:18080"
curl -s "http://localhost:8081/index/api/setServerConfig?secret=$SECRET" \
--data-urlencode "hook.on_play=$HOOK_BASE/index/hook/on_play" \
--data-urlencode "hook.on_publish=$HOOK_BASE/index/hook/on_publish" \
--data-urlencode "hook.on_record_mp4=$HOOK_BASE/index/hook/on_record_mp4" \
--data-urlencode "hook.on_rtp_server_timeout=$HOOK_BASE/index/hook/on_rtp_server_timeout" \
--data-urlencode "hook.on_send_rtp_stopped=$HOOK_BASE/index/hook/on_send_rtp_stopped" \
--data-urlencode "hook.on_server_keepalive=$HOOK_BASE/index/hook/on_server_keepalive" \
--data-urlencode "hook.on_server_started=$HOOK_BASE/index/hook/on_server_started" \
--data-urlencode "hook.on_stream_changed=$HOOK_BASE/index/hook/on_stream_changed" \
--data-urlencode "hook.on_stream_none_reader=$HOOK_BASE/index/hook/on_stream_none_reader" \
--data-urlencode "hook.on_stream_not_found=$HOOK_BASE/index/hook/on_stream_not_found"
注意:每次 WVP 容器重启后都需重新执行此命令。可将此命令加入启动脚本。
3. 设置固定的 mediaServerId
在 WVP application.yml 中设置 media.id:
media:
id: silk-media-001 # 不能为空
同时通过 ZLMediaKit API 设置:
curl -s "http://localhost:8081/index/api/setServerConfig?secret=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf" \
--data-urlencode "general.mediaServerId=silk-media-001"
4. 修复流 URL 中的 Docker 内部主机名
修改 server-go/internal/service/media.go 中的 fixZlmPort 函数,增加主机名替换:
func fixZlmPort(u string) string {
if u == "" {
return u
}
// 替换 Docker 内部主机名为外部可访问 IP
u = strings.Replace(u, "polaris-media:", "100.83.103.1:", 1)
u = strings.Replace(u, ":80/", ":8081/", 1)
if strings.HasSuffix(u, ":80") {
u = u[:len(u)-2] + ":8081"
}
return u
}
重新交叉编译并部署:
cd e:\silk\server-go
$env:GOOS="linux"; $env:GOARCH="amd64"
go build -o silk-server-go-linux ./cmd/server/
$env:GOOS=""; $env:GOARCH=""
pscp -pw pan silk-server-go-linux pan@100.83.103.1:/home/pan/silk-server-go-linux
验证
修复后 WVP 日志应显示:
[ZLM-连接成功] ID:silk-media-001, 地址: polaris-media:80
[ZLM HOOK]推流鉴权-允许-响应:silk-media-001
[ZLM HOOK] 流注册, silk-media-001->rtsp->rtp/...
[点播成功] 设备编号: 34020000002000000001
Go 后端日志应显示:
INFO 调用 WVP play/start url=http://localhost:18080/api/play/start/...
INFO 请求 method=POST path=/api/v1/video/cameras/1/live status=200 latency=1.7s
前端播放器收到的流地址格式:
http://100.83.103.1:8081/rtp/34020000002000000001_34020000001310000001.live.flv
WVP 重启后的完整修复脚本
将以下脚本保存为 /home/pan/fix-hooks.sh,每次 WVP 重启后执行:
#!/bin/bash
SECRET="su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf"
HOOK_BASE="http://polaris-wvp:18080"
curl -s "http://localhost:8081/index/api/setServerConfig?secret=$SECRET" \
--data-urlencode "hook.on_play=$HOOK_BASE/index/hook/on_play" \
--data-urlencode "hook.on_publish=$HOOK_BASE/index/hook/on_publish" \
--data-urlencode "hook.on_record_mp4=$HOOK_BASE/index/hook/on_record_mp4" \
--data-urlencode "hook.on_rtp_server_timeout=$HOOK_BASE/index/hook/on_rtp_server_timeout" \
--data-urlencode "hook.on_send_rtp_stopped=$HOOK_BASE/index/hook/on_send_rtp_stopped" \
--data-urlencode "hook.on_server_keepalive=$HOOK_BASE/index/hook/on_server_keepalive" \
--data-urlencode "hook.on_server_started=$HOOK_BASE/index/hook/on_server_started" \
--data-urlencode "hook.on_stream_changed=$HOOK_BASE/index/hook/on_stream_changed" \
--data-urlencode "hook.on_stream_none_reader=$HOOK_BASE/index/hook/on_stream_none_reader" \
--data-urlencode "hook.on_stream_not_found=$HOOK_BASE/index/hook/on_stream_not_found"
echo "Hooks fixed."
十一、录像片段查不到问题
现象
前端 /videos "历史回看"点击"查询录像"看不到任何录像片段。点击"开始录制"后录制服务立即退出,录制 0 段。
原因
Ceph RGW 只创建了 S3 用户(silk-app),但没有创建 S3 bucket。录制服务上传视频片段时 S3 返回 404 NoSuchBucket,录制线程立即退出。
录制服务日志:
CreateMultipartUpload 失败: operation error S3: CreateMultipartUpload, https response error StatusCode: 404, api error NoSuchBucket: UnknownError
录制线程退出(共完成 0 段)
修复方法
使用 Python S3 API 创建 bucket(radosgw-admin 没有 bucket create 子命令):
# 上传 create-buckets.py 到服务器后执行
python3 /home/pan/create-buckets.py
create-buckets.py 内容:
#!/usr/bin/env python3
import http.client, hashlib, hmac, datetime, base64
host = "localhost:7480"
access_key = "silk-app"
secret_key = "Silk-App-Secret-2026!"
buckets = ["silk-video-archive", "silk-video-events"]
for bucket in buckets:
conn = http.client.HTTPConnection(host)
date = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
string_to_sign = f"PUT\n\n\n{date}\n/{bucket}"
signature = base64.b64encode(
hmac.new(secret_key.encode(), string_to_sign.encode(), hashlib.sha1).digest()
).decode()
conn.request("PUT", f"/{bucket}", headers={
"Date": date,
"Authorization": f"AWS {access_key}:{signature}"
})
resp = conn.getresponse()
print(f"Bucket {bucket}: {resp.status} {resp.reason}")
conn.close()
创建后重启录制服务:
sudo fuser -k 9090/tcp
sleep 2
nohup /home/pan/recorder-go-linux > /home/pan/recorder.log 2>&1 < /dev/null &
验证
# 检查 bucket 列表
python3 /home/pan/create-buckets.py # 会列出所有 bucket
# 录制服务日志应显示正常录制
tail -5 /home/pan/recorder.log
# 不应有 NoSuchBucket 错误
注意:Ceph RGW 的
radosgw-admin不支持bucket create命令,bucket 必须通过 S3 API(PUT /bucket-name)创建。