909 lines
30 KiB
Bash
Executable File
909 lines
30 KiB
Bash
Executable File
#!/bin/bash
|
||
# RK3588 Media Server + Agent 完善部署脚本
|
||
# 部署到 /opt,包含完整的运行时目录、权限、日志轮转等
|
||
#
|
||
# 用法: sudo ./deploy.sh [install|upgrade|status|logs|clean-hls|uninstall]
|
||
# 首次安装 Agent 时传入后台管理统一主钥匙;后续部署默认沿用设备保存的主钥匙:
|
||
# sudo AGENT_TOKEN=<managerd.json 中的 agent_token> ./deploy.sh install
|
||
|
||
set -e
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
# 目录定义
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
BUILD_DIR="$PROJECT_DIR/build"
|
||
|
||
# 安装目录
|
||
INSTALL_DIR="/opt/rk3588-media-server"
|
||
AGENT_INSTALL_DIR="/opt/rk3588-agent"
|
||
SERVICE_DIR="/etc/systemd/system"
|
||
LOGROTATE_DIR="/etc/logrotate.d"
|
||
|
||
# 运行时数据目录(持久化数据)
|
||
RUNTIME_DIR="/var/lib/rk3588-media-server"
|
||
HLS_DIR="$RUNTIME_DIR/hls"
|
||
LOGS_DIR="$RUNTIME_DIR/logs"
|
||
ALARMS_DIR="$RUNTIME_DIR/alarms"
|
||
CLIPS_DIR="$RUNTIME_DIR/clips"
|
||
|
||
# 默认使用模板/profile/overlay 渲染部署配置。可通过环境变量覆盖:
|
||
# DEPLOY_CONFIG_TEMPLATE=configs/templates/std_workshop_face_recognition_shoe_alarm.json
|
||
# DEPLOY_CONFIG_PROFILE=configs/profiles/local_3588_test.json
|
||
# DEPLOY_CONFIG_OVERLAYS="configs/overlays/face_debug.json configs/overlays/production_quiet.json"
|
||
# DEPLOY_CONFIG_ID=local_3588_face_debug
|
||
# DEPLOY_CONFIG_VERSION=20260419.001
|
||
DEPLOY_CONFIG_TEMPLATE="${DEPLOY_CONFIG_TEMPLATE:-configs/templates/std_workshop_face_recognition_shoe_alarm.json}"
|
||
DEPLOY_CONFIG_PROFILE="${DEPLOY_CONFIG_PROFILE:-configs/profiles/local_3588_test.json}"
|
||
DEPLOY_CONFIG_OVERLAYS="${DEPLOY_CONFIG_OVERLAYS:-configs/overlays/face_debug.json}"
|
||
DEPLOY_CONFIG_ID="${DEPLOY_CONFIG_ID:-local_3588_face_debug}"
|
||
DEPLOY_CONFIG_VERSION="${DEPLOY_CONFIG_VERSION:-$(date +%Y%m%d.%H%M%S)}"
|
||
|
||
# 后台管理系统访问所有设备 agent 的统一主钥匙。传入 AGENT_TOKEN 表示写入/轮换主钥匙;
|
||
# 不传时沿用设备上保存的主钥匙。禁止设备本地随机生成,避免后台和设备 token 漂移。
|
||
# 首次安装示例:
|
||
# sudo AGENT_TOKEN=4fe2d69fda23d0d5d04a1486d4920e68 ./scripts/deploy.sh install
|
||
# 后续升级示例:
|
||
# sudo ./scripts/deploy.sh upgrade
|
||
AGENT_TOKEN="${AGENT_TOKEN:-}"
|
||
AGENT_TOKEN_FILE="$AGENT_INSTALL_DIR/agent.token"
|
||
|
||
resolve_agent_token() {
|
||
if [ -z "$AGENT_TOKEN" ]; then
|
||
if [ -f "$AGENT_TOKEN_FILE" ]; then
|
||
AGENT_TOKEN="$(tr -d '\r\n[:space:]' < "$AGENT_TOKEN_FILE")"
|
||
echo -e "${GREEN}✓${NC} 使用设备已保存的后台管理统一主钥匙"
|
||
else
|
||
echo -e "${RED}错误: 未提供 AGENT_TOKEN,且未找到已保存的主钥匙 $AGENT_TOKEN_FILE${NC}"
|
||
echo "首次安装必须使用:sudo AGENT_TOKEN=<managerd.json 中的 agent_token> ./scripts/deploy.sh install"
|
||
echo "部署脚本不会本地生成 token。"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo -e "${GREEN}✓${NC} 使用 AGENT_TOKEN 写入/轮换后台管理统一主钥匙"
|
||
fi
|
||
if ! printf '%s' "$AGENT_TOKEN" | grep -Eq '^[A-Za-z0-9._:-]+$'; then
|
||
echo -e "${RED}错误: AGENT_TOKEN 包含不支持的字符${NC}"
|
||
echo "请使用字母、数字、点、下划线、冒号或短横线组成的 token。"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 创建必要的运行时目录
|
||
create_runtime_dirs() {
|
||
echo -e "${CYAN}[创建运行时目录]${NC}"
|
||
|
||
# 主运行时目录
|
||
mkdir -p "$RUNTIME_DIR"
|
||
mkdir -p "$HLS_DIR"
|
||
mkdir -p "$LOGS_DIR"
|
||
mkdir -p "$ALARMS_DIR"
|
||
mkdir -p "$CLIPS_DIR"
|
||
|
||
# 创建 HLS 符号链接,使 HTTP server 能访问 HLS 文件
|
||
# HLS 输出到 /var/lib/...,但 HTTP server 从 web_root/hls/ 提供文件
|
||
if [ ! -L "$INSTALL_DIR/web/hls" ]; then
|
||
rm -rf "$INSTALL_DIR/web/hls"
|
||
ln -s "$HLS_DIR" "$INSTALL_DIR/web/hls"
|
||
echo -e "${GREEN}✓${NC} 创建 HLS 符号链接: web/hls -> $HLS_DIR"
|
||
fi
|
||
|
||
# 设置权限(日志目录使用 orangepi 用户,方便查看和管理)
|
||
chown -R root:root "$RUNTIME_DIR"
|
||
|
||
# HLS 输出目录 - root 所有,但 orangepi 可读取
|
||
chmod 755 "$HLS_DIR"
|
||
|
||
# 日志目录 - orangepi 用户完全控制
|
||
chown orangepi:orangepi "$LOGS_DIR"
|
||
chmod 755 "$LOGS_DIR"
|
||
|
||
# 告警目录 - orangepi 用户完全控制
|
||
chown orangepi:orangepi "$ALARMS_DIR"
|
||
chmod 755 "$ALARMS_DIR"
|
||
|
||
# 视频片段目录 - orangepi 用户完全控制
|
||
chown orangepi:orangepi "$CLIPS_DIR"
|
||
chmod 755 "$CLIPS_DIR"
|
||
|
||
echo -e "${GREEN}✓${NC} 运行时目录创建完成:"
|
||
echo " HLS输出: $HLS_DIR (root:orangepi, 755)"
|
||
echo " 日志: $LOGS_DIR (orangepi:orangepi, 755)"
|
||
echo " 告警图片: $ALARMS_DIR (orangepi:orangepi, 755)"
|
||
echo " 告警视频: $CLIPS_DIR (orangepi:orangepi, 755)"
|
||
}
|
||
|
||
# 安装日志轮转配置
|
||
install_logrotate() {
|
||
echo -e "${CYAN}[配置日志轮转]${NC}"
|
||
|
||
# Media Server 日志轮转
|
||
cat > "$LOGROTATE_DIR/rk3588-media-server" << 'EOF'
|
||
/var/lib/rk3588-media-server/logs/*.log {
|
||
daily
|
||
rotate 7
|
||
compress
|
||
delaycompress
|
||
missingok
|
||
notifempty
|
||
create 644 orangepi orangepi
|
||
sharedscripts
|
||
postrotate
|
||
/bin/kill -HUP $(cat /var/run/syslogd.pid 2>/dev/null) 2>/dev/null || true
|
||
endscript
|
||
}
|
||
EOF
|
||
|
||
# HLS 分片清理(保留最近7天)
|
||
cat > "$LOGROTATE_DIR/rk3588-hls" << 'EOF'
|
||
/var/lib/rk3588-media-server/hls/*/index*.ts {
|
||
daily
|
||
rotate 1
|
||
maxage 7
|
||
missingok
|
||
notifempty
|
||
nocompress
|
||
}
|
||
|
||
/var/lib/rk3588-media-server/hls/*/index*.m3u8 {
|
||
daily
|
||
rotate 1
|
||
maxage 7
|
||
missingok
|
||
notifempty
|
||
nocompress
|
||
}
|
||
EOF
|
||
|
||
chmod 644 "$LOGROTATE_DIR/rk3588-media-server"
|
||
chmod 644 "$LOGROTATE_DIR/rk3588-hls"
|
||
|
||
echo -e "${GREEN}✓${NC} 日志轮转配置完成"
|
||
}
|
||
|
||
# 安装 HLS 清理脚本
|
||
install_hls_cleanup() {
|
||
echo -e "${CYAN}[安装 HLS 清理脚本]${NC}"
|
||
|
||
cat > "$INSTALL_DIR/bin/cleanup-hls.sh" << 'EOF'
|
||
#!/bin/bash
|
||
# HLS 分片清理脚本 - 保留最近 N 天的分片
|
||
|
||
KEEP_DAYS=${1:-3}
|
||
HLS_DIR="/var/lib/rk3588-media-server/hls"
|
||
|
||
if [ ! -d "$HLS_DIR" ]; then
|
||
echo "HLS 目录不存在: $HLS_DIR"
|
||
exit 1
|
||
fi
|
||
|
||
# 清理旧的分片文件
|
||
find "$HLS_DIR" -name "*.ts" -type f -mtime +$KEEP_DAYS -delete 2>/dev/null
|
||
|
||
# 清理空的摄像头目录
|
||
find "$HLS_DIR" -type d -empty -delete 2>/dev/null
|
||
|
||
echo "$(date): HLS 清理完成 (保留最近 $KEEP_DAYS 天)"
|
||
EOF
|
||
|
||
chmod +x "$INSTALL_DIR/bin/cleanup-hls.sh"
|
||
|
||
# 添加到 crontab(每天凌晨3点执行)
|
||
CRON_FILE="/etc/cron.d/rk3588-media-server"
|
||
cat > "$CRON_FILE" << 'EOF'
|
||
# RK3588 Media Server 定时任务
|
||
SHELL=/bin/bash
|
||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||
|
||
# 每天凌晨3点清理HLS旧分片(保留3天)
|
||
0 3 * * * root /opt/rk3588-media-server/bin/cleanup-hls.sh 3 >> /var/lib/rk3588-media-server/logs/cleanup.log 2>&1
|
||
|
||
# 每周日凌晨4点清理告警视频(保留30天)
|
||
0 4 * * 0 root find /var/lib/rk3588-media-server/clips -name "*.mp4" -type f -mtime +30 -delete 2>/dev/null
|
||
|
||
# 每周日凌晨4点清理告警图片(保留90天)
|
||
30 4 * * 0 root find /var/lib/rk3588-media-server/alarms -name "*.jpg" -type f -mtime +90 -delete 2>/dev/null
|
||
EOF
|
||
|
||
chmod 644 "$CRON_FILE"
|
||
|
||
echo -e "${GREEN}✓${NC} HLS 清理脚本安装完成"
|
||
echo " 清理策略:"
|
||
echo " - HLS分片: 保留3天"
|
||
echo " - 告警视频: 保留30天"
|
||
echo " - 告警图片: 保留90天"
|
||
}
|
||
|
||
# 安装 Media Server
|
||
cmd_install_media_server() {
|
||
echo -e "${BLUE}[1/5] 安装 Media Server...${NC}"
|
||
|
||
# 检查编译产物
|
||
if [ ! -f "$BUILD_DIR/media-server" ]; then
|
||
echo -e "${RED}错误: 未找到编译好的 media-server${NC}"
|
||
echo "请先编译: cd $PROJECT_DIR && cmake --build build"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建目录结构
|
||
mkdir -p "$INSTALL_DIR/bin/plugins"
|
||
mkdir -p "$INSTALL_DIR/lib"
|
||
mkdir -p "$INSTALL_DIR/etc"
|
||
mkdir -p "$INSTALL_DIR/web"
|
||
mkdir -p "$INSTALL_DIR/models"
|
||
|
||
# 复制二进制文件
|
||
cp "$BUILD_DIR/media-server" "$INSTALL_DIR/bin/"
|
||
chmod +x "$INSTALL_DIR/bin/media-server"
|
||
|
||
# 复制运维脚本
|
||
mkdir -p "$INSTALL_DIR/scripts"
|
||
if [ -f "$PROJECT_DIR/scripts/ops.sh" ]; then
|
||
cp "$PROJECT_DIR/scripts/ops.sh" "$INSTALL_DIR/scripts/"
|
||
chmod +x "$INSTALL_DIR/scripts/ops.sh"
|
||
echo -e "${GREEN}✓${NC} 已复制运维脚本: ops.sh"
|
||
fi
|
||
if [ -f "$PROJECT_DIR/scripts/monitor_hw.sh" ]; then
|
||
cp "$PROJECT_DIR/scripts/monitor_hw.sh" "$INSTALL_DIR/scripts/"
|
||
chmod +x "$INSTALL_DIR/scripts/monitor_hw.sh"
|
||
echo -e "${GREEN}✓${NC} 已复制运维脚本: monitor_hw.sh"
|
||
fi
|
||
|
||
# 复制插件
|
||
if [ -d "$BUILD_DIR/plugins" ]; then
|
||
cp "$BUILD_DIR"/plugins/*.so "$INSTALL_DIR/bin/plugins/" 2>/dev/null || true
|
||
PLUGIN_COUNT=$(ls "$INSTALL_DIR/bin/plugins/"/*.so 2>/dev/null | wc -l)
|
||
echo -e "${GREEN}✓${NC} 已复制插件 ($PLUGIN_COUNT 个)"
|
||
fi
|
||
|
||
# 复制依赖库
|
||
if ls "$BUILD_DIR"/*.so 1> /dev/null 2>&1; then
|
||
cp "$BUILD_DIR"/*.so "$INSTALL_DIR/lib/" 2>/dev/null || true
|
||
fi
|
||
|
||
# 复制项目 lib 目录中的 RKNN 等系统库
|
||
if [ -d "$PROJECT_DIR/lib" ]; then
|
||
for lib_file in "$PROJECT_DIR"/lib/*.so*; do
|
||
[ -f "$lib_file" ] || continue
|
||
cp "$lib_file" "$INSTALL_DIR/lib/"
|
||
echo -e "${GREEN}✓${NC} 已复制 $(basename "$lib_file")"
|
||
done
|
||
fi
|
||
|
||
# 复制共享状态库(rk_shared_state)
|
||
if [ -f "$BUILD_DIR/librk_shared_state.so" ]; then
|
||
cp "$BUILD_DIR/librk_shared_state.so" "$INSTALL_DIR/lib/"
|
||
echo -e "${GREEN}✓${NC} 已复制 librk_shared_state.so"
|
||
fi
|
||
|
||
# 复制 web 静态文件
|
||
if [ -d "$PROJECT_DIR/web" ]; then
|
||
cp -r "$PROJECT_DIR/web"/* "$INSTALL_DIR/web/" 2>/dev/null || true
|
||
WEB_FILES=$(find "$INSTALL_DIR/web" -type f | wc -l)
|
||
echo -e "${GREEN}✓${NC} 已复制 web 静态文件 ($WEB_FILES 个文件)"
|
||
fi
|
||
|
||
# 复制模型文件(仅 .rknn)
|
||
if [ -d "$PROJECT_DIR/models" ]; then
|
||
mkdir -p "$INSTALL_DIR/models"
|
||
if ls "$PROJECT_DIR"/models/*.rknn 1>/dev/null 2>&1; then
|
||
cp "$PROJECT_DIR"/models/*.rknn "$INSTALL_DIR/models/" 2>/dev/null || true
|
||
fi
|
||
MODEL_COUNT=$(find "$INSTALL_DIR/models" -type f -name "*.rknn" | wc -l)
|
||
echo -e "${GREEN}✓${NC} 已复制模型文件 ($MODEL_COUNT 个)"
|
||
fi
|
||
|
||
# 复制资源文件(.db 人脸库等)
|
||
if [ -d "$PROJECT_DIR/models" ]; then
|
||
mkdir -p "$INSTALL_DIR/resources/face_gallery"
|
||
if ls "$PROJECT_DIR"/models/*.db 1>/dev/null 2>&1; then
|
||
cp "$PROJECT_DIR"/models/*.db "$INSTALL_DIR/resources/face_gallery/" 2>/dev/null || true
|
||
fi
|
||
RES_COUNT=$(find "$INSTALL_DIR/resources" -type f -name "*.db" | wc -l)
|
||
echo -e "${GREEN}✓${NC} 已复制资源文件 ($RES_COUNT 个)"
|
||
fi
|
||
|
||
# 选择并复制配置文件
|
||
select_config
|
||
|
||
# 修复配置文件路径
|
||
fix_config_paths
|
||
|
||
echo -e "${GREEN}✓${NC} Media Server 安装完成"
|
||
}
|
||
|
||
# 渲染模板化部署配置
|
||
render_template_config() {
|
||
local template_path="$PROJECT_DIR/$DEPLOY_CONFIG_TEMPLATE"
|
||
local profile_path="$PROJECT_DIR/$DEPLOY_CONFIG_PROFILE"
|
||
local render_tool="$PROJECT_DIR/tools/render_config.py"
|
||
local rendered_at
|
||
local out_file
|
||
local overlay_args=()
|
||
local overlay_path
|
||
|
||
if [ ! -f "$render_tool" ] || [ ! -f "$template_path" ] || [ ! -f "$profile_path" ]; then
|
||
return 1
|
||
fi
|
||
|
||
rendered_at="$(date -Iseconds)"
|
||
out_file="/tmp/rk3588-media-server-${DEPLOY_CONFIG_ID}-${DEPLOY_CONFIG_VERSION}.json"
|
||
|
||
for overlay in $DEPLOY_CONFIG_OVERLAYS; do
|
||
overlay_path="$PROJECT_DIR/$overlay"
|
||
if [ ! -f "$overlay_path" ]; then
|
||
echo -e "${YELLOW}警告: overlay 不存在,跳过: $overlay${NC}"
|
||
continue
|
||
fi
|
||
overlay_args+=(--overlay "$overlay_path")
|
||
done
|
||
|
||
echo -e "${CYAN}[渲染模板配置]${NC}"
|
||
echo " template: $DEPLOY_CONFIG_TEMPLATE"
|
||
echo " profile: $DEPLOY_CONFIG_PROFILE"
|
||
echo " overlays: ${DEPLOY_CONFIG_OVERLAYS:-<none>}"
|
||
echo " config_id: $DEPLOY_CONFIG_ID"
|
||
echo " config_version: $DEPLOY_CONFIG_VERSION"
|
||
|
||
python3 "$render_tool" \
|
||
--template "$template_path" \
|
||
--profile "$profile_path" \
|
||
"${overlay_args[@]}" \
|
||
--config-id "$DEPLOY_CONFIG_ID" \
|
||
--config-version "$DEPLOY_CONFIG_VERSION" \
|
||
--rendered-at "$rendered_at" \
|
||
--out "$out_file"
|
||
|
||
cp "$out_file" "$INSTALL_DIR/etc/media-server.json"
|
||
echo -e "${GREEN}✓${NC} 已渲染部署配置: $out_file"
|
||
return 0
|
||
}
|
||
|
||
# 选择配置文件
|
||
select_config() {
|
||
echo -e "${CYAN}[选择配置文件]${NC}"
|
||
|
||
if render_template_config; then
|
||
return
|
||
fi
|
||
|
||
echo -e "${YELLOW}模板渲染不可用,回退到旧配置文件选择流程${NC}"
|
||
|
||
# 查找所有可用的配置文件
|
||
CONFIGS=()
|
||
for f in "$PROJECT_DIR/configs"/*.json; do
|
||
if [ -f "$f" ]; then
|
||
CONFIGS+=("$(basename "$f")")
|
||
fi
|
||
done
|
||
|
||
echo "可用的配置文件:"
|
||
for i in "${!CONFIGS[@]}"; do
|
||
printf " %2d) %s\n" $((i+1)) "${CONFIGS[$i]}"
|
||
done
|
||
echo ""
|
||
|
||
printf "请选择 [1-${#CONFIGS[@]},默认1]: "
|
||
read choice
|
||
choice=${choice:-1}
|
||
|
||
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#CONFIGS[@]}" ]; then
|
||
choice=1
|
||
fi
|
||
|
||
SELECTED_CONFIG="$PROJECT_DIR/configs/${CONFIGS[$((choice-1))]}"
|
||
cp "$SELECTED_CONFIG" "$INSTALL_DIR/etc/media-server.json"
|
||
|
||
echo -e "${GREEN}✓${NC} 使用配置: ${CONFIGS[$((choice-1))]}"
|
||
}
|
||
|
||
# 修复配置文件中的路径
|
||
fix_config_paths() {
|
||
echo -e "${CYAN}[修复配置路径]${NC}"
|
||
|
||
python3 << 'EOF'
|
||
import json
|
||
import os
|
||
import re
|
||
|
||
config_file = "/opt/rk3588-media-server/etc/media-server.json"
|
||
install_dir = "/opt/rk3588-media-server"
|
||
runtime_dir = "/var/lib/rk3588-media-server"
|
||
project_dir = os.path.expanduser("~/apps/OrangePi3588Media")
|
||
|
||
def fix_path(path):
|
||
"""修复配置文件中的路径"""
|
||
if not path or not isinstance(path, str):
|
||
return path
|
||
|
||
# 人脸库/资源文件 -> 安装目录的 resources
|
||
if '.db' in path:
|
||
return os.path.join(install_dir, 'resources', 'face_gallery', os.path.basename(path))
|
||
|
||
# 模型路径 -> 安装目录的 models
|
||
if 'third_party/rknpu2' in path or '.rknn' in path:
|
||
return os.path.join(install_dir, 'models', os.path.basename(path))
|
||
|
||
# HLS 输出路径 -> 运行时目录的 hls
|
||
if 'web/hls' in path or path.startswith('./web/hls'):
|
||
return os.path.join(runtime_dir, 'hls', os.path.basename(os.path.dirname(path)), 'index.m3u8')
|
||
|
||
# web 根目录 -> 安装目录的 web
|
||
if path.startswith('./web/'):
|
||
return os.path.join(install_dir, path[2:])
|
||
|
||
# models 目录 -> 安装目录的 models
|
||
if path.startswith('./models/'):
|
||
return os.path.join(install_dir, path[2:])
|
||
|
||
# 其他相对路径 -> 尝试解析
|
||
if path.startswith('./'):
|
||
abs_path = os.path.join(project_dir, path)
|
||
if os.path.exists(abs_path):
|
||
return abs_path
|
||
return os.path.join(install_dir, path[2:])
|
||
|
||
return path
|
||
|
||
def process_node(node):
|
||
"""处理节点配置"""
|
||
# 修复模型路径
|
||
if 'model_path' in node:
|
||
node['model_path'] = fix_path(node['model_path'])
|
||
|
||
# 修复图库路径
|
||
if 'gallery' in node and isinstance(node['gallery'], dict) and 'path' in node['gallery']:
|
||
node['gallery']['path'] = fix_path(node['gallery']['path'])
|
||
|
||
# 修复输出路径(HLS、报警图片等)
|
||
if 'outputs' in node and isinstance(node['outputs'], list):
|
||
for output in node['outputs']:
|
||
if 'path' in output:
|
||
output['path'] = fix_path(output['path'])
|
||
|
||
# 修复 MinIO 端点(如果是本地)
|
||
if 'minio_endpoint' in node:
|
||
endpoint = node['minio_endpoint']
|
||
if '127.0.0.1' in endpoint or 'localhost' in endpoint:
|
||
# 保持本地端点不变
|
||
pass
|
||
|
||
return node
|
||
|
||
try:
|
||
with open(config_file, 'r', encoding='utf-8') as f:
|
||
config = json.load(f)
|
||
|
||
# 处理 graphs 格式
|
||
if 'graphs' in config:
|
||
for graph in config['graphs']:
|
||
if 'nodes' in graph:
|
||
for node in graph['nodes']:
|
||
process_node(node)
|
||
|
||
# 处理 templates + instances 格式
|
||
if 'templates' in config and 'instances' in config:
|
||
for template_name, template in config['templates'].items():
|
||
if 'nodes' in template:
|
||
for node in template['nodes']:
|
||
process_node(node)
|
||
|
||
# 添加全局配置(如果不存在)
|
||
if 'global' not in config:
|
||
config['global'] = {}
|
||
|
||
# 确保 web_root 指向正确位置
|
||
config['global']['web_root'] = os.path.join(install_dir, 'web')
|
||
config['global']['metrics_port'] = config['global'].get('metrics_port', 9000)
|
||
|
||
with open(config_file, 'w', encoding='utf-8') as f:
|
||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"配置路径修复完成")
|
||
|
||
except Exception as e:
|
||
print(f"配置处理出错: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
EOF
|
||
}
|
||
|
||
# 安装 Agent
|
||
cmd_install_agent() {
|
||
echo -e "${BLUE}[2/5] 安装 Agent...${NC}"
|
||
resolve_agent_token
|
||
|
||
# 查找 Agent 编译产物(优先使用预编译的 arm64 二进制)
|
||
AGENT_SOURCE=""
|
||
if [ -f "$PROJECT_DIR/agent/rk3588-agent_linux_arm64" ]; then
|
||
AGENT_SOURCE="$PROJECT_DIR/agent/rk3588-agent_linux_arm64"
|
||
echo -e "${GREEN}✓${NC} 找到预编译 Agent: rk3588-agent_linux_arm64"
|
||
elif [ -f "$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent" ]; then
|
||
AGENT_SOURCE="$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent"
|
||
echo -e "${GREEN}✓${NC} 找到源码编译 Agent: rk3588-agent"
|
||
fi
|
||
|
||
if [ -z "$AGENT_SOURCE" ]; then
|
||
echo -e "${YELLOW}警告: 未找到编译好的 Agent,跳过安装${NC}"
|
||
return
|
||
fi
|
||
|
||
mkdir -p "$AGENT_INSTALL_DIR"
|
||
mkdir -p "/var/lib/rk3588-agent"
|
||
|
||
cp "$AGENT_SOURCE" "$AGENT_INSTALL_DIR/rk3588-agent"
|
||
chmod +x "$AGENT_INSTALL_DIR/rk3588-agent"
|
||
printf '%s\n' "$AGENT_TOKEN" > "$AGENT_TOKEN_FILE"
|
||
chmod 600 "$AGENT_TOKEN_FILE"
|
||
|
||
# Agent 配置
|
||
cat > "$AGENT_INSTALL_DIR/agent.config.json" << EOF
|
||
{
|
||
"agent": {
|
||
"listen": "0.0.0.0:9100",
|
||
"token": "$AGENT_TOKEN",
|
||
"require_token_for_read": false,
|
||
"discovery_enable": true,
|
||
"discovery_port": 35688,
|
||
"device_name": "rk3588_$(hostname)",
|
||
"device_id_path": "/var/lib/rk3588-agent/device_id",
|
||
"models_dir": "$INSTALL_DIR/models",
|
||
"resources_dir": "$INSTALL_DIR/resources",
|
||
"max_upload_mb": 200,
|
||
"config_path": "$INSTALL_DIR/etc/media-server.json",
|
||
"media_server_process": {
|
||
"enable": false,
|
||
"configs_dir": "$INSTALL_DIR/etc"
|
||
},
|
||
"media_server_base_url": "http://127.0.0.1:9000",
|
||
"media_server_timeout_ms": 3000,
|
||
"media_server_retry": { "max_attempts": 3, "backoff_ms": [200, 500] }
|
||
}
|
||
}
|
||
EOF
|
||
|
||
chown -R orangepi:orangepi "/var/lib/rk3588-agent"
|
||
|
||
echo -e "${GREEN}✓${NC} Agent 安装完成,已写入后台管理统一主钥匙"
|
||
}
|
||
|
||
# 安装 systemd 服务
|
||
cmd_install_services() {
|
||
echo -e "${BLUE}[3/5] 安装 Systemd 服务...${NC}"
|
||
|
||
# Media Server 服务
|
||
cat > "$SERVICE_DIR/media-server.service" << EOF
|
||
[Unit]
|
||
Description=RK3588 Media Server
|
||
After=network.target
|
||
Wants=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=root
|
||
Group=root
|
||
WorkingDirectory=$INSTALL_DIR
|
||
Environment="LD_LIBRARY_PATH=$INSTALL_DIR/lib:/usr/local/lib"
|
||
Environment="RKNPU_LOG_LEVEL=2"
|
||
ExecStartPre=/bin/bash -c 'mkdir -p $HLS_DIR $LOGS_DIR $ALARMS_DIR $CLIPS_DIR && chown orangepi:orangepi $LOGS_DIR $ALARMS_DIR $CLIPS_DIR && chmod 755 $LOGS_DIR $ALARMS_DIR $CLIPS_DIR'
|
||
ExecStart=$INSTALL_DIR/bin/media-server --config $INSTALL_DIR/etc/media-server.json
|
||
Restart=always
|
||
RestartSec=5
|
||
StartLimitInterval=60s
|
||
StartLimitBurst=3
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=media-server
|
||
|
||
# 资源限制
|
||
LimitNOFILE=65536
|
||
LimitNPROC=4096
|
||
|
||
# 优雅关闭
|
||
TimeoutStopSec=30
|
||
KillSignal=SIGTERM
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
# Agent 服务
|
||
cat > "$SERVICE_DIR/rk3588-agent.service" << EOF
|
||
[Unit]
|
||
Description=RK3588 Agent Service
|
||
After=network.target media-server.service
|
||
Wants=media-server.service
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=root
|
||
Group=root
|
||
WorkingDirectory=$AGENT_INSTALL_DIR
|
||
ExecStart=$AGENT_INSTALL_DIR/rk3588-agent --config $AGENT_INSTALL_DIR/agent.config.json
|
||
Restart=always
|
||
RestartSec=5
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=rk3588-agent
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
|
||
echo -e "${GREEN}✓${NC} Systemd 服务安装完成"
|
||
}
|
||
|
||
# 主安装流程
|
||
cmd_install() {
|
||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${BLUE}║ RK3588 Media Server 完善部署脚本 ║${NC}"
|
||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
# 检查 root 权限
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
echo "项目目录: $PROJECT_DIR"
|
||
echo "安装目录: $INSTALL_DIR"
|
||
echo "运行时目录: $RUNTIME_DIR"
|
||
echo ""
|
||
|
||
# 执行安装步骤
|
||
cmd_install_media_server
|
||
cmd_install_agent
|
||
create_runtime_dirs
|
||
cmd_install_services
|
||
install_logrotate
|
||
install_hls_cleanup
|
||
|
||
# 启动服务
|
||
echo -e "${BLUE}[启动服务]${NC}"
|
||
systemctl enable media-server
|
||
systemctl start media-server
|
||
systemctl enable rk3588-agent
|
||
systemctl start rk3588-agent
|
||
|
||
echo ""
|
||
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${GREEN}║ 部署完成! ║${NC}"
|
||
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
echo "目录结构:"
|
||
echo " 程序: $INSTALL_DIR/"
|
||
echo " 数据: $RUNTIME_DIR/"
|
||
echo " 配置: $INSTALL_DIR/etc/media-server.json"
|
||
echo ""
|
||
echo "服务管理:"
|
||
echo " 状态: sudo systemctl status media-server"
|
||
echo " 日志: sudo journalctl -u media-server -f"
|
||
echo " 界面: http://$(hostname -I | awk '{print $1}'):9100"
|
||
echo ""
|
||
echo "HLS播放:"
|
||
echo " http://$(hostname -I | awk '{print $1}'):9000/hls_player.html"
|
||
}
|
||
|
||
# 升级
|
||
cmd_upgrade() {
|
||
echo -e "${YELLOW}========== 升级 Media Server ==========${NC}"
|
||
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
echo -e "${RED}错误: 请使用 sudo 运行${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
# 备份配置
|
||
BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)"
|
||
mkdir -p "$BACKUP_DIR"
|
||
[ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/"
|
||
echo "配置已备份到: $BACKUP_DIR"
|
||
|
||
# 停止服务
|
||
echo "停止服务..."
|
||
systemctl stop media-server rk3588-agent
|
||
|
||
# 重新安装
|
||
cmd_install
|
||
|
||
echo -e "${GREEN}升级完成${NC}"
|
||
}
|
||
|
||
# 查看状态
|
||
cmd_status() {
|
||
echo -e "${BLUE}========== RK3588 Media Server 状态 ==========${NC}"
|
||
echo ""
|
||
|
||
# 系统信息
|
||
echo -e "${CYAN}[系统信息]${NC}"
|
||
echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo " 运行时间: $(uptime -p 2>/dev/null || uptime | awk -F',' '{print $1}')"
|
||
echo ""
|
||
|
||
# NPU
|
||
echo -e "${CYAN}[NPU 负载]${NC}"
|
||
if [ -f /proc/rknpu/load ]; then
|
||
cat /proc/rknpu/load | sed 's/^/ /'
|
||
else
|
||
echo " NPU 信息不可用"
|
||
fi
|
||
echo ""
|
||
|
||
# Media Server
|
||
echo -e "${CYAN}[Media Server]${NC}"
|
||
if systemctl is-active --quiet media-server 2>/dev/null; then
|
||
echo -e " 状态: ${GREEN}● 运行中${NC}"
|
||
PID=$(systemctl show --property=MainPID --value media-server 2>/dev/null)
|
||
echo " PID: $PID"
|
||
if [ -n "$PID" ] && [ "$PID" != "0" ]; then
|
||
CPU_MEM=$(ps -p $PID -o %cpu,%mem --no-headers 2>/dev/null || echo "N/A")
|
||
echo " CPU/MEM: $CPU_MEM"
|
||
fi
|
||
echo " 端口:"
|
||
ss -tlnp 2>/dev/null | grep -E "(9000|8555)" | head -3 | sed 's/^/ /'
|
||
else
|
||
echo -e " 状态: ${RED}○ 停止${NC}"
|
||
fi
|
||
echo ""
|
||
|
||
# HLS 输出
|
||
echo -e "${CYAN}[HLS 输出]${NC}"
|
||
if [ -d "$HLS_DIR" ]; then
|
||
CHANNELS=$(find "$HLS_DIR" -maxdepth 1 -type d | wc -l)
|
||
CHANNELS=$((CHANNELS - 1))
|
||
echo " 通道数: $CHANNELS"
|
||
if [ $CHANNELS -gt 0 ]; then
|
||
echo " 分片统计:"
|
||
for d in "$HLS_DIR"/*/; do
|
||
[ -d "$d" ] || continue
|
||
name=$(basename "$d")
|
||
count=$(find "$d" -name "*.ts" | wc -l)
|
||
size=$(du -sh "$d" 2>/dev/null | cut -f1)
|
||
echo " $name: ${count}个分片, ${size}"
|
||
done
|
||
fi
|
||
else
|
||
echo " HLS 目录不存在"
|
||
fi
|
||
echo ""
|
||
|
||
# Agent
|
||
echo -e "${CYAN}[RK3588 Agent]${NC}"
|
||
if systemctl is-active --quiet rk3588-agent 2>/dev/null; then
|
||
echo -e " 状态: ${GREEN}● 运行中${NC}"
|
||
echo " 管理界面: http://$(hostname -I | awk '{print $1}'):9100"
|
||
else
|
||
echo -e " 状态: ${RED}○ 停止${NC}"
|
||
fi
|
||
echo ""
|
||
|
||
# 存储使用
|
||
echo -e "${CYAN}[存储使用]${NC}"
|
||
echo " HLS 输出: $(du -sh "$HLS_DIR" 2>/dev/null | cut -f1)"
|
||
echo " 日志: $(du -sh "$LOGS_DIR" 2>/dev/null | cut -f1)"
|
||
echo " 告警图片: $(du -sh "$ALARMS_DIR" 2>/dev/null | cut -f1)"
|
||
echo " 告警视频: $(du -sh "$CLIPS_DIR" 2>/dev/null | cut -f1)"
|
||
echo ""
|
||
|
||
echo "常用命令:"
|
||
echo " 查看日志: sudo journalctl -u media-server -f"
|
||
echo " HLS清理: sudo $INSTALL_DIR/bin/cleanup-hls.sh"
|
||
echo " 实时监控: watch -n 1 cat /proc/rknpu/load"
|
||
}
|
||
|
||
# 查看日志
|
||
cmd_logs() {
|
||
SERVICE="${2:-media-server}"
|
||
echo -e "${BLUE}查看 $SERVICE 日志 (按 Ctrl+C 退出)...${NC}"
|
||
journalctl -u "$SERVICE" -f
|
||
}
|
||
|
||
# 清理 HLS
|
||
cmd_clean_hls() {
|
||
echo -e "${YELLOW}清理 HLS 旧分片...${NC}"
|
||
KEEP_DAYS="${2:-3}"
|
||
|
||
if [ -f "$INSTALL_DIR/bin/cleanup-hls.sh" ]; then
|
||
"$INSTALL_DIR/bin/cleanup-hls.sh" "$KEEP_DAYS"
|
||
else
|
||
echo "清理脚本不存在"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 卸载
|
||
cmd_uninstall() {
|
||
echo -e "${YELLOW}========== 卸载 RK3588 Media Server ==========${NC}"
|
||
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
echo -e "${RED}错误: 请使用 sudo 运行${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
read -p "确定要卸载? (y/N): " -r
|
||
[ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ] && echo "已取消" && exit 0
|
||
|
||
echo "[1/4] 停止服务..."
|
||
systemctl stop media-server rk3588-agent 2>/dev/null || true
|
||
systemctl disable media-server rk3588-agent 2>/dev/null || true
|
||
|
||
echo "[2/4] 删除服务文件..."
|
||
rm -f "$SERVICE_DIR/media-server.service"
|
||
rm -f "$SERVICE_DIR/rk3588-agent.service"
|
||
rm -f "$LOGROTATE_DIR/rk3588-media-server"
|
||
rm -f "$LOGROTATE_DIR/rk3588-hls"
|
||
rm -f "/etc/cron.d/rk3588-media-server"
|
||
systemctl daemon-reload
|
||
|
||
echo "[3/4] 备份数据..."
|
||
BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)"
|
||
mkdir -p "$BACKUP_DIR"
|
||
[ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/"
|
||
[ -d "$HLS_DIR" ] && cp -r "$HLS_DIR" "$BACKUP_DIR/" 2>/dev/null || true
|
||
echo "数据已备份到: $BACKUP_DIR"
|
||
|
||
echo "[4/4] 删除文件..."
|
||
read -p "是否删除所有数据 (包括 HLS/日志/告警)? (y/N): " -r
|
||
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
|
||
rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR" "$RUNTIME_DIR"
|
||
echo "所有数据已删除"
|
||
else
|
||
rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR"
|
||
echo "程序已删除,数据保留在: $RUNTIME_DIR"
|
||
fi
|
||
|
||
echo -e "${GREEN}卸载完成${NC}"
|
||
}
|
||
|
||
# 主入口
|
||
case "${1:-install}" in
|
||
install)
|
||
cmd_install
|
||
;;
|
||
upgrade)
|
||
cmd_upgrade
|
||
;;
|
||
status)
|
||
cmd_status
|
||
;;
|
||
logs)
|
||
cmd_logs "$@"
|
||
;;
|
||
clean-hls)
|
||
cmd_clean_hls "$@"
|
||
;;
|
||
uninstall)
|
||
cmd_uninstall
|
||
;;
|
||
*)
|
||
echo "RK3588 Media Server 部署脚本"
|
||
echo ""
|
||
echo "用法: sudo ./deploy.sh [命令]"
|
||
echo ""
|
||
echo "命令:"
|
||
echo " install 安装/部署服务 (默认)"
|
||
echo " upgrade 升级服务 (保留配置和数据)"
|
||
echo " status 查看运行状态"
|
||
echo " logs 查看日志"
|
||
echo " clean-hls 清理 HLS 旧分片"
|
||
echo " uninstall 卸载服务"
|
||
echo ""
|
||
echo "示例:"
|
||
echo " sudo AGENT_TOKEN=<managerd.json 中的 agent_token> ./deploy.sh install"
|
||
echo " sudo ./deploy.sh upgrade # 沿用设备保存的后台管理统一主钥匙"
|
||
echo " sudo ./deploy.sh status"
|
||
echo " sudo ./deploy.sh clean-hls 7 # 保留最近7天"
|
||
exit 1
|
||
;;
|
||
esac
|