Files
silk/部署指南(物理机).md
T

881 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署指南(物理机)
## 一、环境信息
| 项目 | 值 |
|------|-----|
| 机器 | 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.1WireGuard 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
└── 前端 :5174 server.cjs
```
## 三、全新部署步骤
### 1. 安装系统依赖
```bash
# 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 17IoTDB 依赖)
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 镜像
```bash
sudo docker pull mysql:8
sudo docker pull redis:latest
sudo docker pull zlmediakit/zlmediakit:master
```
### 3. 启动 Docker 容器
```bash
# 创建网络
sudo docker network create media-net
# MySQLWVP 数据库)
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
# RedisWVP 缓存,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
# RedisGo 后端缓存,端口 6379,密码 pan)
sudo docker run -d --name silk-redis --restart always \
-p 6379:6379 \
redis:latest redis-server --requirepass pan --appendonly yes
```
### 4. 配置 PostgreSQL
```bash
sudo service postgresql start
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'pan';"
sudo -u postgres psql -c "CREATE DATABASE silk;"
```
### 5. 安装 Ceph
参考[Ceph 安装文档](https://docs.ceph.net.cn/en/latest/install/get-packages/)
#### 添加APT密钥
要安装 release.asc 密钥,请执行以下操作
```bash
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}
```bash
sudo apt-add-repository 'deb https://download.ceph.com/debian-tentacle/ {codename} main'
```
#### 安装
```bash
# 安装 CephUbuntu 默认源,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
# 创建 OSDloop 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` 创建 OSD
> - `auth_*_required = none` 简化认证(单节点测试环境)
> - `osd pool default size = 1` 适配单节点(默认 3 副本会导致 PG 一直 inactive
### 6. 安装 VerneMQ
```bash
# 上传 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
```bash
# 上传 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.04GLIBC 2.38),Ubuntu 22.04 只有 GLIBC 2.35。用 Docker Redis 替代。
### 8. 构建 WVP Docker 镜像
```bash
# 克隆 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
```bash
# 创建 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 后端
```bash
# 在 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 录制服务
```bash
# 在 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. 部署前端
```bash
# 在 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`。
## 四、验证
```bash
# 检查所有端口
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`: 8116
- `sip.domain`: 3402000000
- `sip.id`: 34020000002000000001
- `media.ip`: polaris-mediaDocker 内部网络)
- `media.sdp-ip` / `media.stream-ip`: 100.83.103.1VPN 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.1WireGuard 固定) |
| WVP 端口 | 18978 | 18080 |
| 缓存 | Valkey(裸机安装) | Docker RedisValkey GLIBC 不兼容) |
| Ceph | 已配置(可直接启动) | 需从头配置(loop device OSD |
| WVP 镜像 | 已构建 | 需从 Gitee 克隆源码构建 |
## 七、重启后恢复
物理机重启后,systemd 管理的服务(Docker、PostgreSQL、VerneMQ)会自动启动。需要手动启动的服务:
```bash
# 1. Cephsystemd 管理,但 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/UDPDocker 映射 `-p 8116:8116/tcp -p 8116:8116/udp` |
| SIP 域 | `3402000000` | |
| SIP 服务器 ID | `34020000002000000001` | |
| SIP 密码 | `12345678` | |
> 摄像头需能访问 `100.83.103.1`WireGuard VPN)。若摄像头不在 VPN 网络,改用物理网卡 IP `192.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.04Noble),需要 GLIBC 2.38Ubuntu 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` 权限不足。
**解决**
```bash
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`
```bash
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`
```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"
```
> **注意**:每次 WVP 容器重启后都需重新执行此命令。可将此命令加入启动脚本。
#### 3. 设置固定的 mediaServerId
在 WVP `application.yml` 中设置 `media.id`
```yaml
media:
id: silk-media-001 # 不能为空
```
同时通过 ZLMediaKit API 设置:
```bash
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` 函数,增加主机名替换:
```go
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
}
```
重新交叉编译并部署:
```powershell
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-连接成功] IDsilk-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 重启后执行:
```bash
#!/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` 子命令):
```bash
# 上传 create-buckets.py 到服务器后执行
python3 /home/pan/create-buckets.py
```
`create-buckets.py` 内容:
```python
#!/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()
```
创建后重启录制服务:
```bash
sudo fuser -k 9090/tcp
sleep 2
nohup /home/pan/recorder-go-linux > /home/pan/recorder.log 2>&1 < /dev/null &
```
### 验证
```bash
# 检查 bucket 列表
python3 /home/pan/create-buckets.py # 会列出所有 bucket
# 录制服务日志应显示正常录制
tail -5 /home/pan/recorder.log
# 不应有 NoSuchBucket 错误
```
> **注意**Ceph RGW 的 `radosgw-admin` 不支持 `bucket create` 命令,bucket 必须通过 S3 APIPUT /bucket-name)创建。