chore: 初始化仓库基线(AGENTS.md、git 规范、敏感文件排除)
This commit is contained in:
+880
@@ -0,0 +1,880 @@
|
||||
# 部署指南(物理机)
|
||||
|
||||
## 一、环境信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| 机器 | 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
|
||||
└── 前端 :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 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 镜像
|
||||
|
||||
```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
|
||||
|
||||
# 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
|
||||
|
||||
```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
|
||||
# 安装 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` 创建 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.04(GLIBC 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-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)会自动启动。需要手动启动的服务:
|
||||
|
||||
```bash
|
||||
# 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 网络,改用物理网卡 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.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` 权限不足。
|
||||
**解决**:
|
||||
```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-连接成功] 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 重启后执行:
|
||||
|
||||
```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 API(PUT /bucket-name)创建。
|
||||
Reference in New Issue
Block a user