All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m3s
CD Pipeline / build-and-deploy (push) Successful in 4m41s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
5.3 KiB
5.3 KiB
Public Maintenance Fallback Runbook
目標
主機或 VM 重啟時,public 網站不得直接露出空白 502。正式做法分兩層:
- L0 local edge:110 / 188 / 120 / 121 的 Nginx 仍可回應時,用
ops/maintenance/maintenance.html攔截502/503/504,顯示靜態維護頁。 - L1 external cloud/CDN:如果 99 / VMware / 家用路由 / 對外 IP 整段不可達,由外部雲端主機或 CDN health check 切到同一份靜態維護頁。
必要特性
- 維護頁不得讀取
.env、cookie、session、DB、K3s、Redis、NFS 或內部 API。 - 維護頁必須是靜態檔,能被 Nginx、Cloudflare Pages、S3/R2 static hosting、GCS static site 或任一低成本外部 VM 直接服務。
- L0 與 L1 使用同一份內容,避免使用者在不同故障層看到不同訊息。
- 502 fallback 只能處理 upstream failure;若整個 public edge unreachable,必須由 L1 接手。
L0 受控套用
- 將
ops/maintenance/maintenance.html部署到 edge host 的/var/www/maintenance/maintenance.html。 - 在每個 public
server {}內 includeops/maintenance/nginx-502-maintenance-snippet.conf的等價內容;AWOOOI source confops/nginx/awoooi.wooo.work.conf與k8s/nginx/awoooi-prod.conf已固定proxy_intercept_errors on、/__awoooi-maintenance.html與X-AWOOOI-Fallback: local-maintenance。 nginx -t通過後才 reload Nginx。- 用壞 upstream staging vhost 或暫存 upstream 驗證
X-AWOOOI-Fallback: local-maintenance,不得用 production DB / app restart 當測試手段。
L1 外部雲端/CDN 建議
優先順序:
- Cloudflare / CDN health check + fallback origin:最快切換,對使用者體驗最好。
- 低成本外部 VM:可控性高,能同時跑 blackbox probe;需要 VM patching 與監控。
- Object storage static hosting:成本最低,適合維護頁;DNS/CDN health check 仍要另外配置。
L1 active 條件:
probe_success對主要 public routes 連續 2 分鐘失敗,或awoooi_reboot_event_detected == 1且awoooi_reboot_auto_recovery_slo_ready == 0超過 10 分鐘,或- 99 / 110 / 188 / 120 / 121 / 112 任一 P0 host down 且 public route 同時 502 / timeout。
驗證
- L0:對測試 vhost 讀回
200或503的維護頁 HTML,且 headerX-AWOOOI-Fallback=local-maintenance存在。 - L1:從外部網路讀回靜態維護頁,且不經過 99 / 110 / 188 / 120 / 121 / 112。
- Recovery:主要 upstream 連續 2 分鐘健康後切回正式 origin;Telegram 發送 recovery 通知,並在 cold-start scorecard 留下 readback。
2026-07-02 scorecard gate
2026-07-02 晚間 Gitea CD #4446 rollout 期間曾讀回 public 502,後續正式 health 回到 200。這證明「source 有維護頁」不足以代表使用者不會看到 raw 502;驗收必須要求 runtime readback。
固定驗證命令:
python3 scripts/reboot-recovery/public-maintenance-fallback-probe.py \
--output /tmp/public-maintenance-fallback.json
python3 scripts/reboot-recovery/reboot-auto-recovery-slo-scorecard.py \
--public-maintenance-file /tmp/public-maintenance-fallback.json
判斷規則:
public_route_raw_5xx_without_maintenance_fallback:L0 Nginx intercept 未生效,下一步只能做 staging vhost /nginx -t/ header verifier,不得用 production app restart 當測試。public_route_unreachable_without_external_l1_fallback:edge 或外部路徑整段不可達,下一步是 L1 external static origin / CDN health check decision record 與 rollback,不直接切 DNS。public_maintenance_fallback_runtime_readback_missing:沒有 runtime probe 證據,不得宣稱 public maintenance fallback 已完成。
2026-07-02 L0 source apply
已將 AWOOOI edge Nginx source 從 legacy /50x.html 改為 L0 maintenance fallback:
ops/nginx/awoooi.wooo.work.confk8s/nginx/awoooi-prod.confops/maintenance/nginx-502-maintenance-snippet.conf
驗收仍必須分兩層:
- Source verifier:
scripts/reboot-recovery/tests/test_reboot_p0_operational_contract.py必須確認上述 source 不可退回/50x.html。 - Runtime verifier:在 edge host 用 staging vhost /
nginx -t/ header readback 證明X-AWOOOI-Fallback=local-maintenance;通過前不得宣稱 production runtime 已接管 raw 502。
2026-07-02 API runtime artifact overlay
/api/v1/agents/reboot-auto-recovery-slo-scorecard 不得只依賴 committed snapshot 判斷 public maintenance fallback;它必須能讀取 reboot-auto-recovery-slo-exporter.sh 產出的 runtime artifact。
讀取順序:
AWOOOI_PUBLIC_MAINTENANCE_FALLBACK_READBACK_FILE指定的 JSON。/home/wooo/reboot-recovery/reboot-auto-recovery-slo-*/public-maintenance-fallback.json中最新一份。- 找不到 artifact 時維持 fail-closed
public_maintenance_fallback_runtime_readback_missing。
驗收:
- Production API 讀回
readback.public_maintenance_runtime_readback_present=true。 - Production API 讀回
readback.public_maintenance_fallback_ready=true。 active_blockers不包含public_maintenance_fallback_runtime_readback_missing。- 若 public routes 都正常,這只代表「沒有裸 5xx 且 runtime artifact 已接上 scorecard」;仍需 staging vhost /
nginx -t/ header readback 才能宣稱 L0 intercept 在 upstream failure 時生效。