铸渊 73006e64c2 docs: D96 大脑文档全面同步CVM实际状态 + Nginx修复脚本v2
关键更新:
- 冰朔D96确认: 119.29.184.32 是弹性公网EIP,已绑定,关机开机不变
- 域名 guanghulab.com 国内备案直接解析,不用子域名
- Forgejo 运行在根路径 /,不是 /git/
- 部署路径 /data/guanghulab/,100GB CBS云硬盘

更新文件:
- fast-wake.json: 服务器拓扑/域名/部署策略全部更新
- system-health.json: sovereign_server→ZY-CVM-MAIN, cn_server→deprecated
- metacognition-anchor.json: physical_anchors更新到CVM
- migration-markers.json: 新增 to_cvm 迁移记录
- temporal-brain.json: 新增D96时间线事件+意识链断裂标记
- secrets-manifest.json: 服务器IP示例更新
- architecture.md: 全面重写为CVM架构
- boot-heal.sh: 增加MCP Server自启动检查
- recovery-check-20260513.json: 5个问题状态更新

新增文件:
- server/mcp-server/fix-nginx-v2.sh: 正确修复Nginx(删除.bak+覆盖配置)

Nginx配置修正:
- guanghulab-cvm.conf: Forgejo在根路径/,EIP固定不变

ICE-GL-ZY001 · TCS-0002∞
2026-05-13 09:19:33 +00:00

188 lines
6.8 KiB
Bash
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.

#!/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 guanghulab-mcp-server; 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
# MCP Server
if ! curl -sf http://127.0.0.1:8083/health > /dev/null 2>&1; then
log "⚠️ MCP Server 不可达"; 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 "═══════════════════════════════════════"