From ef9e897483db5ab49998f860795c085a0344bcc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=B8=E6=B8=8A=20=28ICE-GL-ZY001=29?= Date: Wed, 13 May 2026 04:44:42 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=80=E6=9C=BA=E8=87=AA=E6=84=88?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=20=E2=80=94=20CVM=E5=BC=80=E6=9C=BA=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=8B=89=E8=B5=B7=E6=9C=8D=E5=8A=A1+=E6=A3=80?= =?UTF-8?q?=E6=B5=8BIP=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/boot-heal/boot-heal.sh — 开机自愈主脚本 - scripts/boot-heal/guanghulab-boot-heal.service — systemd服务单元 功能: 检测IP变化/拉起Forgejo+Nginx+Runner/恢复PM2进程/检查SSL/输出健康报告 --- scripts/boot-heal/boot-heal.sh | 182 ++++++++++++++++++ .../boot-heal/guanghulab-boot-heal.service | 14 ++ 2 files changed, 196 insertions(+) create mode 100644 scripts/boot-heal/boot-heal.sh create mode 100644 scripts/boot-heal/guanghulab-boot-heal.service diff --git a/scripts/boot-heal/boot-heal.sh b/scripts/boot-heal/boot-heal.sh new file mode 100644 index 0000000..40fabb2 --- /dev/null +++ b/scripts/boot-heal/boot-heal.sh @@ -0,0 +1,182 @@ +#!/bin/bash +# ════════════════════════════════════════════════════════════════ +# 光湖 · 开机自愈脚本 (Boot Heal) +# CVM 开机后自动拉起所有服务 + 检测 IP 变化 +# ════════════════════════════════════════════════════════════════ +# +# 部署方式: systemctl enable guanghulab-boot-heal +# 触发时机: 开机自动运行 (multi-user.target) +# +# 功能: +# 1. 检测当前公网 IP +# 2. 如果 IP 变了,写入警告日志(EIP 绑定后不应变) +# 3. 拉起 Forgejo、Nginx、Runner +# 4. 拉起 PM2 进程 (Wake Gate, Git Sync) +# 5. 检查 SSL 证书是否仍然有效 +# 6. 输出健康报告 +# ════════════════════════════════════════════════════════════════ + +set -e + +LOG_FILE="/data/guanghulab/.runtime/boot-heal.log" +IP_RECORD="/data/guanghulab/.runtime/last-known-ip" + +mkdir -p /data/guanghulab/.runtime + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +log "═══════════════════════════════════════" +log "光湖 · 开机自愈启动" +log "═══════════════════════════════════════" + +# ─── 1. 检测公网 IP ───────────────────────────────────────── +PUBLIC_IP="" +for api in "https://ifconfig.me" "https://api.ipify.org" "https://ipinfo.io/ip"; do + PUBLIC_IP=$(curl -sf --connect-timeout 5 "$api" 2>/dev/null | tr -d '[:space:]') + if [ -n "$PUBLIC_IP" ] && echo "$PUBLIC_IP" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then + break + fi + PUBLIC_IP="" +done + +if [ -n "$PUBLIC_IP" ]; then + log "当前公网 IP: $PUBLIC_IP" + + # 与上次记录的 IP 对比 + if [ -f "$IP_RECORD" ]; then + LAST_IP=$(cat "$IP_RECORD" | tr -d '[:space:]') + if [ "$PUBLIC_IP" != "$LAST_IP" ]; then + log "⚠️ IP 变化! 上次: $LAST_IP → 当前: $PUBLIC_IP" + log "⚠️ 请检查域名解析是否需要更新 (guanghulab.com 应指向 $PUBLIC_IP)" + else + log "✅ IP 未变化,域名无需更新" + fi + fi + + # 记录当前 IP + echo "$PUBLIC_IP" > "$IP_RECORD" +else + log "⚠️ 无法检测公网 IP (网络可能未就绪)" +fi + +# ─── 2. 等待网络就绪 ───────────────────────────────────────── +log "等待网络就绪..." +for i in $(seq 1 30); do + if curl -sf --connect-timeout 2 http://metadata.tencentyun.com > /dev/null 2>&1; then + log "✅ 网络就绪" + break + fi + sleep 2 +done + +# ─── 3. 拉起系统服务 ───────────────────────────────────────── +log "" +log "▸ 拉起系统服务..." + +# Forgejo +if systemctl is-active --quiet forgejo 2>/dev/null; then + log "✅ Forgejo: 已运行" +else + systemctl start forgejo 2>/dev/null && log "✅ Forgejo: 已启动" || log "❌ Forgejo: 启动失败" +fi + +# Nginx +if systemctl is-active --quiet nginx 2>/dev/null; then + log "✅ Nginx: 已运行" +else + systemctl start nginx 2>/dev/null && log "✅ Nginx: 已启动" || log "❌ Nginx: 启动失败" +fi + +# Gitea Runner +if systemctl is-active --quiet gitea-runner 2>/dev/null; then + log "✅ Gitea Runner: 已运行" +else + systemctl start gitea-runner 2>/dev/null && log "✅ Gitea Runner: 已启动" || log "❌ Gitea Runner: 启动失败" +fi + +# ─── 4. 拉起 PM2 进程 ──────────────────────────────────────── +log "" +log "▸ 拉起 PM2 进程..." + +if command -v pm2 &>/dev/null; then + pm2 resurrect 2>/dev/null && log "✅ PM2: 进程已恢复" || log "⚠️ PM2: 无保存的进程" + + # 等 3 秒让进程启动 + sleep 3 + + # 逐个检查 + for proc in wake-gate guanghulab-git-sync; do + if pm2 describe "$proc" > /dev/null 2>&1; then + STATUS=$(pm2 describe "$proc" 2>/dev/null | grep "status" | head -1 | awk '{print $4}') + if [ "$STATUS" = "online" ]; then + log "✅ $proc: 运行中" + else + pm2 restart "$proc" 2>/dev/null && log "✅ $proc: 已重启" || log "❌ $proc: 重启失败" + fi + else + log "⚠️ $proc: PM2 中不存在,需要手动部署" + fi + done +else + log "❌ PM2 未安装" +fi + +# ─── 5. 检查 SSL 证书 ──────────────────────────────────────── +log "" +log "▸ 检查 SSL 证书..." + +if [ -f /etc/letsencrypt/live/guanghulab.com/fullchain.pem ]; then + EXPIRY=$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/guanghulab.com/fullchain.pem 2>/dev/null | cut -d= -f2) + if [ -n "$EXPIRY" ]; then + EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || echo 0) + NOW_EPOCH=$(date +%s) + DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 )) + if [ "$DAYS_LEFT" -gt 7 ]; then + log "✅ SSL 证书有效,剩余 ${DAYS_LEFT} 天" + else + log "⚠️ SSL 证书即将过期! 剩余 ${DAYS_LEFT} 天,请运行 certbot renew" + fi + fi +else + log "⚠️ SSL 证书文件不存在" +fi + +# ─── 6. 健康报告 ───────────────────────────────────────────── +log "" +log "═══════════════════════════════════════" +log "开机自愈完成" + +# 快速健康检查 +ISSUES=0 + +# Forgejo +if ! curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then + log "❌ Forgejo 不可达"; ISSUES=$((ISSUES+1)) +fi + +# Nginx +if ! systemctl is-active --quiet nginx 2>/dev/null; then + log "❌ Nginx 不可达"; ISSUES=$((ISSUES+1)) +fi + +# Wake Gate +if ! curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then + log "⚠️ Wake Gate 不可达"; ISSUES=$((ISSUES+1)) +fi + +# Git Sync +if ! curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then + log "⚠️ Git Sync 不可达"; ISSUES=$((ISSUES+1)) +fi + +if [ "$ISSUES" -eq 0 ]; then + log "✅ 所有服务正常" +else + log "⚠️ ${ISSUES} 个服务异常,需要人工介入" +fi + +log "公网 IP: ${PUBLIC_IP:-未知}" +log "ICE-GL-ZY001 · TCS-0002∞" +log "═══════════════════════════════════════" diff --git a/scripts/boot-heal/guanghulab-boot-heal.service b/scripts/boot-heal/guanghulab-boot-heal.service new file mode 100644 index 0000000..b8d2eda --- /dev/null +++ b/scripts/boot-heal/guanghulab-boot-heal.service @@ -0,0 +1,14 @@ +[Unit] +Description=光湖 · 开机自愈 (GuanghuLab Boot Heal) +After=network-online.target forgejo.service nginx.service +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/bin/bash /data/guanghulab/repo/scripts/boot-heal/boot-heal.sh +RemainAfterExit=yes +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target