Files
awoooi/docs/runbooks/REBOOT-RECOVERY-SOP.md

903 lines
71 KiB
Markdown
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.
# AWOOOI 重開機恢復 SOP
> **版本**: v5.31
> **最後更新**: 2026-07-11 (台北時間)
> **更新者**: Codex
> **觸發事件**: 2026-07-11 真實全主機 reboot drill、late recovery 與 Agent99/VMware/runtime 閉環
---
## 目錄
1. [架構概覽與依賴圖](#架構概覽與依賴圖)
2. [自動化腳本狀態](#自動化腳本狀態)
3. [正常重啟流程 (計劃性維護)](#正常重啟流程)
4. [異常重啟流程 (緊急恢復)](#異常重啟流程)
5. [各主機詳細啟動序列](#各主機詳細啟動序列)
6. [常見故障排查手冊](#常見故障排查手冊)
7. [E2E 驗證腳本](#e2e-驗證腳本)
---
## 架構概覽與依賴圖
### 2026-07-11 真實 reboot drillSLA breach 與 late recovery 真相
本輪不是模擬。重啟前 Agent99 runtime `7cf83f7f59a5d72ce6c303f59c144bd5bf8566df``14/14` matched、核心 task `9/9`production-principal `Recover` baseline 已通過 full-SOP coordinator且正確標示 `scope=service_recovery``rebootSloClaimed=false`。七主機與六核心路由在 observer 開始時均可達。
真實重啟後,`2026-07-11T08:00:07Z` 五台 VMware guests 與六核心路由離線T+10 `08:10:07Z` 時服務仍未恢復,因此本輪 SLA 明確失敗。後續交叉比對 Windows boot time 證明 99 實際於 `16:08:03+08:00` 開機;舊 observer 將 host probe 從 `nc` 改成 `curl telnet://`TCP 已連線仍等待 SSH payload 到 timeout導致大量 host-down false negative。這些 host samples 不再作為「99 沒有回來」的證據route-down samples 與 T+10 breach 仍有效;修正版 observer 已改用 Python `socket.create_connection`
真正延遲鏈是Agent99 Startup-Recovery 於 `16:08:57` 啟動,卻被原本 `PT10M` task limit 終止boot event 又在 terminal recovery 前被消耗。VMware autostart task 直到 `16:26:48` 才執行188 PostgreSQL 到 `16:40:05` 才 ready120/121 K3s 在此之前持續等待 `192.168.0.188:5432`。120 與 121 又分別在 `16:49:32``16:56:00` 收到 Windows 99 VMware VIX `SoftReboot`;這不是 guest crash也不是 unattended-upgrades。VMware process audit 未啟用,不能歸屬 actor任何後續 reset/stop/suspend 都必須有同一 trace、before evidence、verifier 與 Telegram/KM receipt。
服務於 `16:59` 後恢復SignOz upstream bridge 受控修復後 public route 也回 200。`17:35` production-principal Agent99 `Recover``10.3` 秒完成 service-recovery七主機、VMware、Harbor、K3s、service/data/freshness/backup/188 hygiene/full-SOP coordinator 全通過,且 `rebootSloClaimed=false``17:37` 全部 23 個 public surfaces 為預期 2xx/3xx。機器可讀完整 receipt 在 `docs/operations/agent99-full-host-reboot-drill-20260711.snapshot.json`
因此正式 SOP 固定為三層,不能再把 99 本機排程當成完整自動化:
1. **L0 外部層99/LAN 之外)**:每 15 秒 public synthetic連續兩輪 outage quorum 後建立 durable incident執行繁中 Telegram down、外部 maintenance origin、具 verifier 的 99 out-of-band power。WOL 只可作第一個低風險嘗試,不是唯一電源路徑。
2. **L1 Agent99/VMware 層**99 回線後由 L0 投遞同一 incidentAgent99 驗證 Windows boot、runtime manifest、tasks、VMware service、VMX、lock依序恢復並驗證 110/188/112/120/121。111 是獨立實體 Mac。
3. **L2 110 full-SOP 層**110 回線後建立 fresh scorecard驗證七主機、service/data/freshness/backup、188 hygiene、edge/maintenance、VMware/update policy、版本與 callback。只有 blocker=0 且 `can_claim_slo=true` 才可撤維護頁與結案。
L0 source contract 是 `scripts/reboot-recovery/external-l0-recovery-supervisor.py``awoooi-external-l0-recovery-supervisor.service/.timer``ops/reboot-recovery/external-l0-recovery-supervisor.example.json`。Callback 只能位於 `/usr/local/libexec/awoooi-l0` allowlist使用 direct exec 與 `--event-file`,禁止 arbitrary shellTelegram/token、maintenance provider 與電源憑證留在外部 host-local wrapper不寫入 repo/evidence。未部署外部 runtime、OOB power 與 maintenance callbacks前正式狀態是 `slo_breached_recovered_late`:目前服務正常,不代表下一次全斷可在 10 分鐘內自動恢復。
### 2026-07-11 P0 Cold-start 實機收旂
最新完成 artifact `/home/wooo/reboot-recovery/reboot-auto-recovery-slo-20260711-172623` 回報 `GREEN_WITH_EVIDENCE_WARNINGS`、readiness `87%`、七主機 reachable、service/data/backup/post-start/188 hygiene 與 Windows99 VMware `5/5` 皆綠。唯三 blockers 是 `all_required_hosts_not_in_10_minute_reboot_window``fresh_all_host_reboot_event_missing``host_boot_observation_older_than_target_window`;它們保留本輪逾時真相,只能由下一次真實全主機 reboot 的同一 10 分鐘 window 驗收,不能靠事後健康讀回清除。
- 99 VMware required guest set 是 `110/188/120/121/112`。111 是 M1 Pro MacBook Local Ollama已用 110 public key + forced command 回傳 Darwin boot/uptime/Ollama status不授權 shell。舊版把 111 當 Windows99 VMX 的敘述依 live inventory 標記為 superseded。
- 110 runner fail-closed authority 只能使用 `flock` + `timeout` + `--ensure`,不得每分鐘無條件 mask + daemon-reload。完成條件是 apply/reload count 為 0 且 PID 1 CPU 收旂。
- edge fallback 只走 `/usr/local/sbin/awoooi-edge-maintenance check|apply|rollback-latest`canonical/live SHA parity、backup、`nginx -t`、reload、public route verifier 缺一不可完成。`/api/sentry-tunnel` 必須在 generic `/api/` 前且 proxy 到 web。
- 111 host probe 只讀 `boot_id=darwin_<epoch>` / uptime / LaunchAgent / local Ollama`UNKNOWN_UPTIME_HOSTS=[]` 才可進 reboot-event detector。
- SLO timer 使用 `OnBootSec=2min` + `OnUnitInactiveSec=5min`;禁止恢復 `OnUnitActiveSec=2min`,否則 2-3 分鐘 oneshot 會連續排程並製造 CPU / 告警噪音。
- 188 production PG 由 `k3s-postgres-recovery` host-network container 管理host `start.conf=manual``awoooi-host188-hygiene reconcile` 只安裝 startup source/unit、daemon-reload、reset stale failed state禁止產品 restart。startup 內 containerd/Docker metadata 刪除、kine write、ClawBot rebuild、188 legacy SignOz 均預設 fail-closed/opt-in。
- 2026-07-10 Stock gap 來自 TWSE 颱風休市,不是 ingestion 中斷。交易日曆修正後 freshness/ingestion 皆 green不得對休市日無限重跑 ETL。
下一次 reboot drill 必須在 T+0 由 boot-id 變更自動觸發 Agent99/110 SLO laneT+10 前產生同一 run 的 VM power、host uptime、services/routes、Stock freshness、backup、Telegram lifecycle 與 KM writeback receipt。若任一主機不在 window不得宣稱 10 分鐘 SLA 完成。
### 2026-07-11 Agent99 full-SOP coordinator
舊版 Agent99 `Recover` 只驗證 `vm_start / host_reachability / harbor_registry / k3s_workloads / public_routes`live evidence 曾在 10.4 秒寫入 `withinSlo=true`。這只能證明既有服務 baseline不能證明全主機 reboot SLOv5.28 起標記為 superseded`full_sop_scorecard` 是必要 phase。
`recoveryCoordinator` 固定從 99 以專用 Agent99 SSH identity 讀 110 最新 `/home/wooo/reboot-recovery/reboot-auto-recovery-slo-*` 的 bounded summary不讀 raw log、`.env`、secret、session 或 backup contents。必要輸入是 `summary.txt``host-probe.txt``public-maintenance-edge-fallback.txt``windows99-vmware-verify.txt``scorecard.json` 的 allowlisted fields。
一般告警觸發的服務恢復,至少要通過 7 host reachable、`SERVICE_GREEN=1``PRODUCT_DATA_GREEN=1`、Stock freshness `ok``BACKUP_CORE_GREEN=1`、188 service/hygiene green、edge fallback ready、VMware ready、Windows Update policy ready、source controls 與 public fallback ready。此時 scope 只能是 `service_recovery`Telegram 必須明示「沒有 fresh reboot event不宣稱全主機重啟 SLA」。
若 99 boot age 小於 10 分鐘或 boot identity 改變scope 強制為 `full_host_reboot`Agent99 最多等待 420 秒讓 110 的 `OnBootSec=2min` SLO lane 完成。除上述 baseline 外還必須同時符合artifact timestamp 新於本次 recovery start、`fresh_reboot_window_observed=true`、active blocker `0``can_claim_all_services_recovered_within_target=true`。任一缺口都要 fail-closed`Get-AgentOutcomeContract``full_sop_coordinator_verified` 不得通過。
部署前 gate 是PowerShell parse、Agent99 contract、synthetic fresh/blocked fixture、runtime staging hash部署後 gate 是:`scripts/reboot-recovery/agent99-live-preflight.ps1``9/9` tasks、relay listener、queue zero、required evidence fresh、runtime manifest matched再由 production S4U principal 觸發 `Recover`。Administrator 直接跑 launcher 不能取代 production-principal verifier。
2026-07-11 20:09 final runtime receiptAgent99 已原子 promote 為 `d0e364bfa42947190cf1e88d1d85623e23f84858`deploy evidence `C:\Wooo\Agent99\evidence\agent99-deploy-20260711-200939.json`、rollback backup `C:\Wooo\Agent99\deploy\backup-20260711-200939`runtime manifest `14/14`、mismatch `0`。production-principal evidence `C:\Wooo\Agent99\evidence\agent99-Recover-20260711-200320.json` 顯示七個 phases 全部 `ok`,新增必要 phase `host112_guest_readiness`before/after verifier 均為 ready、`applyAttempted=false`transport 為 direct serializedrecovery completed、coordinator verifiedelapsed `44.6s`,且正確維持 `scope=service_recovery``rebootSloClaimed=false`。SRE routing self-test `17/17`、Telegram ingest self-test `16/16`,後者不寫 alert、不送 Telegram。這是 service recovery closure不覆蓋原始 T+10 breach。
Windows 99 VMware task 已經由 `scripts/reboot-recovery/windows99-vmware-autostart-controlled-apply.ps1` 正式受控套用DryRun、Apply、Verify 同一 receipttask XML、舊 startup script、Windows Update policy 與 service start modes 都有 rollback。Evidence `C:\Wooo\Agent99\evidence\windows99-vmware-autostart-apply-20260711-173505.json` 回報 pre/post VMX process 都是 `5`、running guest rows `5`、process-aware duplicate-start guard true、沒有 host reboot、沒有 VM power change、沒有 secret read。這才是 Agent99 與重啟 SOP 的 runtime 整合證據;仍不取代下一次 600 秒 E2E drill。
### 112 Kali 真正開機完成 Gate
112 一直存在於 Windows99 required VM inventory、host probe 與 reboot event required host set本次事故暴露的缺陷是舊流程只用 VMX process、Ping、SSH port 或 node-exporter 判定「已開機」,沒有驗證 Kali 的圖形登入與安全服務。這會讓 112 實際以 `multi-user.target` 無 GUI 啟動、LightDM 未執行且 Wazuh Indexer 開機逾時時仍被錯誤判成綠燈。另一個獨立現象是 SYSTEM principal 以 `vmrun ... nogui` 啟動的 VMX 不會出現在 Administrator 的 VMware Workstation sessionGUI 可能顯示 `Powered off`;在 VMX process 與 guest boot identity 已存在時,禁止因此再次按 Power on避免 exclusive lock 與重複啟動。
112 的固定完成鏈改為:`VMX process -> guest boot_id/uptime -> SSH forced-command readback -> systemd running -> graphical.target -> LightDM/display-manager -> Xorg -> open-vm-tools -> Wazuh Indexer/Manager/Dashboard/Filebeat -> recovery timer -> Console-ready``scripts/reboot-recovery/host112-guest-readiness.sh` 提供 no-secret `--check` 與 allowlisted `--apply`apply 只可補起 graphical target、LightDM、VMware Tools以及在開機 210 秒後、600 秒 cooldown 內重試一次 Wazuh Indexer。`awoooi-host112-guest-recovery.timer` 於開機 20 秒後開始、每 60 秒檢查;不得 reboot host、reset/power cycle VM、修改 firewall、讀取 secret 或執行資料庫寫入。
110 到 112 使用來源限制、`restrict` 與 forced command 的既有公鑰,只能執行 `/usr/local/sbin/awoooi-host112-guest-readiness --check`,不能取得 shell 或 forwarding。Host probe 必須輸出 `console_ready=1``services_ready=1``recovery_timer_ready=1``guest_ready=1`;任何一項缺失都建立具名 `host112_*` blocker阻止 full-host recovery 與 maintenance withdrawal。Prometheus 固定輸出 `awoooi_host112_guest_readback_present``awoooi_host112_console_ready``awoooi_host112_services_ready``awoooi_host112_recovery_timer_ready``awoooi_host112_guest_ready`Agent99 收到狀態變更後執行受控修復並以同一 incident 回報 verifier 結果。
2026-07-11 runtime closureinstaller runs `host112-guest-recovery-20260711T193137+0800-35698``host112-guest-recovery-20260711T195449+0800-43505` 已完成備份、受限公鑰、exact-command sudo、timer 與 durable rollback installer未 reboot、未改 VM power、未讀 secret。110 artifact `/home/wooo/reboot-recovery/reboot-auto-recovery-slo-20260711-200338` 讀回 112 `systemd=running`、Console/Wazuh/timer/guest `1/1/1/1`、五項 Prometheus gauges `5/5``host112_* blocker=0`。兩條 112 告警規則 health `2/2 ok` 且 healthy state 為 inactivesource/canonical/active/runtime rule SHA 均為 `86fd0e56cb7633da2a5a09960b8bf2dd61ee917c55072b5a16ba68577e8379cc`。Windows99 verifier 同輪讀到 `VM_PROCESS alias=112 process_id=22288 session_id=0 owner=NT_AUTHORITY\SYSTEM ui_truth=runtime_running_gui_session_isolated`,因此 operator power truth 固定信任 VMX process + guest readiness不信任跨 principal 的互動式 VMware UI。Agent99 `Recover` 也已把 112 納入必要 phase 並完成 production-principal verifier完整 receipt 見 `docs/operations/host112-guest-recovery-20260711.snapshot.json`。Agent99 tests `49 passed`、reboot-recovery tests `142 passed`。目前 scorecard 僅保留 `all_required_hosts_not_in_10_minute_reboot_window``fresh_all_host_reboot_event_missing``host_boot_observation_older_than_target_window`;它們只能由下一次真實全主機 600 秒 drill 關閉。
2026-07-11 20:25 Agent99 false-green closureWindows runtime 已原子部署 `06509e071c8b7c538feb112e4e4774ee22abf7d1`deploy receipt `agent99-deploy-20260711-202021.json`、rollback `backup-20260711-202021`、manifest `14/14` matched。新版 preflight 為 tasks `9/9`、required evidence fresh/usable `7/7`、parse/content failure `0`。production-principal run `agent99-recovery-20260711-202159-3404b5b3` 在 42.9 秒完成 5/5 VM、5/5 host、5/5 public 與七個 phaseTG `sent=1``visualSent=1`,實際圖卡 `agent99-card-recovery_slo_result-20260711-202159.png` 為 1200×760 phase timeline下一輪同狀態以 `dedupe_window` 抑制且留下 receipt。這關閉 Agent99 可見性假綠,不改寫原始全主機 T+10 breach。
### 2026-07-10 P0 Agent99 Transport / Verified Remediation 覆蓋
99 的 Agent99 是全主機恢復控制節點。Windows 開機後由既有 Agent99 scheduled tasks 啟動;`Status` 讀到 boot identity 變更時,只能用 single-flight queue 觸發一次 `Recover`,並以 10 分鐘 cooldown 避免併發恢復。每次執行必須寫入 `bootState``recoveryTrigger``recoveryCoordinator``recoverySlo` evidence。只有實際全主機重啟事件、七主機 reachability、必要服務、public routes、資料 freshness、備份、edge fallback、VMware/Windows policy 與 188 hygiene 全部在同一個 10 分鐘 window 通過,才可宣稱 reboot recovery SLO 達標。
Agent99 SSH transport 使用 99 本機專用 Ed25519 identity `C:\Wooo\Agent99\keys\agent99_ed25519`private key 不得輸出、複製到 evidence、Telegram 或 repo。只允許將 public key 以 `from="192.168.0.99",restrict` 授權到 110 `wooo` 與 112 `kali`。固定 route map 為 110 / 112 direct120 / 121 / 188 優先經 110 jump禁止以共用密碼或任意 interactive shell 取代。Runtime 必須由 `agent99-deploy.ps1` 經 parse、contract、synthetic、staging、backup、atomic promote 與 rollback gate 部署,`runtime-manifest.json.sourceRevision` 必須等於 Gitea commit SHA。
所有 SSH command 都必須 bounded timeout。Timeout 時先用 `taskkill /T` 終止 process tree再確認 process 是否仍存活;`Perf` / `LoadShed` controlled apply 執行前會清理只符合以下全部條件的 stale clientAgent99 allowlisted target、`BatchMode=yes`、沒有 `-N/-L/-R/-D` tunnel、超過 15 分鐘且 parent 已消失,或超過 60 分鐘 hard limit。guard 不記錄 command line不碰互動式 SSH 或 tunnel結果寫入 `sshProcessGuard` evidence。
Agent99 的 Heartbeat、Performance、手動 Status 或告警修復可能由不同 Windows process 同時啟動。所有 `Invoke-SshText` 必須先取得 `C:\Wooo\Agent99\state\ssh-transport.lock` 的 exclusive file handle跨 scheduled task process 與 RDP operator session 一次只允許一條 Agent99 SSH command程序結束時由 OS 自動釋放 handle。預設最多等待 90 秒,逾時回 `ssh_transport_lock_timeout`,不得再開新 client。若同輪五主機 ping 全通,但 Harbor、AWOOOI 與所有 performance readback 同時空白,優先判定為 transport contention必須等序列化後重驗不得宣稱所有服務同時故障。
99 的正式 Agent99 scheduled tasks 目前使用本機 `WOOO` 帳號的 S4U principal專用 private key ACL 不授權 RDP 的 `Administrator`。因此 operator 直接執行 `agent99-run.ps1` 若出現 `Load key ... Permission denied`,只能判定為 execution-identity mismatch不得判定五台主機或服務故障也不得為了測試放寬 private-key ACL。正式 verifier 必須用 `schtasks /run /tn Wooo-Agent99-Heartbeat``Wooo-Agent99-Performance-Guard` 走既有 principalevidence 必須包含 `transportSerialized``transportLockWaitMs`。長駐 `Wooo-Agent99-SRE-Alert-Relay` 若 task 為 `Running/ok`,不得因 start evidence 超過 1440 分鐘就製造 freshness warning應記錄 `running_task_supersedes_stale_start_evidence`
效能修復不得以 command exit code 取代 post-verifier。只要 action 配置 verifier成功判定完全由 verifier 決定verifier 失敗必須記為 failed僅建立 10 分鐘 failure backoff不得建立 success cooldown。policy 變更必須提升 `policyVersion`,由 deployer migration 更新 99 live config避免舊 command 或 marker 繼續造成 false green。
本輪 live receipt`agent99-load-shed-marker-correction.json` 已封存 188 舊 false-success marker`agent99-stale-ssh-cleanup-v2.json` 已受控終止 7 個逾時 orphan SSH client`agent99-Perf-20260710-224454.json` 顯示 110、112、120、121、188 全部 readback OKroute 分別為 direct、direct、via110、via110、via110。110 load/core `0.60`、disk `84%`188 load/core `0.18`、disk 由 `90%` 降至 `88%`。這證明 transport、效能讀回與 verifier 修復 lane 已恢復,不等於已完成下一次實際全主機 cold-start 的 10 分鐘 SLA 驗收。
2026-07-11 production-principal concurrency receipt同時觸發 Heartbeat 與 Performance Guard 後,`agent99-Status-20260711-004148.json` 顯示五台 `ping=true`、AWOOOI `coreHealthy=true` / `redisUp=true`、Harbor `registryHealthy=true` / `deployReady=true``agent99-Perf-20260711-004149.json` 顯示五台 `severity=ok`110 / 112 / 120 / 121 / 188 load/core 為 `0.54 / 0.01 / 0.06 / 0.01 / 0.11`disk 為 `84 / 53 / 49 / 27 / 89%`。同輪 public routes 全部 `200`。這是排程身份與並行 readback closure仍不得取代下一次真實全主機重啟的 10 分鐘 SLA drill。
### 2026-07-10 P0 備份/Gitea 自動驗證覆蓋
每次 host reboot 後,備份 lane 必須依序執行 `/backup/scripts/backup-status.sh --no-notify``/backup/scripts/verify-gitea-backup.sh --no-notify`,再由 110 的每日排程執行 `/backup/scripts/gitea-bundle-backup-sync-188.sh`。最後讀回 110/188 `backup_health.prom`、Gitea restore status、bundle sync status、188 sample restore metric 與 Prometheus Backup/Gitea alerts不得只看檔案 mtime、HTTP 200 或命令 exit code。
本輪 live 驗證已證明110 `13/13`、188 `4/4`、core blocker `0`Gitea full dump 為 `6928031514` bytesCRC、DB/config、15/15 regular repo inventory digest 一致12/12 product private bundles 已從 110 local container 產生並完整同步 188remote SHA-256 `12/12`、sample clone/fsck 成功Velero missed weekly dry-run 已由 `startingDeadlineSeconds=604800` 自動 backfill 成功。20:17 drift guard 自動修復 read-only bind-mount inode drift 後source/canonical/active/runtime rule SHA 一致145 rules 已載入Backup/Gitea 非 inactive alerts 為空20:20 正式 cron 回 `repaired=0` 且 container ID/StartedAt 未變,穩態無重複 recreate。
Prometheus rules 部署不得用會置換 bind source inode 的 `install` / atomic rename 直接覆蓋 `/home/wooo/monitoring/alerts.yml`。正式 guard 會先 promtool canonical再以保留 host path 的方式更新 active若 container runtime SHA 仍不同,才用 `/home/wooo/monitoring/docker-compose.yml` 單獨 `--no-deps --force-recreate prometheus` 重新掛載TSDB volume 保留。post-verifier 必須同時通過 `145` rules health、runtime SHA、required rules 與 active alert query失敗則還原舊 active file 並重建 Prometheus不得只看 reload HTTP 200。
失敗處置固定為 fail closedfull dump 或 bundle incomplete 不得更新 complete symlinkremote sync 不使用 deleterestore drill 只能在隔離暫存目錄;不得讀 raw backup contents、secret 或 private repo 名稱,不得 restore over production、prune 或刪遠端備份。Telegram 只在狀態改變或失敗時送一張繁中摘要卡,成功心跳維持每日一次。
### 2026-07-09 P0 AI Log Triage / Controlled Auto-Recovery 覆蓋
主機重啟後不得再靠人工逐台看 log 才判斷異常。固定第一步改為執行 `scripts/reboot-recovery/ai-log-triage-auto-recovery.sh --apply`;若只允許讀取,則用 `--check`,但 scorecard / 告警仍必須吃同一份 artifact。此 runner 會 bounded 收集 110 Docker / containerd / Harbor / Nginx、120 / 121 K3s events、188 edge Nginx / Docker / systemd、99 VMware no-secret readback 與 public route HTTP status然後輸出 `findings.tsv``actions.tsv``summary.txt`
本輪 2026-07-09 16:23 orchestrator check evidence `/tmp/awoooi-full-host-reboot-orchestrator-codex/20260709T082305Z` 顯示 `RECOVERY_BLOCKED_P0``FINDING_COUNT=3``FINDING_P0_COUNT=2`P0 依序為 `STOCK_UPSTREAM_NOT_READY host=110_188``EDGE_502_UPSTREAM_REFUSED host=188_edge`,另有 `HOST188_POSTGRES_SYSTEMD_FAILED` P2 hygiene。public route readback 為 AWOOOI health `200`、AWOOOI web `307`、Stock freshness `200`、Gitea `200`、registry `/v2/` `401`、Harbor `200`;因此不得沿用稍早 Harbor 5000 refused / K3s ImagePullBackOff 舊 artifact 當作目前 truth。若後續 registry 再度不 readyK3s pod delete 只能被自動化標為 `skipped_registry_not_ready`,不得宣稱 502 已修復。
`awoooi-reboot-auto-recovery-slo.service` 必須設定 `AI_LOG_TRIAGE_AUTO_RECOVERY_MODE=apply`,並由 `reboot-auto-recovery-slo-exporter.sh` 在 host probe / readiness summary 前先跑 AI log triage。允許的 controlled apply 僅限110 Docker inactive 時 start、Harbor watchdog `--repair-once` / harbor-log-first recovery、registry ready 後只刪 `ImagePullBackOff` / `ErrImagePull` pods 讓 Kubernetes 重建、188 nginx/docker inactive 時 start。仍禁止 reboot、node drain、firewall cutover、DB restore/drop/truncate、Docker prune、secret/session/.env/raw-volume reads。
### 2026-07-03 18:19 P0-4 VMX Backup / Source Search 覆蓋
最新 via-110 no-secret locator 已成功收斂為 `candidate_source_count=0``source_locator_status=blocked_missing_expected_vmx_source_no_candidate_found`,早先 `candidate_source_count=6` / generic Ubuntu candidate 僅保留為歷史 receipt不再作為目前 relink 候選。`docs/operations/windows99-vmx-source-locator-receipt.snapshot.json` 已更新為 17:57 no-candidate truth。
新增 `/api/v1/agents/windows99-vmx-source-backup-search-package``scripts/reboot-recovery/collect-windows99-vmx-source-backup-search.sh`。此 package / collector 固定只做 filename / path-fingerprint 搜尋,不讀 VMX 內容、不輸出 raw path、不讀 secret、不寫遠端、不改 Windows、不啟停 VM、不重啟任何服務或主機。18:19 receipt120 / 121 / local 均未找到 verified 111 source99 alias 無 Linux backup host mapping110 / 188 / 112 SSH BatchMode 不可用;`candidate_source_count=0``blocked_host_count=4``apply_allowed_by_this_package=false`
P0-4 下一步固定為離線備份 inventory 或授權 console source path取得 verified source path 後才可建立 scoped relink dry-run + rollback + post-verifier不得猜 generic Ubuntu candidate、不得直接 relink、不得 restore、不得 power on VM、不得讀 Windows 密碼或 VMX 內容。
### 2026-07-03 17:34 P0-4 Verify Timeout 覆蓋
110→99 SSH public-key channel 可用via-110 locator 已成功收集且未讀 secret / 未寫 Windows / 未改 VM power`expected_vmx_path_present_count=0``discovery_root_present_count=2``candidate_source_count=0``source_locator_status=blocked_missing_expected_vmx_source_no_candidate_found`。同輪 full VMware Verify 在 90 秒曾 timeout但 180 秒成功讀回 `MISSING_VMX_ALIASES=111``VMWARE_AUTOSTART_CONFIG_READY=0``VMWARE_AUTOSTART_VERIFY_READY=0`
SOP v5.19 起Windows99 remote verify 預設 timeout 使用 `180` 秒;仍屬 bounded no-secret verifier超過 180 秒 fail-closed。不得把 90 秒 timeout 當成 SSH channel 故障,不得因此偏離到 VM power / Windows service restart / host rebootP0-4 下一步仍是取得 111 既有 VMX source / verified backup path。
### 2026-07-03 17:18 P0-4 VMX Repair CLI 覆蓋
Gitea CD `#4617` 已完成 source `40713b2f8` 投產deploy marker `b85d574`production deploy readback matched=truejob succeeded。production reboot SLO scorecard 已讀回 Windows99 default target `111 / 188 / 120 / 121 / 112`,其中 `missing_vmx_aliases=["111"]``powered_off_aliases=["112","120","121","188","111"]`,舊 `110` 不得再當 Windows99 guest alias。
`build-windows99-vmx-source-repair-package.py` 是 no-secret / no-DB / check-mode 工具,必須能在工作站缺 `DATABASE_URL` 時輸出 `windows99-vmx-source-repair-111` package這只表示 repair check-mode package ready不表示 111 VMX 已恢復。via-110 locator 若回 `blocked_ssh_publickey_auth_missing``ssh_auth_probe_exit_status=124`,固定視為 management-channel blocker。下一步是取得 111 既有 VMX source / verified backup / 授權 console candidate identity建立 scoped relink dry-run、rollback 與 post-verifier不得猜 generic Ubuntu candidate、不得直接 relink、不得 restore、不得 power on VM、不得讀 Windows 密碼。
### 2026-07-03 15:57 P0-4 Windows99 Task Run-State 覆蓋
Windows99 no-secret via-110 collector 已讀回 `AWOOOI-Start-VMware-VMs` scheduled task`present=1``enabled=1``state=Ready``trigger_count=1``action_count=1`VMware service 與 Windows Update no-auto-reboot policy 也為 ready但新增 task detail 顯示 `last_run_time=1999-11-30T00:00:00``last_task_result=267011`,等價於 `0x41303`,表示 task 尚未跑過 / 未在本輪 boot 觸發。2026-07-03 16:33 進一步確認舊 verifier default 把 `110` 誤列為 Windows99 guest最新 source-of-truth 改為 `111 / 188 / 120 / 121 / 112`,以對齊本輪 host probe `111 unreachable` 與 99 VMware guest 需求。因此目前 P0-4 真 blocker 是 `windows99_vmware_guest_power_not_ready``windows99_vmware_autostart_task_never_ran`,且 deployment 後必須用新版 collector 重新讀回 111。
SOP v5.17 起,`windows99-vmware-autostart.ps1 -Mode Verify` 必須輸出 `VMWARE_AUTOSTART_TASK_DETAIL``VMWARE_AUTOSTART_TASK_ACTION`110 textfile exporter 必須輸出 `awoooi_windows99_vmware_autostart_task_start_script_present``awoooi_windows99_vmware_autostart_task_last_result``awoooi_windows99_vmware_autostart_task_never_ran`production scorecard / API 必須把 `windows99_vmware_autostart_task_never_ran` 放進 active blockers 與 action matrix。固定下一步是執行已建立的 Windows99 VMware autostart check-mode package然後 rerun no-secret collector 與 reboot SLO scorecard不得直接 power on VM、重啟 Windows host / VMware service、修改 scheduled task、修改 registry、讀 Windows 密碼或讀 secret。
### 2026-07-03 15:35 P0-2 API Readback 覆蓋
110 textfile / Prometheus 已有 `awoooi_public_maintenance_edge_fallback_ready{scope="public_routes"} 0` 與 active blocker `edge_public_maintenance_live_config_drift`production API 必須把此 blocker 合併進 `active_blockers`。route probe 200 / route fallback ready 不能替代 edge Nginx fallback ready只有 `edge_fallback_runtime_readback_present=true``edge_fallback_ready=true` 才能清掉 edge drift。
本輪 source fix 將 public maintenance blockers 拆成 route-level 與 edge-level 兩組route readback present / route 0 raw 5xx 只清 route blockersedge blocker 必須由 edge readback green 才能清。這是 readback overlay 修正不授權讀密碼、Nginx reload、app restart、Docker / host restart、VM power change 或 DB / firewall 操作。
### 2026-07-03 15:13 P0-2 Runtime 覆蓋
110 SLO controlled install 已完成,`awoooi-reboot-auto-recovery-slo.timer``enabled/active`service `Result=success``ExecMainStatus=0`。本輪只 stage SLO verifier source、啟動 verifier oneshot 與寫 textfile metric未重啟 Docker / Nginx / K3s / DB / firewall未讀 secret / token / `.env` / raw sessions / SQLite / auth。
installer v5.15 規定 payload 必須包含 `ops/nginx`。若缺少 `ops/nginx/awoooi.wooo.work.conf`110 runtime edge check 會在本地 source gate 先失敗成 `local_nginx_conf_missing`,導致無法讀到 188 live edge truth這是 verifier packaging bug不是 Nginx runtime 狀態。完成條件是 110 artifact 內 `public-maintenance-edge-fallback.txt` 進入遠端 readback且 blocker 必須是 live edge drift。
2026-07-03 15:13 live artifact `/home/wooo/reboot-recovery/reboot-auto-recovery-slo-20260703-151219` 已讀到 188 edge`REMOTE_CONF_READABLE=1``LIVE_CONF_HAS_INTERCEPT=0``LIVE_CONF_HAS_ERROR_PAGE_503=0``LIVE_CONF_HAS_FALLBACK_HEADER=0``LIVE_CONF_HAS_INTERNAL_LOCATION=0``LIVE_CONF_HAS_API_VIP=1``LIVE_CONF_HAS_WEB_VIP=1``MAINTENANCE_FILE_PRESENT=0``SUDO_N_READY=0``EDGE_FALLBACK_READY=0``BLOCKER edge_public_maintenance_live_config_drift`。110 textfile 必須輸出 `awoooi_public_maintenance_edge_fallback_ready{scope="public_routes"} 0` 與 active blocker `edge_public_maintenance_live_config_drift`;目前 blocker count 為 `8`
此狀態仍不授權 `--apply`。P0-2 完成需要 passwordless sudo / privileged edge apply channel、live conf backup、maintenance file install、`nginx -t`、reload 與 public route header verifier 全綠,或完成外部 L1 cloud/CDN fallback decision record。`SUDO_N_READY=0` 時不得讀密碼、不得 reload Nginx、不得重啟 app / Docker / host。
### 2026-07-03 14:40 P0-2 Public Maintenance 現況覆蓋
最新 main `3ebeeeb4b` 的 Gitea CD `#4592` 已推 deploy marker `ee0da93`production deploy readback matched、job succeededdeploy window 期間 AWOOOI API health 與 SLO route 曾回 raw `502 Bad Gateway`,沒有 `X-AWOOOI-Fallback``Retry-After` 或維護頁 body。後續 public route probe 自癒回 `200`,但 raw 502 exposure 仍必須列為 P0 public UX blocker。
live 188 edge check 的目前事實是:`LIVE_CONF_HAS_INTERCEPT=0``LIVE_CONF_HAS_ERROR_PAGE_503=0``LIVE_CONF_HAS_FALLBACK_HEADER=0``LIVE_CONF_HAS_INTERNAL_LOCATION=0``MAINTENANCE_FILE_PRESENT=0``SUDO_N_READY=0``EDGE_FALLBACK_READY=0`blocker 固定為 `edge_public_maintenance_live_config_drift`。此 blocker 不授權 app restart、Docker restart、host reboot 或讀密碼;只能先 `public-maintenance-edge-fallback-apply.sh --check`,具備 passwordless sudo 後才可進受控 `--apply` + backup + `nginx -t` + reload + public header verifier或走 L1 external static/CDN fallback decision record。
v5.14 起110 SLO exporter 必須把 edge check 合併到 public maintenance readback輸出 `awoooi_public_maintenance_edge_fallback_ready{scope="public_routes"}`production scorecard / API 必須在 routes 當下 200 但 edge fallback 未套用時,仍顯示 `public_maintenance_fallback.ready=false` 與 active blocker `edge_public_maintenance_live_config_drift`。Alertmanager 規則 `PublicMaintenanceEdgeFallbackConfigDrift` 必須對此 metric 告警,避免下一次 deploy / reboot raw 502 才由使用者發現。
### 2026-07-03 14:26 P0 現況覆蓋
Gitea CD `#4589` 已完成 source `33c391808` 部署deploy marker `9f49651 chore(cd): deploy 33c3918 [skip ci]` 已 fast-forward 回 `gitea-ssh/main`public queue readback 顯示 `current_main_cd_run_status=Success``current_main_cd_deploy_evidence_succeeded_from_log=true``latest_visible_cd_production_deploy_readback_matched_from_log=true``latest_visible_cd_job_succeeded_from_log=true``no_matching_online_runner_visible=false`,且沒有 host pressure refusal。110 `awoooi-reboot-auto-recovery-slo.service` 最新 run `20260703-142122``Result=success``ExecMainStatus=0`textfile metric 顯示 `awoooi_reboot_auto_recovery_slo_blocker_count=7``awoooi_windows99_vmware_autostart_config_ready=1``awoooi_windows99_vmware_windows_update_no_auto_reboot_ready=1``awoooi_windows99_vmware_missing_vmx_alias` 無 alias gaugepowered-off aliases 為 `110 / 112 / 120 / 121 / 188`
Production `/api/v1/agents/reboot-auto-recovery-slo-scorecard` 已與 runtime metric 對齊:`status=blocked_reboot_auto_recovery_slo_not_ready``active_blocker_count=7``runtime_metric_active_blocker_count=7``runtime_metric_runtime_readback_added_blocker_count=0``readiness_percent=67``primary_blocker=reboot_event_required_host_unreachable``can_claim_all_services_recovered_within_target=false``windows99_vmware_autostart_config_not_ready``windows99_update_no_auto_reboot_policy_not_ready` 已不得再列為目前 blocker若未來再次出現優先判定為 metric/API overlay regression不得轉成 Windows policy apply、service restart、VM power change 或 host reboot。
目前仍不得宣稱「全主機重啟後 10 分鐘內全部恢復」。剩餘 7 個 active blockers 固定為:
- `all_required_hosts_not_in_10_minute_reboot_window`
- `fresh_all_host_reboot_event_missing`
- `host_boot_observation_older_than_target_window`
- `host_unreachable_after_reboot`
- `host_uptime_unknown`
- `reboot_event_required_host_unreachable`
- `windows99_vmware_guest_power_not_ready`
固定下一步:維持 110 timer / textfile / production scorecard readback優先補 fresh all-host reboot event / 10-minute window evidence / host reachability以及 Windows99 powered-off guest 的 check-mode package 與 post-verifier禁止讀 Windows 密碼、啟動或關閉 VM、host reboot、service restart、Docker / Nginx / K3s / DB / firewall restart、restore、prune、delete。
### 2026-07-03 14:10 P0 現況覆蓋
目前仍不得宣稱「全主機重啟後 10 分鐘內全部恢復」。Gitea CD `#4587` 已完成 source `30c89a0` 部署並推 deploy marker `4da6dda`110 上 `awoooi-reboot-auto-recovery-slo.timer``enabled/active`,最新 textfile runtime metric 為 `active_blocker_count=7`primary blocker `reboot_event_required_host_unreachable``can_claim_all_services_recovered_within_target=false`。7 個 runtime active blockers 固定為:
- `all_required_hosts_not_in_10_minute_reboot_window`
- `fresh_all_host_reboot_event_missing`
- `host_boot_observation_older_than_target_window`
- `host_unreachable_after_reboot`
- `host_uptime_unknown`
- `reboot_event_required_host_unreachable`
- `windows99_vmware_guest_power_not_ready`
最新 no-write artifact 以 110 runtime textfile、latest SLO artifact、production API readback 與本輪 Windows99 collector readback 為來源:
- 99 no-secret VMware verify stdout 已能透過 110 / public-key SSH 收集latest artifact 顯示 `missing_vmx_aliases=[]``VMWARE_AUTOSTART_CONFIG_READY=1``WINDOWS_UPDATE_NO_AUTO_REBOOT_READY=1``VMWARE_AUTOSTART_VERIFY_READY=0`
- 111 / 188 / 120 / 121 / 112 才是 Windows99 VMware default guest set110 是 P0 host / control-plane truth不得再被 validator 或 scorecard fallback 當成 99 VMware guest default。
- 111 / 188 / 120 / 121 / 112 VM power readback 仍需用新版 no-secret collector 確認;`windows99_vmware_guest_power_not_ready` 仍 blocked且此 SOP 不授權直接 power change。
- 111 host 仍 unreachable。
- 112 / 120 / 121 可達,但 uptime 已超過 10 分鐘目標窗口。
- 188 可達,但 `systemd_state=degraded``awoooi-startup.service failed`
- `reboot_event_required_host_unreachable` 仍為 primary blocker因此 10 分鐘 SLO 尚未證明。
固定下一步:保留 110 timer / textfile每輪讀最新 `scorecard.json` 與 Prometheus metricWindows99 lane 只建立 powered-off aliases 的 check-mode / post-verifier package不能從 scorecard 直接 power on。API overlay 必須以 runtime metric 的 `awoooi_windows99_vmware_autostart_config_ready``awoooi_windows99_vmware_windows_update_no_auto_reboot_ready` 為準;當這些 metric 為 ready 時,不得用 committed snapshot 的舊 `service_blockers` / `policy_blockers` 反加 `windows99_vmware_autostart_config_not_ready``windows99_update_no_auto_reboot_policy_not_ready`。同時繼續補 fresh all-host reboot event / approved drill proof禁止讀 Windows 密碼、啟動 / 關閉 VM、host reboot、service restart、Docker / Nginx / K3s / DB / firewall restart、restore、prune、delete。
2026-07-03 13:10 最新 overlayGitea CD `#4582` 已推 deploy marker `558b9e9`source `ba0849f70`production deploy readback matched=true 且 job_succeeded=trueGitea UI 若仍顯示 Running只能視為 run-level lag。production `/api/v1/agents/windows99-vmx-source-locator-readback` 已上卷 redacted locator receipt`candidate_source_count=6``alias_hint_candidate_count=0``unassigned_ubuntu_candidate_count=5``identity_unassigned_ubuntu_candidate_count=1``candidate_confirmation_required=true`,狀態為 `collector_readback_single_identity_candidate_confirmation_required`。新的 `candidate_confirmation_gate` 固定輸出 `candidate_identity_evidence_sufficient=false``candidate_relink_dry_run_ready=false``apply_allowed_by_candidate_confirmation_gate=false`;缺少 evidence 為 `target_alias_identity_hint``verified_backup_or_authorized_console_candidate_identity``pre_apply_backup_plan``scoped_relink_dry_run`。因此下一步已從「重跑 locator」收斂為取得授權 console candidate identity evidence 或 verified backup path重跑 confirmation gate再產生 scoped relink dry-run + rollback + post-verifier仍不得猜測 generic Ubuntu candidate、不得 relink、不得 restore、不得 power on VM。
2026-07-03 16:33 source-of-truth correction本輪全主機 reboot 需求與 host probe 均指向 `111` 是 Windows99 guest target舊的 `110 -> 188 -> 120 -> 121 -> 112` default 會讓 111 unreachable 被錯誤排除。因此 repo source、API scorecard fallback、CLI scorecard fallback、validator default 必須使用 `111 / 188 / 120 / 121 / 112` 作為 Windows99 default VM target`110` 仍是 reboot SLO required host 與 control-plane host但不是 99 VMware 預設 VM。禁止用舊 110 default 清掉 111 blocker禁止為了清 blocker 修改 Windows task、relink、restore、啟動 VM 或重啟 host。
2026-07-03 13:50 no-secret verifier overlay 曾以舊 default `110 / 188 / 120 / 121 / 112` 讀回 artifact2026-07-03 16:33 已判定該 default 與目前事故目標不一致。下一步必須部署新版 SLO exporter/collector 到 110、重跑 verifier service確認 validator default、Prometheus textfile、production scorecard 全部改讀 `111 / 188 / 120 / 121 / 112`。仍不得啟動 VM、relink、restore、重啟 host 或讀密碼。
2026-07-03 16:57 bounded verifier overlay新版 verifier 預設不再遞迴掃 VM roots避免 111 VMX 缺失時 timeout實測 via-110 no-secret collector 可回 `VMX alias=111 present=0``VM_POWER alias=111 vmx_present=0 running=0``VMWARE_AUTOSTART_CONFIG_READY=0`。SLO exporter fallback 只可使用 alias set 完全符合 `111 / 188 / 120 / 121 / 112` 的 artifact`110` artifact 必須被跳過,不得回灌成 runtime truth。
2026-07-03 14:10 runtime metric overlay110 最新 SLO textfile 已輸出 `active_blocker_count=7`,且 `awoooi_windows99_vmware_missing_vmx_alias` 沒有 alias gauge、`awoooi_windows99_vmware_powered_off_alias` 只列 `110 / 112 / 120 / 121 / 188`。production API 在 source 修正前仍可能因 stale committed snapshot 反加 Windows99 config / Windows Update policy 假 blocker新版 API 必須讀 `awoooi_windows99_vmware_autostart_config_ready` gauge並以 runtime metric overlay 清掉這兩個假 blocker。若 production `/api/v1/agents/reboot-auto-recovery-slo-scorecard` 仍顯示 `runtime_metric_active_blocker_count=7``active_blocker_count>7` 且多出的 blocker 是 Windows99 config / policy先部署 / 驗證 API overlay source不得回頭做 VMX locate、relink、power change 或 Windows policy apply。
### 五主機全貌
```
192.168.0.188 (OLLAMA / 主服務主機)
├── containerd ← 基礎,必須最先啟動
├── Docker ← 依賴 containerd
├── PostgreSQL :5432 ← K3s kine 後端API DB
├── Redis :6380 ← Working Memory (重啟後清空,需 warm-up)
├── Ollama :11434 ← LLM 推論引擎
├── Nginx :80/:443 ← 反向代理
├── OpenClaw (clawbot) :8088 ← AI 核心 (Docker Compose)
├── MinIO ← Velero 備份存儲 (Docker Compose)
└── SignOz ← 可觀測性 (Docker Compose)
192.168.0.110 (DevOps 主機)
├── Docker
├── Harbor :5000 ← Container Registry (K3s 拉映像用)
├── Gitea :3001 ← 主要 Git / CI 管理介面
├── Gitea Act Runner ← CD pipeline 執行者 (docker: gitea-runner)
├── Langfuse :3100 ← LLMOps 追蹤
├── Prometheus :9090 ← 指標收集
├── Alertmanager :9093 ← 告警路由
├── Grafana :3002 ← 監控儀表板
├── Sentry :9000 ← Error Tracking (2026-03-242026-04-05 加入 startup)
└── SignOz ← 可觀測性
192.168.0.120 (K3s Master - mon)
├── K3s server (control plane)
├── kube-proxy ← NodePort 轉發 32334/32335
└── keepalived ← VIP 192.168.0.125 (secondary)
192.168.0.121 (K3s Worker - mon1)
├── K3s agent (worker)
└── kube-proxy
192.168.0.125 (VIP, keepalived 管理)
├── → :32334 AWOOOI API
└── → :32335 AWOOOI Web
```
### 服務依賴關係
```
嚴格啟動順序:
【188 層】
containerd
└── Docker
├── PostgreSQL (kine DB, API DB)
│ └── Redis (Working Memory)
│ └── Ollama (LLM)
│ └── Nginx
│ ├── SignOz
│ ├── MinIO
│ └── OpenClaw (依賴 aiops-network)
【110 層】
Docker
└── harbor-log (等 healthy)
└── Harbor 全組件 (:5000)
└── Gitea (:3001)
├── Langfuse
├── Monitoring (Prometheus + Alertmanager + Grafana)
│ [Alertmanager → AWOOOI API 告警鏈路]
├── SignOz
└── Gitea Act Runner (等 Gitea 就緒後才啟動)
【120/121 層】
K3s (依賴 PostgreSQL@188)
└── kube-proxy (NodePort 規則)
└── Pods: API / Web / Worker
└── keepalived (VIP)
【告警鏈路】
Prometheus → Alertmanager(110)
→ AWOOOI API(VIP 192.168.0.125:32334/api/v1/webhooks/alertmanager) ← 直接,不走 OpenClaw
→ TelegramGateway → Telegram
【CD 鏈路】
Git push → Gitea(110:3001)
→ Act Runner (gitea-runner container)
→ Build Docker image → Harbor(:5000) → kubectl → K3s pods
```
### 110 Controlled CD Lane 與 Fail-Closed Enforcer
110 runner 壓力事故後,`awoooi-cd-lane.service`、legacy runner、generic label 與重型 runner 仍必須維持 fail-closed。唯一可被保留的 110 CD entrypoint 是專用 `awoooi-cd-lane-drain.service` 的非 secret staging 狀態,且必須同時符合下列條件:
- `config.yaml` 只允許 `capacity <= 1``awoooi-host:host``awoooi-ubuntu:docker://192.168.0.110:5000/awoooi/ci-runner:act-22.04`
- `awoooi_cd_lane_controlled` 必須是 executable ELF不可為 fail-closed shell stub。
- unit 必須含 `ConditionPathExists=/home/wooo/awoooi-cd-lane-drain/data/.runner`、CPU / Memory / Tasks accounting 與 limit、`NoNewPrivileges=true`
- service staging 階段必須 inactive、`MainPID=0`、未 enabled / 未 maskedrunner token registration 與 `.runner` 內容仍不可讀、不可列印。
讀回方式固定先跑 `scripts/reboot-recovery/enforce-110-runner-failclosed.sh --check`,看 `CONTROLLED_DRAIN_STAGING_ALLOWED`;再跑 `ops/runner/check-awoooi-110-controlled-cd-lane-readiness.sh`,看 `CONFIG_READY``BINARY_READY``REGISTRATION_READY``SERVICE_READY`。若只剩 registration / service inactive blocker代表非 secret guardrail 已收斂,下一步才是 token-safe registration path不得用手工反覆補 config / binary 取代 source enforcer 修正。
### 關鍵依賴說明
| 服務 | 關鍵依賴 | 若依賴失敗 |
|------|---------|-----------|
| K3s | PostgreSQL@188 ready | K3s 無法啟動,所有 pod 無法調度 |
| OpenClaw | Docker + aiops-network | Telegram AI 對話失效 |
| Alertmanager → API | NetworkPolicy 允許 110/32 | 告警全面沉默 |
| CD pipeline | Gitea Act Runner online | 所有部署失效 |
| Harbor | harbor-log healthy | 所有其他 Harbor 容器 Exited 128 |
| K8s pods | Harbor (映像倉庫) | ImagePullBackOff |
---
## 自動化腳本狀態
### 已部署 (2026-04-05)
| 主機 | 腳本 | 部署位置 | systemd service | 狀態 | 類型 |
|------|------|---------|----------------|------|------|
| **188** | `awoooi-startup.sh` | `/usr/local/bin/` | `awoooi-startup.service` | ✅ enabled | 重開機 oneshot |
| **110** | `awoooi-startup-110.sh` | `/usr/local/bin/` | `awoooi-startup-110.service` | ✅ enabled | 重開機 oneshot |
| **110** | `harbor-watchdog.sh` | `/usr/local/bin/` | `harbor-watchdog.service` | ✅ active | **常駐 watchdog** |
| **120** | K3s 原生 | 系統內建 | `k3s.service` | ✅ enabled | 系統服務 |
| **121** | K3s 原生 | 系統內建 | `k3s-agent.service` | ✅ enabled | 系統服務 |
**本地原始碼**: `scripts/reboot-recovery/`
### harbor-watchdog2026-04-05 新增)
**問題背景**: Harbor 的 Exited (128) Race Condition
- `restart: always` 在 harbor-log 未就緒時不斷重試,最終進入 backoff 放棄
- 即使 harbor-log 後來 healthy其他容器不會自動重試
- 只靠 startup 腳本無法處理**運行中崩潰**的情況
**設計**:
- `Type=simple` 常駐進程(不是 oneshotsystemd 永久監控
- 每 60 秒輪詢 `http://127.0.0.1:5000/v2/`401 = healthy
- 偵測到不健康 → 等 5 秒再確認(避免誤報)→ 執行完整時序修復
- 修復邏輯:清除 Exited 容器 → 只啟動 harbor-log → 等 healthy → 啟動全部
**查看狀態**:
```bash
journalctl -u harbor-watchdog.service -f # 即時 log
systemctl status harbor-watchdog.service # 服務狀態
cat /var/log/harbor-watchdog.log # 持久化 log
```
### 各腳本覆蓋清單
**188 (7 步驟)**:
- containerd BoltDB 損壞偵測與修復
- Docker BoltDB 損壞偵測與修復
- PostgreSQL 啟動偵測checkpoint / WAL 類資料層錯誤一律 fail-closed禁止自動 `pg_resetwal`
- Redis 啟動
- Ollama 啟動
- Nginx 啟動
- SignOz + MinIO + aiops-network + OpenClaw
**110 (7 步驟 + harbor-watchdog 常駐)**:
- Docker BoltDB 損壞偵測與修復
- 孤兒容器清除 (Exited 128)
- Harborharbor-log first 策略Phase 1 清除 → Phase 2 只啟 harbor-log → Phase 3 等 healthy → Phase 4 啟全部)
- Gitea + Langfuse + Monitoring (含 Alertmanager 健康驗證)
- SignOz
- **Gitea Act Runner** (自動清除過期 .runner 配置)
- **Sentry** (/opt/sentry含 PostgreSQL WAL + Redis RDB 損壞自動修復)
- **harbor-watchdog.service** (常駐,每 60s 自動修復)
---
## 正常重啟流程
> 適用於計劃性維護、安全更新、OS 升級等**有預期**的重啟。
### 重啟前準備 (T-30 分鐘)
```bash
# 1. 確認沒有 INVESTIGATING/MITIGATING 的 incident
curl -s http://192.168.0.121:32334/api/v1/incidents | python3 -c 'import sys,json; d=json.load(sys.stdin); print("Active incidents:", d.get("count",0))'
# 2. 確認 K3s 健康
ssh wooo@192.168.0.120 "kubectl get nodes && kubectl get pods -n awoooi-prod"
# 3. 備份確認 (MinIO Velero)
ssh wooo@192.168.0.120 "kubectl get backup -n velero 2>/dev/null | head -5"
```
### 建議重啟順序
```
第一步: 188 + 110 同時重啟
(兩者獨立,可並行)
等待 5 分鐘確認基礎服務就緒:
- PostgreSQL accepting connections
- Harbor returning HTTP 200
- Gitea accessible
第二步: 120 + 121 同時重啟
(K3s cluster)
等待 10 分鐘:
- Nodes Ready
- Pods Running
```
> **重要**: 120/121 必須在 188 PostgreSQL 完全就緒後才重啟,
> 否則 K3s kine 連不到 DB 無法啟動。
### 重啟後驗證 (T+15 分鐘)
執行底部 E2E 驗證腳本。所有項目必須 ✅。
---
## 異常重啟流程
> 適用於電源中斷、OOM、系統崩潰、強制重啟等**非預期**的重啟。
### 第一步:快速診斷 (T+2 分鐘)
```bash
# 188 狀態
ssh ollama@192.168.0.188 "
systemctl is-active containerd docker postgresql@14-main redis-server ollama nginx
docker ps --format '{{.Names}}: {{.Status}}' | grep -E 'clawbot|minio|signoz'
"
# 110 狀態
ssh wooo@192.168.0.110 "
docker ps --format '{{.Names}}: {{.Status}}' | grep -E 'harbor-log|gitea$|alertmanager|gitea-runner'
"
# K3s 狀態
ssh wooo@192.168.0.120 "
kubectl get nodes 2>/dev/null || echo 'K3s not ready'
kubectl get pods -n awoooi-prod 2>/dev/null | grep -v Running | head -10
"
# API 健康
curl -s --max-time 5 http://192.168.0.121:32334/api/v1/health 2>/dev/null | python3 -c 'import sys,json; d=json.load(sys.stdin); print("API:", d["status"])' 2>/dev/null || echo 'API unreachable'
```
### 第二步:按優先級恢復
**P0 (T+0~5 分鐘): 基礎設施**
```bash
# 自動化腳本通常已處理,但若沒有自動啟動:
ssh ollama@192.168.0.188 "sudo /usr/local/bin/awoooi-startup.sh"
ssh wooo@192.168.0.110 "echo '0936223270' | sudo -S /usr/local/bin/awoooi-startup-110.sh"
```
**P1 (T+5~15 分鐘): K3s 叢集**
```bash
# 等 PostgreSQL@188 就緒後
ssh ollama@192.168.0.188 "pg_isready -h localhost -p 5432"
# K3s 若未自動恢復
ssh wooo@192.168.0.120 "sudo systemctl restart k3s"
ssh wooo@192.168.0.121 "sudo systemctl restart k3s-agent"
# 等待 Nodes Ready
ssh wooo@192.168.0.120 "kubectl wait --for=condition=Ready nodes --all --timeout=120s"
```
**P2 (T+15~30 分鐘): 業務驗證**
```bash
# Pods 全部 Running?
ssh wooo@192.168.0.120 "kubectl get pods -n awoooi-prod"
# API 健康 (含所有 components)?
curl -s http://192.168.0.121:32334/api/v1/health | python3 -m json.tool
# 告警鏈路 E2E 測試
curl -X POST http://192.168.0.121:32334/api/v1/webhooks/alertmanager \
-H 'Content-Type: application/json' \
-d '{"receiver":"test","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"RebootRecoveryTest","severity":"info"},"annotations":{"summary":"重開機恢復測試,請忽略"},"startsAt":"2026-04-05T00:00:00Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":""}],"groupLabels":{},"commonLabels":{},"commonAnnotations":{},"externalURL":"","version":"4","groupKey":"test"}'
# 預期: {"success":true,...} 且 Telegram 收到測試告警
```
---
## 各主機詳細啟動序列
### 192.168.0.188 — 主服務主機
| 步驟 | 服務 | 驗證方式 | 常見失敗原因 |
|------|------|---------|------------|
| 1 | containerd | `systemctl is-active containerd` | BoltDB meta.db 損壞 |
| 2 | Docker | `systemctl is-active docker` | BoltDB local-kv.db 損壞 |
| 3 | PostgreSQL | `pg_isready -h localhost -p 5432` | WAL checkpoint 損壞 |
| 3a | kine VACUUM | `psql -d k3s_datastore -c "VACUUM ANALYZE kine;"` | 慢查詢導致 K3s 超時 |
| 4 | Redis | `redis-cli -p 6380 ping` | bind 配置錯誤 (必須 0.0.0.0:6380) |
| 5 | Ollama | `systemctl is-active ollama` | GPU 記憶體不足 |
| 6 | Nginx | `systemctl is-active nginx` | port 衝突 |
| 7a | aiops-network | `docker network ls \| grep aiops-network` | 重啟後 external network 消失 |
| 7b | OpenClaw | `curl http://localhost:8088/health` | pip 依賴損壞需 rebuild |
| 7c | MinIO | `docker ps \| grep minio` | 無狀態服務,重啟即可 |
| 7d | SignOz | `curl http://localhost:3301/-/healthy` | ClickHouse 起慢 |
**BoltDB 損壞修復**:
```bash
# containerd meta.db
BOLT=/var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db
cp $BOLT ${BOLT}.bak.$(date +%s) && rm $BOLT && systemctl restart containerd
# Docker network BoltDB
BOLT=/var/lib/docker/network/files/local-kv.db
cp $BOLT ${BOLT}.bak.$(date +%s) && rm $BOLT && systemctl restart docker
```
**PostgreSQL checkpoint / WAL 類錯誤處理**:
> 2026-06-26 校正:這不是一般重啟 SOP 的自動修復步驟。`pg_resetwal` 可能造成不可逆資料風險,只能在 DB owner、備份/restore evidence、maintenance window、rollback owner 與 post-check plan 都具備時,作為 break-glass 人工程序。AI、startup 腳本或值班腳本不得自動執行。
```bash
# 只讀確認,不代表可執行修復
journalctl -u postgresql@14-main -n 30 | grep "could not locate a valid checkpoint"
pg_lsclusters
systemctl status postgresql@14-main --no-pager
```
若確認需要進入 break-glass先完成
```text
1. DB owner approval
2. 最新本機與 offsite backup evidence
3. restore / rollback plan
4. maintenance window
5. post-checkpg_lsclusters、pg_isready、應用 DB smoke、backup-status、cold-start wrapper
```
### 192.168.0.110 — DevOps 主機
| 步驟 | 服務 | 驗證方式 | 常見失敗原因 |
|------|------|---------|------------|
| 1 | Docker | `systemctl is-active docker` | BoltDB 損壞 |
| 2 | 孤兒容器清除 | `docker ps -a \| grep "Exited (128)"` | 舊容器引用已不存在的 network |
| 3 | harbor-log | `docker inspect --format='{{.State.Health.Status}}' harbor-log` | syslog :1514 未就緒 |
| 3 | Harbor 全組件 | `curl http://localhost:5000/v2/` | harbor-log 還沒 healthy |
| 4 | Gitea | `curl http://localhost:3001` | 無特殊問題 |
| 5 | Langfuse | `curl http://localhost:3100` | DB 未就緒 |
| 6 | Alertmanager | `curl http://localhost:9093/-/healthy` | docker compose 未啟動 |
| 6a | 告警鏈路驗證 | 發送測試 webhook | webhook URL 錯誤 / NetworkPolicy |
| 7 | SignOz | `curl http://localhost:3301/-/healthy` | ClickHouse 起慢 |
| 8 | Gitea Runner | `docker logs gitea-runner \| grep SUCCESS` | .runner 配置過期 |
| 9 | Sentry | `curl -o /dev/null -w '%{http_code}' http://localhost:9000/` | PostgreSQL WAL/Redis RDB 損壞 (見下方) |
**110 control-path / Harbor receipt 判讀 (2026-07-02)**:
重啟後若公開 Gitea queue 仍顯示舊的 Harbor repair failure、`harbor_110_remote_ssh_publickey_auth_stalled`、remote-control unavailable 或 jobs stale不得直接把它當成 current blocker。必須先用同一輪 read-only receipt 合併以下證據:
```bash
scripts/reboot-recovery/diagnose-110-ssh-publickey-auth.sh
ssh wooo@192.168.0.110 'sudo -n /usr/local/bin/recover-110-control-path-and-harbor-local.sh --check'
python3 ops/runner/read-public-gitea-actions-queue.py --json
curl -k -s -o /dev/null -w '%{http_code}' https://registry.wooo.work/v2/
curl -s -o /dev/null -w '%{http_code}' http://192.168.0.110:5000/v2/
```
`SSH_COMMAND_PATH ... classification=command_path_ready` 成立,且 public/internal registry `/v2/``401`,則舊 Harbor repair SSH failure 只能列為 historical queue metadataactive blocker 應轉向實際的 controlled CD lane readiness、CD jobs SHA/readback mismatch 或 deploy marker。2026-07-02 的機器可讀證據是 `docs/operations/harbor-110-control-path-recovery-readback-2026-07-02.snapshot.json`Gitea latest visible CD `#4335``Success`,但 active blockers 仍包含 110 controlled CD lane config / binary / registration / service guardrail 與 Gitea CD jobs head-SHA / stale mismatch。
combined local-console output 只能依 marker 判讀:`AWOOOI_110_CONTROLLED_CD_LANE_READY` 只屬於 110 controlled lane沒有 `AWOOOI_NON110_RUNNER_READY` 時,不得從同一份 `BLOCKER` 行推導 non110 runner blocker。
**Gitea repository / bundle backup 判讀 (2026-07-02)**:
Gitea UI/API 可見性、SSH refs、bundle backup、sample restore dry-run 是四層不同證據不得混成「repo 不見」或「backup 完成」一句話。
固定順序:
```bash
curl -s https://awoooi.wooo.work/api/v1/agents/gitea-repo-bundle-backup-readback | python3 -m json.tool
curl -s http://192.168.0.188:9100/metrics | grep '^awoooi_gitea_bundle_'
scripts/backup/gitea-bundle-sample-restore-dry-run.sh --repo wooo/awoooi --write-textfile
```
判讀規則:
- `expected_repo_missing_count=0``failed_repo_count=0``checksum_missing_count=0` 代表 bundle repo / checksum 層沒有缺,不代表 freshness 或 restore drill 已完成。
- `awoooi_gitea_bundle_fresh=0` 是 freshness blocker下一步是補跑 `gitea-repo-bundle-backup.sh` 與同步到 188不得刪 repo、改 visibility、或 restore 到 production。
- `awoooi_gitea_bundle_sample_restore_dry_run_ok` absent / `0` 是 restore confidence blocker只允許在暫存目錄 clone bundle + `git fsck --connectivity-only`,不得把 bundle restore 回 Gitea production。
- public repo search 只代表 public visibilityprivate/internal repo 要看 authenticated inventory、SSH heads 或 bundle metrics不能用 404 直接推論 history 消失。
**Backup alert receipt 判讀 (2026-07-03)**:
備份監控不只看 freshness / failed / age / restore drill還必須能證明告警進入 Telegram / AwoooP receipt 鏈。重啟後若使用者問「備份到底有沒有告警」,固定順序如下:
```bash
python3 scripts/ops/backup-alert-label-contract-check.py
curl -G -s http://192.168.0.110:9090/api/v1/query --data-urlencode 'query=awoooi_backup_alert_receipt_stage_fresh{host=~"110|188"}' | python3 -m json.tool
scripts/ops/backup-alert-live-visibility-check.py --prometheus-url http://192.168.0.110:9090 --alertmanager-url http://192.168.0.110:9093 --host 110
scripts/ops/backup-alert-live-visibility-check.py --prometheus-url http://192.168.0.110:9090 --alertmanager-url http://192.168.0.110:9093 --host 188
```
判讀規則:
- `required_alert` 是原始 backup alert例如 `BackupJobStale``BackupCoverageDomainStale``GiteaPrivateBundleBackupIncomplete`;不得用 Prometheus 保留 label `alertname` 承載它。
- 每個 required alert 都要有 `sent / received / dedup / escalation` 四段 marker缺段明細由 `awoooi_backup_alert_receipt_stage_fresh == 0` 保留,`BackupAlertReceiptStageMissing` 只允許聚合成每個 `host / receipt_channel` 一條,避免 Telegram/AwoooP 噪音淹沒真問題。
- marker 只能是 `/backup/alert-receipts/*.last_success` 這類 timestamp / evidence id不得寫 Telegram token、raw payload、chat id 原文或 secret。
- 這個 receipt proof 不取代真正 backup / restore proofGitea bundle、Gitea full dump、DB/settings/issues/packages/LFS、offsite、restore drill 仍要各自綠。
**Harbor Exited 128 修復**:
```bash
# 等 harbor-log healthy
until [ "$(docker inspect --format='{{.State.Health.Status}}' harbor-log)" = "healthy" ]; do
echo "waiting..."; sleep 5
done
# 清除 Exited 128 容器
docker rm -f $(docker ps -a --filter status=exited --format "{{.Names}}" | grep -v harbor-log)
# 重新啟動
cd /home/wooo/harbor/harbor && docker compose up -d
```
**Gitea Runner 重新註冊**:
```bash
# 清除過期配置 (指向錯誤 hostname 的)
sudo rm /home/wooo/act-runner/data/.runner
# 獲取新的 registration token
curl -s -u 'wooo:TOKEN' 'http://192.168.0.110:3001/api/v1/repos/wooo/awoooi/actions/runners/registration-token'
# 更新 docker-compose.yml 的 GITEA_RUNNER_REGISTRATION_TOKEN
# 然後啟動
cd /home/wooo/act-runner && docker compose up -d
```
### 192.168.0.120/121 — K3s 叢集
| 步驟 | 項目 | 驗證方式 |
|------|------|---------|
| 前提 | PostgreSQL@188 ready | `pg_isready -h 192.168.0.188 -p 5432` |
| 1 | K3s service | `systemctl is-active k3s` (120) |
| 2 | K3s agent | `systemctl is-active k3s-agent` (121) |
| 3 | Nodes Ready | `kubectl get nodes` |
| 4 | Pods Running | `kubectl get pods -n awoooi-prod` |
| 5 | keepalived VIP | `ip addr show \| grep 192.168.0.125` (120) |
| 6 | NodePort | `nc -zv 192.168.0.121 32334` |
---
## 常見故障排查手冊
### 1. 告警沉默 (Telegram 沒收到告警)
```
診斷樹:
Alertmanager healthy? (http://192.168.0.110:9093/-/healthy)
├── NO → docker compose up -d (在 110 /home/wooo/monitoring/)
└── YES
Webhook URL 正確?
grep 'url:' /home/wooo/monitoring/alertmanager.yml
必須是: http://192.168.0.125:32334/api/v1/webhooks/alertmanager
├── NO → bash scripts/ops/deploy-alertmanager-config.sh
└── YES
從 110 curl POST webhook 成功?
curl -X POST http://192.168.0.125:32334/api/v1/webhooks/alertmanager ...
├── timeout → NetworkPolicy 未允許 110
│ kubectl apply -f k8s/awoooi-prod/02-network-policy.yaml
└── {"success":true} → 檢查 Telegram Bot Token
```
Alertmanager config drift 修復:
```bash
# 從專案根目錄執行;會從 K8s Secret 注入 Telegram bot token 與 SRE_GROUP_CHAT_ID
# 先用 amtool 驗證,再備份 110 live config、原 inode 覆寫、修正 0644 權限並 HUP reload。
bash scripts/ops/deploy-alertmanager-config.sh
```
**補充診斷 — 特定服務異常但無告警Alertmanager 正常)**:
```bash
# 確認 Prometheus 規則數量和關鍵規則是否存在
ssh wooo@192.168.0.110 "curl -s http://localhost:9090/api/v1/rules | python3 -c \"
import sys,json
r=json.load(sys.stdin)
names=[x['name'] for g in r['data']['groups'] for x in g['rules']]
total=len(names)
key=['SentryDown','HarborDown','GiteaDown','OpenClawDown','AlertmanagerDown','AlertChainUnhealthy']
for k in key:
print(f'{'OK' if k in names else 'MISSING'}: {k}')
print(f'Total rules: {total}')
\""
# 若規則不存在或 total < 25 → 規則未部署
# 修復: 從專案根目錄執行
bash scripts/ops/deploy-alerts.sh
# 若規則存在但 inactive → 確認 blackbox probe target
curl http://192.168.0.110:9090/api/v1/query?query=probe_success
```
**根本教訓 (2026-04-05)**:
- 舊架構: Alertmanager → OpenClaw 8088 (中間層,重開機後 OpenClaw 不健康就沉默)
- 新架構: Alertmanager → AWOOOI API 直通 (更穩定,單一故障點少)
- NetworkPolicy 必須允許 110/32 進入 pod
### 2. Gitea CD 無法部署
```
診斷樹:
Gitea runner 數量?
curl -u 'wooo:TOKEN' 'http://192.168.0.110:3001/api/v1/repos/wooo/awoooi/actions/runners'
├── total_count: 0 → Runner 離線
│ docker ps | grep gitea-runner
│ ├── 不存在 → cd /home/wooo/act-runner && docker compose up -d
│ └── 存在但 Restarting → docker logs gitea-runner
│ "lookup gitea" → 清除 data/.runner → docker compose up -d
└── total_count: 1 → Runner 在線但 CD 失敗
查看 Gitea Actions 日誌: http://192.168.0.110:3001/wooo/awoooi/actions
```
### 3. 網站數據顯示 0 / incidents 消失
```
原因: Redis Working Memory 重啟後清空
新版 API (f4f454f+) 啟動時自動 warm-up
舊版 API 沒有此邏輯
診斷:
curl http://192.168.0.121:32334/api/v1/incidents
→ count: 0 但 Telegram 有歷史告警?
→ 確認 API pod image 版本
修復 (若 image 版本正確):
kubectl rollout restart deployment/awoooi-api -n awoooi-prod
修復 (若 image 版本過舊):
等待 CD 完成部署最新版本
```
### 4. NodePort 32334 從外部無法訪問
```
診斷:
nc -zv 192.168.0.121 32334 ← 這個通嗎?
├── 通 → 用 192.168.0.121:32334 直連
└── 不通
kubectl get pods -n awoooi-prod
├── 沒有 Running pods → K3s pods 問題
└── 有 Running pods
iptables -t nat -L KUBE-NODEPORTS -n | grep 32334
├── 沒有規則 → K3s kube-proxy 問題,重啟 k3s
└── 有規則 → NetworkPolicy 或防火牆
```
### 5. Sentry 重開機後損壞修復 (2026-04-05 事故記錄)
**症狀**: sentry-self-hosted-postgres-1 / redis-1 / clickhouse-1 持續 Restarting
**原因**: 重開機時各 DB 未正常關閉,導致資料損壞:
- PostgreSQL: WAL 損壞 (`could not locate a valid checkpoint record`)
- Redis: dump.rdb 損壞 (`Wrong signature trying to load DB from file`)
- ClickHouse: system table store parts 損壞 (`broken and needs manual correction`)
**Break-glass 修復範例,不屬於一般重啟 SOP**:
先確認 Sentry DB volume 是受影響權威資料面,並具備 owner approval、volume backup / rollback ref、maintenance window 與 post-check plan。不得由 startup script、AI agent 或值班自動流程直接執行。
```bash
# Step 1: 修復 PostgreSQL WAL
docker run --rm -u 999 -v sentry-postgres:/var/lib/postgresql/data \
postgres:14 pg_resetwal -f /var/lib/postgresql/data
echo "PostgreSQL WAL reset 完成"
# Step 2: 修復 Redis (session cache可丟失)
docker run --rm --user root -v sentry-redis:/data alpine \
sh -c 'rm -f /data/dump.rdb && echo cleared'
echo "Redis dump.rdb 已清除"
# Step 3: 找出損壞的 ClickHouse store 目錄
docker logs sentry-self-hosted-clickhouse-1 2>&1 \
| grep -oE 'store/[a-f0-9]{3}/[a-f0-9-]+' \
| sort -u
# Step 4: 依據上方輸出刪除損壞目錄 (以 UUID 為準,以下為範例)
for uuid in "469/469957c9-..." "57d/57d1d567-..."; do
docker run --rm --user root -v sentry-clickhouse:/data alpine \
sh -c "rm -rf /data/store/$uuid && echo OK:$uuid"
done
# Step 5: 重啟 Sentry
cd /opt/sentry && docker compose up -d
# Step 6: 等待 2-3 分鐘後驗證
sleep 120
curl -o /dev/null -w "%{http_code}" http://192.168.0.110:9000/
# 預期: 200 / 302 / 400 (需登入)
```
**2026-06-26 修正方向**: 舊 `startup-110.sh` 曾把 PostgreSQL WAL 和 Redis RDB 損壞列入自動修復,這類資料層破壞性動作後續必須遷移到 owner-gated break-glass不得再擴大成預設自動化。ClickHouse parts 損壞同樣需人工識別 UUID、備份與 post-check。
---
### 6. Harbor ImagePullBackOff
```
診斷:
kubectl describe pod <pod> -n awoooi-prod | grep -A 5 Events
→ "ImagePullBackOff" or "ErrImagePull"
確認 Harbor 健康:
curl http://192.168.0.110:5000/v2/ → 應該回 {}
若 Harbor 不健康:
- 等 harbor-log healthy 後重啟: 見 Harbor Exited 128 修復
- 確認 Docker daemon 在 120/121 有設定 insecure registry:
cat /etc/docker/daemon.json | grep insecure
```
---
## E2E 驗證腳本
執行此腳本確認重開機後全系統正常。
```bash
#!/bin/bash
# 重開機後 E2E 驗證
# 使用: bash docs/runbooks/scripts/e2e-verify.sh
# 執行位置: 任意可 SSH 的機器
API="http://192.168.0.121:32334"
GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
PASS=0; FAIL=0
check() {
local name="$1"; local cmd="$2"; local expect="$3"
result=$(eval "$cmd" 2>/dev/null)
if echo "$result" | grep -q "$expect"; then
echo -e "${GREEN}$name${NC}"
((PASS++))
else
echo -e "${RED}$name${NC} (got: ${result:0:80})"
((FAIL++))
fi
}
echo "=== AWOOOI 重開機 E2E 驗證 $(date '+%Y-%m-%d %H:%M:%S') ==="
# 188 服務
check "188 PostgreSQL" "ssh ollama@192.168.0.188 'pg_isready -h localhost -p 5432'" "accepting"
check "188 Redis" "ssh ollama@192.168.0.188 'redis-cli -p 6380 ping'" "PONG"
check "188 OpenClaw" "ssh ollama@192.168.0.188 'curl -s http://localhost:8088/health'" "healthy"
# 110 服務
check "110 Harbor" "curl -s -o /dev/null -w '%{http_code}' http://192.168.0.110:5000/v2/" "200"
check "110 Gitea" "curl -s -o /dev/null -w '%{http_code}' http://192.168.0.110:3001" "200"
check "110 Alertmanager" "curl -s http://192.168.0.110:9093/-/healthy" "OK"
check "110 Gitea Runner" "curl -s -u 'wooo:TOKEN' 'http://192.168.0.110:3001/api/v1/repos/wooo/awoooi/actions/runners' | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get(\"total_count\",0))'" "1"
check "110 Sentry" "curl -s -o /dev/null -w '%{http_code}' --max-time 10 http://192.168.0.110:9000/" "[23][0-9][0-9]\|400"
check "Prometheus rules ≥25" "ssh wooo@192.168.0.110 'curl -s http://localhost:9090/api/v1/rules' | python3 -c \"import sys,json; r=json.load(sys.stdin); print(sum(len(g['rules']) for g in r['data']['groups']))\"" "[2-9][5-9]\|[3-9][0-9]"
# K3s
check "K3s Nodes Ready" "ssh wooo@192.168.0.120 'kubectl get nodes --no-headers' | awk '{print \$2}'" "Ready"
check "K3s Pods Running" "ssh wooo@192.168.0.120 'kubectl get pods -n awoooi-prod --no-headers' | grep -c Running" "3"
# AWOOOI API
check "API Health" "curl -s $API/api/v1/health" '"status":"healthy"'
check "API openclaw" "curl -s $API/api/v1/health" '"openclaw":{"status":"up"'
# 告警鏈路 E2E
PAYLOAD='{"receiver":"e2e-test","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"RebootE2ETest","severity":"info"},"annotations":{"summary":"重開機 E2E 驗證,請忽略"},"startsAt":"2026-04-05T00:00:00Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":""}],"groupLabels":{},"commonLabels":{},"commonAnnotations":{},"externalURL":"","version":"4","groupKey":"e2e-test"}'
check "告警鏈路 E2E" "curl -s -X POST $API/api/v1/webhooks/alertmanager -H 'Content-Type: application/json' -d '$PAYLOAD'" '"success":true'
echo ""
echo "=== 結果: ${PASS} 通過, ${FAIL} 失敗 ==="
[ $FAIL -eq 0 ] && echo -e "${GREEN}🎉 全部通過!系統正常${NC}" || echo -e "${RED}⚠️ 有 ${FAIL} 項失敗,請排查${NC}"
```
---
## 版本歷史
| 版本 | 日期 | 說明 |
|------|------|------|
| v5.30 | 2026-07-11 | 交叉驗證 99 實際 boot time修正 observer false negative部署 durable reboot state、PT20M task、principal-aware VM verifier/parser、VMware controlled apply並以 production-principal Recover 與 23/23 public matrix 證明 late recovery |
| v5.29 | 2026-07-11 | 初始 observer 將 99 判為未回電;此判讀已由 v5.30 的 Windows boot/readback 與 socket probe evidence supersedeT+10 breach 本身仍有效 |
| v1.0 | 2026-04-04 | 初版containerd + Docker + PostgreSQL WAL 修復流程 |
| v2.0 | 2026-04-05 上午 | 188 啟動腳本 + 110 啟動腳本 + Harbor race condition 修復 |
| v3.0 | 2026-04-05 下午 | 完整架構重盤 + Gitea Runner 自動化 + 告警鏈路根因修復 + NetworkPolicy 修正 |
| v4.0 | 2026-04-05 下午 | Prometheus 規則統一部署 (28條) + Sentry startup (Step 7) + Sentry損壞修復SOP + 規則未部署診斷樹 + E2E腳本加入Sentry/Prometheus驗證 |
| v5.0 | 2026-04-05 下午 | Harbor Exited(128) Race Condition 根治startup-110.sh 改用 harbor-log first 策略 + 新增 harbor-watchdog.service 常駐自愈curl -f bug 修正)|