仓库结构: edge/ 设备端(原根目录设备端代码整体移入) control/ 管理端(清理后) docs/ 文档(PRD 移入 design/) README.md 根导航(新增) 清理: - control/.brainstorm 临时草稿删除 - control 根级重复文档(API表/PRD_04)并入 docs/design/ - control/plan.md -> docs/implementation/control-plan.md - control/safesightd-linux-arm64 二进制取消版本控制(.gitignore) - edge/transform 模型转换产物归入 models/,onnx/pt 大源文件取消跟踪(.gitignore) - Readme.md(PRD) -> docs/design/PRD_Product_v1.2.md(避开 README 大小写冲突) 更新: - 根 README.md 导航、docs/README.md 文档索引 - deployment.md/检查表路径加 edge/ 前缀 - .gitignore 重写(edge/control 分区规则)
244 lines
10 KiB
Bash
244 lines
10 KiB
Bash
#!/bin/bash
|
||
# =============================================================================
|
||
# init-device.sh — 批量烧录后的单台设备初始化
|
||
# -----------------------------------------------------------------------------
|
||
# 在每台烧录好产品镜像的盒子上运行(root)。完成:
|
||
# 1. 设置主机名
|
||
# 2. 设置静态 IP(netplan)
|
||
# 3. 验证设备身份唯一(machine-id / SSH 密钥 / device_id)
|
||
# 4. 验证 safesight 服务正常
|
||
# 5. 启用断电保护 overlayroot(出厂镜像故意未启用,统一在此启用)
|
||
# 6. 重启
|
||
#
|
||
# 用法:
|
||
# sudo ./init-device.sh --hostname safesight-01 \
|
||
# --ip 10.0.0.83/24 --gw 10.0.0.1 --dns 10.0.0.1
|
||
# 可选:
|
||
# --token <后台管理统一主钥匙> 轮换 agent token(默认沿用镜像内主钥匙)
|
||
# --hls-ram HLS 分片放内存(减小 SD 写入)
|
||
# --dhcp 保持 DHCP(默认会写静态 IP)
|
||
# --no-overlay 不启用断电保护(仅初始化)
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
|
||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
||
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
||
die() { echo -e "${RED}✗${NC} $*" >&2; exit 1; }
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 参数
|
||
# ---------------------------------------------------------------------------
|
||
HOSTNAME="" IP_CIDR="" GW="" DNS="" TOKEN="" HLS_RAM=0 USE_DHCP=0 NO_OVERLAY=0
|
||
|
||
usage() {
|
||
echo "用法: sudo $0 --hostname <名> --ip <IP/掩码> --gw <网关> --dns <DNS> [选项]"
|
||
echo ""
|
||
echo "必选:"
|
||
echo " --hostname <名称> 如 safesight-01"
|
||
echo " --ip <IP/掩码> 如 10.0.0.83/24"
|
||
echo " --gw <网关> 如 10.0.0.1"
|
||
echo " --dns <DNS> 如 10.0.0.1"
|
||
echo "可选:"
|
||
echo " --token <主钥匙> 轮换后台管理统一主钥匙"
|
||
echo " --hls-ram HLS 分片放内存"
|
||
echo " --dhcp 保持 DHCP(不写静态 IP)"
|
||
echo " --no-overlay 不启用断电保护"
|
||
exit 1
|
||
}
|
||
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--hostname) HOSTNAME="$2"; shift 2 ;;
|
||
--ip) IP_CIDR="$2"; shift 2 ;;
|
||
--gw) GW="$2"; shift 2 ;;
|
||
--dns) DNS="$2"; shift 2 ;;
|
||
--token) TOKEN="$2"; shift 2 ;;
|
||
--hls-ram) HLS_RAM=1; shift ;;
|
||
--dhcp) USE_DHCP=1; shift ;;
|
||
--no-overlay) NO_OVERLAY=1; shift ;;
|
||
*) warn "未知参数: $1"; shift ;;
|
||
esac
|
||
done
|
||
|
||
[ "$(id -u)" -eq 0 ] || die "请使用 sudo 运行"
|
||
|
||
if [ "$USE_DHCP" = "0" ]; then
|
||
[ -n "$HOSTNAME" ] || die "缺少 --hostname"
|
||
[ -n "$IP_CIDR" ] || die "缺少 --ip"
|
||
[ -n "$GW" ] || die "缺少 --gw"
|
||
[ -n "$DNS" ] || die "缺少 --dns"
|
||
fi
|
||
|
||
OVERLAY_SCRIPT="/opt/safesight-edge-server/scripts/setup-overlayroot.sh"
|
||
[ -x "$OVERLAY_SCRIPT" ] || OVERLAY_SCRIPT="$(cd "$(dirname "$0")" && pwd)/setup-overlayroot.sh"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 1. 主机名
|
||
# ---------------------------------------------------------------------------
|
||
if [ -n "$HOSTNAME" ]; then
|
||
info "设置主机名: $HOSTNAME"
|
||
hostnamectl set-hostname "$HOSTNAME"
|
||
sed -i "/^127.0.1.1/d" /etc/hosts
|
||
echo "127.0.1.1 $HOSTNAME" >> /etc/hosts
|
||
ok "主机名已设置"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 2. 静态 IP(netplan)
|
||
# ---------------------------------------------------------------------------
|
||
NETPLAN_DIR="/etc/netplan"
|
||
if [ "$USE_DHCP" = "0" ]; then
|
||
# 检测默认网卡(优先有默认路由的网卡,其次第一个非 lo 网卡)
|
||
IFACE="$(ip route show default 2>/dev/null | awk '/default/{print $5; exit}')"
|
||
[ -n "$IFACE" ] || IFACE="$(ls /sys/class/net | grep -v '^lo$' | head -1)"
|
||
[ -n "$IFACE" ] || die "未检测到网卡"
|
||
|
||
info "配置静态 IP: $IFACE $IP_CIDR (gw=$GW dns=$DNS)"
|
||
mkdir -p "$NETPLAN_DIR"
|
||
# 备份现有配置
|
||
cp -a "$NETPLAN_DIR"/01-*.yaml "$NETPLAN_DIR/backup-"$(date +%s)".yaml" 2>/dev/null || true
|
||
rm -f "$NETPLAN_DIR"/01-*.yaml 2>/dev/null || true
|
||
|
||
cat > "$NETPLAN_DIR/01-safesight-static.yaml" << EOF
|
||
network:
|
||
version: 2
|
||
ethernets:
|
||
$IFACE:
|
||
dhcp4: no
|
||
addresses:
|
||
- $IP_CIDR
|
||
routes:
|
||
- to: default
|
||
via: $GW
|
||
nameservers:
|
||
addresses:
|
||
- $DNS
|
||
EOF
|
||
netplan apply 2>/dev/null || warn "netplan apply 失败(重启后生效)"
|
||
ok "静态 IP 已配置: $IP_CIDR"
|
||
else
|
||
info "保持 DHCP"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 3. 设备身份验证(出厂镜像已清空,首次启动应已自动重建)
|
||
# ---------------------------------------------------------------------------
|
||
info "验证设备身份 ..."
|
||
[ -s /etc/machine-id ] || { systemd-machine-id-setup 2>/dev/null || warn "machine-id 缺失(重启后自动生成)"; }
|
||
[ -s /etc/machine-id ] && ok "machine-id: $(cat /etc/machine-id)"
|
||
if ls /etc/ssh/ssh_host_*_key >/dev/null 2>&1; then
|
||
ok "SSH 主机密钥已生成"
|
||
else
|
||
ssh-keygen -A 2>/dev/null || true
|
||
ok "SSH 主机密钥已生成(ssh-keygen -A)"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4. agent token(可选轮换)
|
||
# ---------------------------------------------------------------------------
|
||
if [ -n "$TOKEN" ]; then
|
||
info "轮换 agent 主钥匙 ..."
|
||
printf '%s\n' "$TOKEN" > /opt/safesight-edge-agent/agent.token
|
||
chmod 600 /opt/safesight-edge-agent/agent.token
|
||
# 更新 agent 配置中的 token
|
||
if [ -f /opt/safesight-edge-agent/agent.config.json ]; then
|
||
sed -i "s/\"token\": \"[^\"]*\"/\"token\": \"$TOKEN\"/" /opt/safesight-edge-agent/agent.config.json
|
||
fi
|
||
ok "agent token 已更新"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4.5 /data 分区自动扩容(小镜像烧到 64G 产品卡后,把 /data 扩到整卡剩余)
|
||
# ---------------------------------------------------------------------------
|
||
grow_data_partition() {
|
||
info "扩容 /data 分区到 SD 卡剩余空间 ..."
|
||
DATA_PART="$(findmnt -no SOURCE /data 2>/dev/null || true)"
|
||
[ -n "$DATA_PART" ] || { warn "/data 未挂载,跳过扩容"; return; }
|
||
|
||
DISK=""; PARTNUM=""
|
||
case "$DATA_PART" in
|
||
/dev/mmcblk*) DISK="${DATA_PART%p*}"; PARTNUM="${DATA_PART##*p}" ;;
|
||
/dev/nvme*) DISK="${DATA_PART%p*}"; PARTNUM="${DATA_PART##*p}" ;;
|
||
/dev/sd*) DISK="${DATA_PART%[0-9]*}"; PARTNUM="${DATA_PART##*[a-z]}" ;;
|
||
esac
|
||
[ -n "$DISK" ] || { warn "无法识别 /data 所在磁盘,跳过扩容"; return; }
|
||
|
||
# 需要 growpart(cloud-guest-utils 提供)
|
||
if ! command -v growpart >/dev/null 2>&1; then
|
||
info "安装 growpart ..."
|
||
apt-get install -y cloud-guest-utils 2>/dev/null || warn "growpart 安装失败,跳过扩容"
|
||
fi
|
||
command -v growpart >/dev/null 2>&1 || return
|
||
|
||
# 停服务并卸载 /data 后扩容(避免分区忙)
|
||
systemctl stop safesight-edge-server safesight-edge-agent 2>/dev/null || true
|
||
umount /data 2>/dev/null || true
|
||
growpart "$DISK" "$PARTNUM" 2>/dev/null || warn "growpart 无变化(可能已扩满)"
|
||
resize2fs "$DATA_PART" 2>/dev/null || warn "resize2fs 失败"
|
||
mount /data 2>/dev/null || mount -a 2>/dev/null || true
|
||
systemctl start safesight-edge-server safesight-edge-agent 2>/dev/null || true
|
||
ok "/data 扩容完成: $(df -h /data 2>/dev/null | awk 'NR==2{print "总"$2" 可用"$4}')(64G 产品卡自动占满剩余空间)"
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 5. 验证 safesight 服务
|
||
# ---------------------------------------------------------------------------
|
||
info "验证服务 ..."
|
||
systemctl start safesight-edge-server safesight-edge-agent 2>/dev/null || true
|
||
sleep 3
|
||
for s in safesight-edge-server safesight-edge-agent; do
|
||
if systemctl is-active --quiet "$s" 2>/dev/null; then
|
||
ok "$s 运行中"
|
||
else
|
||
warn "$s 未运行(可稍后 systemctl status $s 排查)"
|
||
fi
|
||
done
|
||
if [ -f /var/lib/safesight-edge-agent/device_id ]; then
|
||
ok "device_id: $(cat /var/lib/safesight-edge-agent/device_id)"
|
||
else
|
||
info "device_id 由 agent 首次运行时生成(读取 machine-id)"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4.5 /data 分区自动扩容(小镜像烧到 64G 产品卡后,把 /data 扩到整卡剩余)
|
||
# ---------------------------------------------------------------------------
|
||
grow_data_partition
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 6. 启用断电保护
|
||
# ---------------------------------------------------------------------------
|
||
if [ "$NO_OVERLAY" = "1" ]; then
|
||
warn "跳过断电保护启用(--no-overlay)"
|
||
else
|
||
info "启用断电保护(overlayroot + /data)..."
|
||
EXTRA=""
|
||
[ "$HLS_RAM" = "1" ] && EXTRA="--hls-ram"
|
||
if [ -x "$OVERLAY_SCRIPT" ]; then
|
||
bash "$OVERLAY_SCRIPT" install --yes $EXTRA || die "断电保护启用失败"
|
||
else
|
||
die "未找到 setup-overlayroot.sh($OVERLAY_SCRIPT),请先完成部署"
|
||
fi
|
||
ok "断电保护已配置,重启后生效"
|
||
fi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 7. 完成
|
||
# ---------------------------------------------------------------------------
|
||
echo ""
|
||
echo -e "${GREEN}════════════════════════════════════════════════════════════════${NC}"
|
||
echo -e "${GREEN} 初始化完成!${NC}"
|
||
echo -e "${GREEN}════════════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
echo " IP: ${IP_CIDR:-DHCP}"
|
||
echo " 主机名: ${HOSTNAME:-未设置}"
|
||
echo " 管理界面: http://${IP_CIDR%/*}:9100"
|
||
echo ""
|
||
echo "请重启使全部配置生效:"
|
||
echo " sudo reboot"
|
||
echo ""
|
||
echo "重启后建议:"
|
||
echo " sudo $OVERLAY_SCRIPT verify # 断电保护验证"
|
||
echo " 在管理端发现该设备 → 设置别名 → 下发配置"
|