thread-master/docs/product/haixun-deployment/spec.md

7.8 KiB
Raw Permalink Blame History

Spec: 設定驅動的多主機正式部署

Status: draft-for-review
Source: requirements.md
Last updated: 2026-07-18
Implementation: not started

1. 目標拓撲

Internet
  |
  v
Edge Nginx
  |-- /                 -> frontend-01, frontend-02, ...
  |-- /api              -> backend-01, backend-02, ...
  `-- /haixun-assets    -> MinIO

backend-* ----+---- MongoDB Atlas (fixed egress IP)
worker-*  ----+---- Redis over WireGuard
              `---- MinIO over WireGuard

worker-N <---- private Docker network ----> crawler-N

control machine -- SSH/Ansible --> all hosts
control machine -- push --------> private Registry
all Docker hosts -- pull -------> private Registry

2. 部署控制檔

2.1 deployment.yml

非機密設定的單一入口:

environment: production

bootstrap:
  operatingSystem: ubuntu-24.04
  installDocker: true
  installWireGuard: true
  configureFirewall: true
  configureTimeSync: true
  installSecurityUpdates: true
  enableAutomaticUpdates: true
  acceptNewSSHHostKeys: true

ssh:
  user: ubuntu
  port: 22
  privateKey: ~/.ssh/id_ed25519

nodes:
  edge:
    - ip: 203.0.113.10
  registry:
    - ip: 203.0.113.11
  frontend:
    - ip: 203.0.113.20
  backend:
    - ip: 203.0.113.30
  worker:
    - ip: 203.0.113.40
  redis:
    - ip: 203.0.113.50
  minio:
    - ip: 203.0.113.60

network:
  wireguardCIDR: 10.80.0.0/16
  atlasEgressNode: edge

domains:
  web: app.example.com
  registry: registry.example.com
  letsEncryptEmail: admin@example.com

images:
  registry: registry.example.com/haixun
  tag: auto

database:
  provider: mongodb-atlas
  name: haixun
  runMigrations: true
  runInit: true
  seedAdmin: true

redis:
  namespace: haixun-prod
  persistencePath: /var/lib/haixun/redis
  appendOnly: true

minio:
  persistencePath: /var/lib/haixun/minio
  bucket: haixun-assets
  initializeBucket: true

每個 node 可選擇覆寫 sshUsersshPortsshPrivateKeywireguardIParchitecture。preflight 必須拒絕重複 IP、重複 WireGuard IP、空角色、Redis/MinIO 多於一台(第一版)與缺少必要角色。

2.2 secrets.vault.yml

至少包含:

mongoAtlasURI: mongodb+srv://...
registryUser: ...
registryPassword: ...
redisPassword: ...
minioRootUser: ...
minioRootPassword: ...
authAccessSecret: ...
authRefreshSecret: ...
memberSettingsEncryptionKeyID: v1
memberSettingsEncryptionKey: ...
aiModelCacheFingerprintSecret: ...
scoutSessionSecret: ...
crawlerToken: ...
initialAdmin:
  email: admin@example.com
  displayName: System Admin
  password: ...

Provider、SMTP、OAuth、Stripe 等既有 runtime secret 也放同一 Vault。Vault password 不進 repo遠端 role 只取得執行該角色必要的 secret。

3. Image 與執行單位

Image 內容 持久資料
frontend Vite build + static Nginx
backend Go gateway + extension ZIP
worker Go worker + Node scraper + Chromium 無;僅 ephemeral temp
crawler Node + Playwright crawler
ops migrate/init/seeder binaries 與 migration files
registry:2 私有 image registry Registry volume
redis:7 cache/lock + AOF Redis volume
minio/minio object storage MinIO volume

應用 image 使用不可變 git SHA tag。latest 不可作為 production deploy 真相inventory 另保存各角色目前與上一個成功 tag。

4. First deploy 狀態機

make first-deploy 僅是本機 orchestrator按下列 checkpoint 執行:

  1. control doctor:檢查 Ansible、Docker Buildx、SSH、Vault、Git worktree tag 能力。
  2. preflight:解析設定、檢查 DNS、SSH/sudo、Ubuntu 版本、CPU architecture、磁碟/RAM、Atlas 固定出口前置條件。
  3. raw bootstrap:在無 Python VM 先安裝 python3
  4. common role套件、安全更新、chrony、fail2ban、目錄、Docker、log rotation。
  5. WireGuard:配置 peer、防火牆與連通測試所有私網測試成功才繼續。
  6. Registry firstDNS 驗證、Let's Encrypt、認證、持久 volume、所有 Docker host login。
  7. local build/push:測試並 build frontend/backend/worker/crawler/opspush immutable tag。
  8. data services:啟動 Redis/MinIO檢查 persistence 與私網存取;建立 bucket/policy。
  9. Atlas prepare:由單一受控 backend host 執行 ops container依序 migration -> init -> optional first seed。
  10. applicationsbackend rolling start、worker+crawler start、frontend rolling start。
  11. Edge:產生 upstream、申請網站 TLS、驗證後 reload。
  12. acceptanceHTTP、job claim、crawler health、Redis/MinIO persistence、公網封鎖與版本報告。

任一步驟失敗即停止後續 checkpoint重跑從 idempotent state 繼續,不執行 wipe。

5. 一般部署狀態機

test -> local build -> push -> remote pull while old runs
     -> optional migration(run_once)
     -> atomic config sync
     -> rolling recreate
     -> readiness
     -> edge upstream update
     -> record successful tag
  • backend migration 失敗:不 recreate backend。
  • backend/frontend readiness 失敗:不加入 upstream保留其他健康節點。
  • worker 失敗:回報 host其他 worker 不回滾。
  • config-only deploy跳過 build/push但仍做 render validation、atomic sync 與 recreate。
  • rollback切回上一個成功 image tag資料庫只允許 forward-compatible migration不自動執行 down migration。

6. 初始資料庫契約

  • ops migrate:使用 Atlas URI 執行所有 forward migrations。
  • ops init:執行 cmd/init,只建立可重複建立的 operational indexes。
  • ops seed-admin:只在 database.seedAdmin=true 且目標管理員不存在時建立初始資料。
  • seeder 需把目前寫死的 email/display name 改成設定值password 至少 12 字元。
  • 既有管理員存在時seed 必須成功 no-op不可更新 password hash。
  • 管理員改密碼另設 make admin-reset-password,要求明確 host/environment 與確認。

7. 網路規則

來源 目的 Port 公開
Internet Edge 80/443
Control/Docker hosts Registry 443 認證 + TLS
Edge WG IP Frontend container HTTP
Edge WG IP Backend gateway port
Backend/Worker WG IP Redis 6379
Backend/Worker/Edge WG IP MinIO 9000
Worker container crawler sidecar crawler port 否,不 publish
Backend/Worker Atlas 27017/SRV 經固定出口

防火牆啟用前先 allow SSH所有 role 完成後做反向驗證,確認 private port 從公網不可達。

8. 操作介面

make setup-control
make doctor
make preflight
make bootstrap
make bootstrap ROLE=worker
make bootstrap HOST=worker-02
make first-deploy
make deploy-all
make deploy-frontend
make deploy-backend
make deploy-worker
make deploy-config ROLE=worker
make db-check
make db-migrate
make db-init
make db-seed
make status
make logs ROLE=worker HOST=worker-01
make restart ROLE=backend
make rollback ROLE=backend
make vault-edit

具破壞性的資料 reset 不提供無參數捷徑,必須指定 environment、role 並互動確認。

9. Readiness 與擴縮

  • gateway readiness 至少驗證程序、Redis、MongoDBS3 狀態另外呈現,不能再只靠目前 Redis-only health。
  • frontend readiness 驗證靜態首頁與指定 build version。
  • crawler 增加 health endpointcrawler token 不得記錄。
  • worker 第一版以 container running + Mongo/Redis connectivity + worker identity 啟動檢查為準;後續補 durable heartbeat/lag。
  • worker ID 由 hostname/role 派生,不得在多主機都使用 worker-1
  • Outbox 全域 lock 是吞吐限制,部署完成不代表 Outbox 已水平擴充;須另立應用改造 task。