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

241 lines
7.8 KiB
Markdown
Raw Permalink 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.

# Spec: 設定驅動的多主機正式部署
> Status: `draft-for-review`
> Source: `requirements.md`
> Last updated: `2026-07-18`
> Implementation: not started
## 1. 目標拓撲
```text
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`
非機密設定的單一入口:
```yaml
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 可選擇覆寫 `sshUser`、`sshPort`、`sshPrivateKey`、`wireguardIP` 與 `architecture`。preflight 必須拒絕重複 IP、重複 WireGuard IP、空角色、Redis/MinIO 多於一台(第一版)與缺少必要角色。
### 2.2 `secrets.vault.yml`
至少包含:
```yaml
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 first**DNS 驗證、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. **applications**backend rolling start、worker+crawler start、frontend rolling start。
11. **Edge**:產生 upstream、申請網站 TLS、驗證後 reload。
12. **acceptance**HTTP、job claim、crawler health、Redis/MinIO persistence、公網封鎖與版本報告。
任一步驟失敗即停止後續 checkpoint重跑從 idempotent state 繼續,不執行 wipe。
## 5. 一般部署狀態機
```text
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. 操作介面
```bash
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。