# Plan: 設定驅動的多主機正式部署 > Status: `draft-for-review` > Source: `requirements.md`, `spec.md` > Last updated: `2026-07-18` > Implementation: not started; this plan does not authorize a live deployment ## 1. 交付策略 1. 先建立設定 schema、preflight 與空 VM bootstrap,再碰正式服務。 2. 先完成 WireGuard 與 Registry,確保後續 image 只需本機 build、遠端 pull。 3. 資料層先行:Atlas connectivity、Redis persistence、MinIO persistence/bucket/backup。 4. 將現有共同 release 拆成 frontend、backend、worker、crawler、ops images。 5. 先單節點驗證,再做 frontend/backend rolling pool 與 Edge 動態 upstream。 6. first deploy、一般 deploy、config-only deploy、rollback 必須是不同明確路徑。 7. 所有 live 操作前先在全新 Ubuntu 24.04 VM 的 staging inventory 演練。 ## 2. 里程碑 ### M0 - 文件、設定契約與安全邊界 - 建立 `deployment.yml.example` 與 Vault schema。 - 定義 host role、覆寫規則、WireGuard 自動配號、image tag 與目錄契約。 - 實作 `doctor`/`preflight`,拒絕缺 IP、重複 IP、DNS 未指向、SSH/sudo 不可用與 unsupported OS。 - 定義正式環境防火牆矩陣、Atlas 固定出口與 TLS 前置條件。 完成定義:不連線部署即可驗證完整設定;錯誤訊息能指出欄位與 host。 ### M1 - 空 VM bootstrap - 用 Ansible raw task 支援無 Python 的 Ubuntu 24.04。 - 安裝 Python、Docker/Compose、WireGuard、UFW、chrony、fail2ban、unattended-upgrades。 - 建立 `/opt/haixun`、`/etc/haixun`、`/var/lib/haixun`、服務帳號與權限。 - 設定 Docker log rotation、daemon 啟動、Registry trust/login hook。 - 防火牆先保留 SSH,再按 role 套規則。 - bootstrap 可重跑,且不修改/清除 data volume。 完成定義:七種角色的空 VM 都可從只有 SSH/sudo 到 bootstrap ready;第二次執行為 no-op 或安全更新。 ### M2 - WireGuard、Registry 與本機 build pipeline - 依 inventory 自動分配 WireGuard IP,建立 peer 並驗證連通。 - 配置固定出口節點供 Atlas 連線。 - 部署帶 TLS/auth/persistent volume 的自架 Registry。 - Edge/Registry DNS 檢查與 Let's Encrypt 自動申請/續期。 - 新增 frontend/backend/worker/crawler/ops Dockerfile 與 Buildx build/push 指令。 - image 使用 git SHA;保存上一個成功 tag 供 rollback。 完成定義:遠端 VM 不具備 source tree 也能 pull 並啟動測試 image;Registry 重啟後 image 仍存在。 ### M3 - Atlas、Redis、MinIO 與 first data initialization - production 移除本機 Mongo 假設,所有 runtime 讀取 Atlas URI。 - Redis 獨立 Compose:auth、AOF、volume、health、backup/restore。 - MinIO 獨立 Compose:volume、health、bucket/policy idempotent init、backup/restore。 - ops image 提供 migration/init/seeder。 - seeder 改為由 Vault 設定 admin email/display name/password,既有資料不覆寫。 - 將現有 backup、monitoring、reset 腳本從本機 Mongo 假設拆開;Atlas 備份交由 Atlas Backup/PITR。 完成定義:空 Atlas database 可按 migration -> init -> seed 建立;重跑不重複資料、不重設密碼;Redis/MinIO restart 後資料保留。 ### M4 - 應用角色拆分與無狀態修正 - frontend image 只含 production static build。 - backend image 含 gateway 與固定 extension ZIP path。 - worker image 含 Go worker、Node profile scraper、相容 Chromium。 - crawler sidecar 可設定 bind address、有 health endpoint,只走 private Docker network。 - role-specific env/template 只下發所需 secret。 - gateway readiness 補 Mongo + Redis;frontend 提供 build version;worker 啟動檢查 identity/connectivity。 完成定義:frontend/backend/worker 可分別 build、push、deploy、restart;刪除任一 app container 後可從 image/config 重建且無業務資料遺失。 ### M5 - 多節點、Edge 與一般部署 - Edge 依 inventory 產生 frontend/backend upstream。 - backend/frontend 採 `serial: 1` rolling recreate,readiness 後才加入流量。 - worker 支援指定 host 或整組新增/更新,worker ID 唯一。 - config-only deploy 原子同步設定並只 recreate 指定角色。 - status/logs/restart/rollback 以 role/host 過濾。 - migration `run_once` 且先於 backend recreate;失敗立即停止。 完成定義:增加一個 IP 可加入新 worker;增加 frontend/backend IP 可健康註冊;部署期間其他健康節點持續服務。 ### M6 - First deploy orchestrator、備份與演練 - `make first-deploy` 串接 M0-M5 checkpoint,但每一步仍可單獨重跑。 - 建立 Redis/MinIO/Registry restore runbook 與 Atlas restore 責任邊界。 - 提供首次部署報告:host、role、WG IP、image tag、health、TLS、backup 與單點警告。 - 在全新 staging VM 完成安裝、失敗續跑、config-only、加節點、rollback、重啟持久性與公網封鎖測試。 - live deployment 必須由使用者另行確認,不因完成程式碼而自動執行。 完成定義:從設定檔與空 VM 到完整 staging 可用,不登入遠端手動安裝或改設定。 ## 3. 預計檔案落點 ```text apps/web/Dockerfile apps/backend/Dockerfile.gateway apps/backend/Dockerfile.worker apps/backend/Dockerfile.ops apps/backend/crawler/Dockerfile deploy/prod/ Makefile ansible.cfg deployment.yml.example secrets.vault.yml.example inventory/ playbooks/ bootstrap.yml first-deploy.yml deploy.yml database.yml status.yml roles/ common/ wireguard/ registry/ edge/ frontend/ backend/ worker/ redis/ minio/ templates/ scripts/ doctor.sh preflight.sh build.sh push.sh deploy.sh ``` 現有單機 production scripts 在新流程驗收前不直接刪除;實作時先標示 legacy,確認沒有現役依賴後再決定移除,避免中斷既有環境。 ## 4. 驗證矩陣 | ID | 驗證 | |----|------| | DEP-01 | 空 Ubuntu 24.04 無 Python VM 可 bootstrap | | DEP-02 | bootstrap 重跑不清資料、不重建 secret | | DEP-03 | 只填 SSH IP 即可自動配置 WireGuard IP | | DEP-04 | Registry TLS/auth/persistence 正常 | | DEP-05 | 應用 image 全由本機 build,遠端無 source/build | | DEP-06 | Atlas migration/init/seed 順序正確且可重跑 | | DEP-07 | seed 不覆寫既有管理員密碼 | | DEP-08 | Redis/MinIO restart 後資料保留 | | DEP-09 | config-only deploy 不產生新 image且新設定生效 | | DEP-10 | backend/frontend rolling deploy 保留健康服務 | | DEP-11 | 新增 worker IP 後只有新 host 被 bootstrap/deploy | | DEP-12 | 新增 backend/frontend 後 Edge 只加入健康節點 | | DEP-13 | migration 失敗時 backend 保持舊版 | | DEP-14 | rollback 可切回上一個成功 image tag | | DEP-15 | 公網無法直連 Redis/MinIO private API/backend/crawler | | DEP-16 | crawler sidecar 可用且 host 無公開 crawler port | | DEP-17 | Atlas 連線顯示固定出口 IP | | DEP-18 | 備份與還原演練有可驗證結果 | ## 5. 風險與後續 | 風險 | 第一版處理 | |------|------------| | Redis/MinIO/Registry/Edge 單點 | 明示風險、持久化、備份、restore runbook;HA 後續里程碑 | | 全 mesh 新增節點需更新 peers | 由 Ansible inventory 統一生成並分批套用 | | Atlas 固定出口節點故障 | 第一版接受單點;文件化切換出口與 allowlist 流程 | | Outbox 全域 lock 限制 worker 擴縮 | 部署不誤稱 Outbox 已水平擴充;另立應用 task 改 due query/claim/concurrency | | 外部發文 at-least-once 重複 | 不在部署層假裝解決,保留既有 lease 風險並另案處理 | | seeder 目前寫死管理員資料 | M3 先修成 Vault 可設定,再允許 first deploy seed | | health 目前只檢查 Redis | M4 補 Mongo readiness,避免壞節點進 upstream | | 舊單機腳本仍可能在使用 | 新流程先並存並標 legacy,不自動刪除或改動現役服務 | ## 6. 實作前仍需填入的實際值 - 所有 VM SSH IP、user、port、private key。 - Edge、Registry 網域與已完成的 DNS A/AAAA record。 - Atlas SRV URI、database user 與固定出口 allowlist。 - Registry、Redis、MinIO、JWT、加密、Crawler、Provider、SMTP 等 Vault secret。 - 初始管理員 email、display name、password。 - 各 VM CPU architecture、磁碟掛載與備份目的地。 上述值未備齊時可完成程式與 staging 測試,但不得執行正式 first deploy。