Files
awoooi/scripts/reboot-recovery/awoooi-startup.sh
Your Name 1c3c7279ac
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
fix(recovery): bound 188 startup clawbot rebuild
2026-06-30 22:37:33 +08:00

235 lines
10 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
# AWOOOI 重開機自動恢復腳本
# 2026-04-04 ogt: 根據實際事故建立,處理 container / Docker 啟動順序與 K3s Kine 維護。
# 2026-06-26 Codex: PostgreSQL checkpoint/WAL 錯誤改為 fail-closed
# 不在自動啟動腳本內執行 pg_resetwal避免資料破壞被誤判成恢復。
# 2026-06-26 Codex: 允許受控 recovery container 提供 14/main runtime
# 不再因 systemd postgresql@14-main failed 而誤判活 DB 為不可用。
# 2026-06-30 Codex: ClawBot rebuild 改為 bounded / opt-in避免重開機
# startup oneshot 被單一 image rebuild timeout 拖成 failed。
# 部署位置: /usr/local/bin/awoooi-startup.sh (on 192.168.0.188)
# systemd unit: /etc/systemd/system/awoooi-startup.service
set -uo pipefail
LOG="/var/log/awoooi-startup.log"
exec > >(tee -a "$LOG") 2>&1
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
CLAWBOT_UP_TIMEOUT_SECONDS="${CLAWBOT_UP_TIMEOUT_SECONDS:-90}"
CLAWBOT_BUILD_TIMEOUT_SECONDS="${CLAWBOT_BUILD_TIMEOUT_SECONDS:-120}"
CLAWBOT_STARTUP_REBUILD_ALLOWED="${CLAWBOT_STARTUP_REBUILD_ALLOWED:-0}"
run_bounded() {
seconds="$1"
shift
if command -v timeout >/dev/null 2>&1; then
timeout "${seconds}s" "$@"
else
"$@"
fi
}
postgres_runtime_ready() {
if systemctl is-active postgresql@14-main >/dev/null 2>&1; then
log "✅ PostgreSQL systemd unit active"
return 0
fi
if docker inspect -f '{{.State.Running}} {{.HostConfig.NetworkMode}}' k3s-postgres-recovery 2>/dev/null | grep -q '^true host$'; then
if pg_isready -h localhost -p 5432 >/dev/null 2>&1; then
log "✅ PostgreSQL recovery container active on host network"
return 0
fi
fi
return 1
}
log "=== AWOOOI 啟動序列開始 ==="
# ──────────────────────────────────────────────
# STEP 1: containerd 修復(若損壞)
# ──────────────────────────────────────────────
log "[1/7] 檢查 containerd..."
if ! systemctl is-active containerd >/dev/null 2>&1; then
log "containerd 未啟動,嘗試啟動..."
systemctl start containerd || true
sleep 5
fi
if ! systemctl is-active containerd >/dev/null 2>&1; then
log "containerd 啟動失敗,檢查 BoltDB 損壞..."
BOLT_DB="/var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db"
if [ -f "$BOLT_DB" ]; then
log "備份並刪除損壞的 meta.db..."
cp "$BOLT_DB" "${BOLT_DB}.bak.$(date +%Y%m%d%H%M%S)"
rm -f "$BOLT_DB"
fi
systemctl start containerd
sleep 5
systemctl is-active containerd && log "✅ containerd 修復成功" || { log "❌ containerd 修復失敗"; exit 1; }
else
log "✅ containerd 已 active"
fi
# ──────────────────────────────────────────────
# STEP 2: Docker 修復(若損壞)
# ──────────────────────────────────────────────
log "[2/7] 檢查 Docker..."
if ! systemctl is-active docker >/dev/null 2>&1; then
log "Docker 未啟動,嘗試啟動..."
systemctl start docker || true
sleep 8
fi
if ! systemctl is-active docker >/dev/null 2>&1; then
log "Docker 啟動失敗,修復 network BoltDB..."
NETWORK_DB="/var/lib/docker/network/files/local-kv.db"
if [ -f "$NETWORK_DB" ]; then
log "備份並刪除損壞的 local-kv.db..."
cp "$NETWORK_DB" "${NETWORK_DB}.bak.$(date +%Y%m%d%H%M%S)"
rm -f "$NETWORK_DB"
fi
systemctl restart containerd
sleep 5
systemctl start docker
sleep 8
systemctl is-active docker && log "✅ Docker 修復成功" || { log "❌ Docker 修復失敗"; exit 1; }
else
log "✅ Docker 已 active"
fi
# ──────────────────────────────────────────────
# STEP 3: PostgreSQL 啟動檢查(資料層錯誤必須人工 gate
# ──────────────────────────────────────────────
log "[3/7] 檢查 PostgreSQL..."
if ! postgres_runtime_ready; then
log "PostgreSQL 未啟動,嘗試啟動..."
systemctl start postgresql@14-main || true
sleep 8
fi
if ! postgres_runtime_ready; then
log "PostgreSQL 啟動失敗,檢查是否屬於 checkpoint/WAL 類資料層錯誤..."
if journalctl -u postgresql@14-main -n 20 | grep -q "could not locate a valid checkpoint"; then
log "❌ 偵測到 PostgreSQL checkpoint/WAL 錯誤;禁止自動 pg_resetwal。"
log "需要 DB owner、備份/restore evidence、maintenance window 與 post-check 後才能人工處理。"
exit 1
fi
postgres_runtime_ready && log "✅ PostgreSQL runtime 可用" || { log "❌ PostgreSQL 修復失敗"; exit 1; }
fi
# 等待 PG 接受連線
log "等待 PostgreSQL 就緒..."
for i in $(seq 1 30); do
pg_isready -h localhost -p 5432 >/dev/null 2>&1 && break
sleep 2
done
pg_isready -h localhost -p 5432 && log "✅ PostgreSQL accepting connections" || { log "❌ PostgreSQL 無法接受連線"; exit 1; }
# kine 表維護(若有 stale 連線或 WAL 剛重置)
log "維護 k3s_datastore kine 表..."
sudo -u postgres psql -d k3s_datastore -c "
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname='k3s_datastore' AND pid!=pg_backend_pid() AND state='active'
AND query_start < now() - interval '5 minutes';
" 2>/dev/null || true
sudo -u postgres psql -d k3s_datastore -c "VACUUM ANALYZE kine;" 2>/dev/null && log "✅ kine VACUUM ANALYZE 完成" || log "⚠️ kine VACUUM 跳過(可能無法連線)"
# ──────────────────────────────────────────────
# STEP 4: Redis
# ──────────────────────────────────────────────
log "[4/7] 啟動 Redis..."
systemctl start redis-server || true
sleep 3
redis-cli ping 2>/dev/null | grep -q PONG && log "✅ Redis UP" || log "⚠️ Redis 可能未就緒"
# ──────────────────────────────────────────────
# STEP 5: Ollama
# ──────────────────────────────────────────────
log "[5/7] 啟動 Ollama..."
systemctl start ollama || true
# Ollama 載入模型需要時間,不立刻驗證
# ──────────────────────────────────────────────
# STEP 6: Nginx
# ──────────────────────────────────────────────
log "[6/7] 啟動 Nginx..."
systemctl start nginx || true
systemctl is-active nginx >/dev/null 2>&1 && log "✅ Nginx UP" || log "⚠️ Nginx 未就緒"
# ──────────────────────────────────────────────
# STEP 7: Docker Compose 服務
# ──────────────────────────────────────────────
log "[7/7] 啟動 Docker Compose 服務..."
# SignOz
SIGNOZ_DIR="/home/ollama/signoz/deploy/docker"
if [ -f "$SIGNOZ_DIR/docker-compose.yaml" ]; then
log "啟動 SignOz..."
cd "$SIGNOZ_DIR"
docker compose up -d 2>&1 | tail -3
log "✅ SignOz 啟動指令已發送"
else
log "⚠️ 找不到 SignOz compose 檔案"
fi
# MinIOVelero 備份存儲)
# 2026-04-05 Claude Code: 加入 MinIO — 解決 Velero BSL Unavailable 問題
MINIO_DIR="/home/ollama/minio"
if [ -f "$MINIO_DIR/docker-compose.yml" ]; then
log "啟動 MinIO (Velero 備份存儲)..."
cd "$MINIO_DIR"
docker compose up -d 2>&1 | tail -3
log "✅ MinIO 啟動指令已發送"
else
log "⚠️ 找不到 MinIO compose 檔案"
fi
# ClawBot依賴 aiops-network
# 確保 aiops-network 存在(重開機後 external network 需手動建立)
if ! docker network ls | grep -q aiops-network; then
log "建立 aiops-network..."
docker network create aiops-network && log "✅ aiops-network 建立" || log "⚠️ aiops-network 建立失敗"
fi
CLAWBOT_DIR="/home/ollama/clawbot-v5"
if [ -f "$CLAWBOT_DIR/docker-compose.yml" ]; then
log "啟動 ClawBot..."
cd "$CLAWBOT_DIR"
if ! run_bounded "$CLAWBOT_UP_TIMEOUT_SECONDS" docker compose up -d 2>&1 | tee /tmp/clawbot-up.log | grep -q "Started\|Running\|healthy"; then
log "⚠️ ClawBot 啟動未在 ${CLAWBOT_UP_TIMEOUT_SECONDS}s 內確認;交由 post-check 判定"
if [ "$CLAWBOT_STARTUP_REBUILD_ALLOWED" = "1" ]; then
log "ClawBot bounded rebuild 已允許,最多等待 ${CLAWBOT_BUILD_TIMEOUT_SECONDS}s..."
run_bounded "$CLAWBOT_BUILD_TIMEOUT_SECONDS" docker compose build 2>&1 | tail -5 || log "⚠️ ClawBot bounded rebuild 未完成"
run_bounded "$CLAWBOT_UP_TIMEOUT_SECONDS" docker compose up -d 2>&1 | tail -3 || log "⚠️ ClawBot rebuild 後啟動仍未確認"
else
log "⚠️ ClawBot rebuild 預設停用,避免拖垮 10 分鐘 post-reboot SLO"
fi
fi
if docker inspect -f '{{.State.Running}}' clawbot 2>/dev/null | grep -q '^true$'; then
log "✅ ClawBot container running"
else
log "⚠️ ClawBot container 未確認 runningpost-reboot verifier 會標示服務 blocker"
fi
log "✅ ClawBot 啟動指令已發送"
else
log "⚠️ 找不到 ClawBot compose 檔案"
fi
# ──────────────────────────────────────────────
# 完成
# ──────────────────────────────────────────────
log "=== AWOOOI 啟動序列完成 ==="
# 最後永遠 exit 0不讓 systemd 認為失敗
log "K3s 需在 120/121 手動確認啟動(或由 k3s.service 自動啟動)"
log "詳細 SOP: docs/runbooks/REBOOT-RECOVERY-SOP.md"
exit 0