- ADR-037 監控增強架構 - MONITORING_MASTER_PLAN 主計畫 - MASTER_EXECUTION_SCHEDULE 執行排程 - Phase D/E/Worker HPA Runbooks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
27 KiB
AWOOOI 整體整合架構統合設計
文件類型: 統合架構設計(Single Source of Truth for Integration) 優先級: 🔴 統帥最高指令 建立: 2026-03-29 13:27 (台北) 核心命題: 所有節點必須在同一座大腦的神經網路中協同運作,不允許孤島。
第一部分:現況誠實盤點(精確)
已確認:比稽核報告更樂觀的部分
| 項目 | 稽核報告誤判 | 真實現況 |
|---|---|---|
| Worker SIGTERM 處理 | 報告說「缺失」 | ✅ 已實作(signal_worker.py:450-455)— signal.signal(SIGTERM) + shutdown_event |
| Worker 優雅關機流程 | 報告說「需要實作」 | ✅ 已實作(stop() 方法,有心跳機制) |
| SignOz Webhook 路由 | 報告說「未部署」 | ✅ 已路由(main.py:419) |
已確認:比稽核報告更嚴峻的部分
| 項目 | 稽核報告版本 | 真實缺口 |
|---|---|---|
| Worker stop() timeout | 未提及 | ❌ 只有 5 秒,AI 分析 30-60 秒會被強殺 |
| K8s terminationGracePeriodSeconds | 未提及 | ❌ 未設定,K8s 預設 30 秒不夠用 |
| ESLint i18n 強制 | 說「CI 攔截」 | ❌ 只有 TODO 注解(.eslintrc.js:20-22),未實際安裝 plugin |
| Visual Regression 跨平台 | 說「截圖比對」 | ❌ Mac 與 CI Linux 字體渲染不同,baseline 不能在 Mac 產生 |
| PostgreSQL HA | 說「Streaming Replication」 | ❌ 無切換機制,主庫掛了需要人工介入 |
| Redis HA | 完全未提及 | ❌ 無 Sentinel,Redis 單點故障 |
| SSE Event Sourcing | 只設計了事件類型 | ❌ F5 刷新後 GenUI 卡片全消失 |
| Kali 整合 | Cronjob 被動 | 🟡 層次太低,應升格為 SecurityAgent |
第二部分:完整整合地圖
2.1 系統神經網路拓撲
外部事件輸入
┌─────────────────────────────────────────────────────────────────┐
│ Alertmanager :9093 Sentry :9000 SignOz :3301 │
│ GitHub Actions Kali Scanner K8s Events │
└──────────┬──────────────┬──────────────┬──────────────────────┘
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────┐
│ AWOOOI API (K3s :32334) │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌─────────────┐ │
│ │Alertmanager│ │Sentry │ │SignOz │ │Kali(未來) │ │
│ │Webhook │ │Webhook │ │Webhook │ │Webhook │ │
│ └────┬─────┘ └────┬─────┘ └─────┬─────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └──────────────┴──────────────┴────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Signal Worker (Redis XREADGROUP) │ │
│ │ ← 消費 awoooi:signals stream │ │
│ │ → IncidentEngine (聚合 / GraphRAG / 持久化) │ │
│ └──────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ OpenClaw (192.168.0.188:8089) │ │
│ │ 決策引擎: RCA → Blast Radius → Risk → Action │ │
│ │ 工具: kubectl / SSH / Prometheus / SigNoz │ │
│ └──────┬─────────────────────┬────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ Telegram Bot Approval DB (PostgreSQL) │
│ 統帥通知 審核佇列 │
└──────────────────────────────────────────────────────────────────┘
│
統帥批准 / 拒絕
│
┌────────────▼────────────┐
│ Auto-Repair Actions │
│ restart/scale/rollback │
└─────────────────────────┘
2.2 前端整合地圖
AWOOOI Web (K3s :32335 / Next.js)
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌── Dashboard (/) ─────────────────────────────────────────┐ │
│ │ AutonomyIndexPanel ← GET /api/v1/stats/autonomy │ │
│ │ SystemPulseRow ← GET /api/v1/stats/overview │ │
│ │ DecisionZone ← SSE /api/v1/approvals/stream │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌── Omni-Terminal ─────────────────────────────────────────┐ │
│ │ Input Area → POST /api/v1/terminal/command │ │
│ │ ThinkingStream ← SSE /api/v1/terminal/stream/{id} │ │
│ │ GenUI Renderer ← event: render_ui │ │
│ │ Event Replay ← Redis List (Last-Event-ID) │ ← ❌ 缺失
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌── Knowledge Base (/knowledge-base) ─────────────────────┐ │
│ │ ❌ 空白頁面,缺後端 API │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ [深度調查跳脫入口] │
│ → SigNoz: http://192.168.0.188:3301 (新分頁) │
│ → Grafana: http://192.168.0.188:3000 (新分頁) │
│ → Sentry: http://192.168.0.110:9000 (新分頁) │
│ │
└─────────────────────────────────────────────────────────────────┘
第三部分:六大缺口的系統整合修復方案
缺口 1:Worker terminationGracePeriodSeconds 不足
問題根源:signal_worker.py 的 stop() 等 5 秒,但 AI 分析任務最長 60 秒。K8s 預設 terminationGracePeriodSeconds: 30。兩個值都不夠,且彼此沒有對齊。
與整體整合的依賴:
- Worker 縮容影響:HPA 縮容時 → K8s 發 SIGTERM →
stop()被調用 → 5 秒後強殺 - 上游依賴:Sentry 分析任務、Alertmanager 分析任務都在 Worker background task 中執行
- 下游影響:PostgreSQL 寫入可能不完整(Incident 狀態 Dirty)
修復:三層數值對齊
# k8s/awoooi-prod/08-deployment-worker.yaml
# 修改 terminationGracePeriodSeconds
spec:
template:
spec:
terminationGracePeriodSeconds: 90 # 給 Worker 足夠時間完成當前任務
containers:
- name: awoooi-worker
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # 給 K8s 時間發 SIGTERM
# apps/api/src/workers/signal_worker.py
# 修改 stop() 的 timeout 與 K8s terminationGracePeriodSeconds 對齊
async def stop(self) -> None:
if not self._running:
return
self._running = False
if self._task:
try:
# 從 5 秒改為 75 秒(比 terminationGracePeriodSeconds=90 少 15 秒緩衝)
await asyncio.wait_for(self._task, timeout=75.0)
except TimeoutError:
logger.warning("signal_worker_stop_timeout_forcekill")
self._task.cancel()
except asyncio.CancelledError:
pass
logger.info("signal_worker_stopped")
整合驗證指令:
# 模擬 K8s 縮容,確認 Worker 優雅關機
kubectl scale deployment awoooi-worker -n awoooi-prod --replicas=0
kubectl logs -n awoooi-prod $(kubectl get pod -n awoooi-prod -l app=awoooi-worker -o name) --tail=20
# 預期看到:
# shutdown_signal_received signal=15
# signal_worker_shutting_down
# signal_worker_shutdown_complete
# (整個流程在 90 秒內完成)
缺口 2:ESLint i18n 強制攔截(eslint-plugin-i18next)
問題根源:.eslintrc.js:20-22 只有 TODO 注解,未安裝 eslint-plugin-i18next。
與整體整合的依賴:
- 這是 i18n 清零後的防護層:清零是清過去的債,ESLint 是防未來的債
- 需在
pnpm build和pnpm lintCI 步驟中阻擋 - 影響:所有前端開發流程(AI 生成代碼也必須通過)
修復:安裝並啟用 Plugin
# Step 1: 安裝
cd apps/web
pnpm add -D eslint-plugin-i18next
// apps/web/.eslintrc.js 修改後
module.exports = {
extends: [
'@awoooi/eslint-config/react',
'next/core-web-vitals',
'plugin:i18next/recommended', // ← 新增
],
plugins: ['i18next'], // ← 新增
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'@next/next/no-html-link-for-pages': 'off',
'no-console': 'off',
// 🚨 i18n 鐵律:所有 JSX 文字必須透過 t() 函式
// 違反此規則 = PR 阻擋(error 級別)
'i18next/no-literal-string': ['error', {
markupOnly: true, // 只攔截 JSX 文字節點(非 JS 字串)
ignoreAttribute: [ // 技術屬性不攔截
'className', 'id', 'href', 'src', 'type', 'key',
'data-testid', 'aria-label', 'placeholder'
],
}],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'@typescript-eslint/consistent-type-imports': 'warn',
'no-constant-condition': 'warn',
},
ignorePatterns: [
'node_modules', '.next', 'out', 'dist', 'test-results',
'*.config.js', '*.config.ts',
],
}
與 CI 整合(必須加入 cd.yaml):
# .github/workflows/cd.yaml 在 Build 之前加入 Lint 步驟
- name: 🔍 ESLint i18n 強制檢查
run: |
cd apps/web
pnpm lint
# 失敗 = 有硬編碼字串 = 直接阻擋部署
⚠️ 重要:第一次啟用 eslint-plugin-i18next 後,現有的 40+ 違規會立刻全部報錯。因此必須先完成 i18n 清零,再啟用此 Rule。正確順序:
- i18n 清零(一次性修復 40+ 違規)
- 安裝 eslint-plugin-i18next(啟用防護)
- 加入 CI Lint 步驟
缺口 3:Visual Regression 跨平台渲染問題
問題根源:Mac(M1/M2/M3)vs GitHub Actions(Linux Ubuntu)的字體渲染引擎不同(CoreText vs FreeType),導致截圖像素不吻合。
與整體整合的依賴:
- Baseline 快照必須統一來源(CI Docker 環境)
- 每次更新 Baseline 必須是可審計的(透過 PR),不能在本機靜默更新
修復:Docker 強制基準線更新 + threshold 調整
// apps/web/package.json 新增 scripts
{
"scripts": {
"test:visual:update": "docker run --rm -v $(pwd):/work -w /work -p 3000:3000 mcr.microsoft.com/playwright:v1.44.0-jammy pnpm exec playwright test --update-snapshots --project=chromium --grep @visual",
"test:visual": "pnpm exec playwright test --project=chromium --grep @visual"
}
}
// apps/web/playwright.config.ts 修改截圖比對設定
export default defineConfig({
expect: {
toHaveScreenshot: {
threshold: 0.05, // 允許 5% 差異(吸收跨平台微小差)
maxDiffPixelRatio: 0.05,
// 強制使用 CI 環境的字體設定
},
},
use: {
// 截圖時的視窗大小固定,避免不同螢幕 DPI 差異
viewport: { width: 1280, height: 720 },
deviceScaleFactor: 1, // 強制 1x,避免 Retina 差異
},
});
強制規範(加入 .awoooi-agent-rules.md 條款):
## 條款 21:Visual Regression Baseline 更新規範
🚨 絕對禁止在本機 Mac 環境執行 `--update-snapshots`
✅ 更新 Baseline 必須透過以下流程:
1. 在本機執行:`pnpm test:visual:update`(Docker 環境)
2. Docker 生成的 .png 截圖自動存入 tests/e2e/__snapshots__/
3. 提 PR,標注 📸 VISUAL_UPDATE
4. 統帥視覺審核截圖後方可合併
缺口 4:PostgreSQL HA(Patroni / CloudNativePG)
問題根源:PostgreSQL 在 .188 上是單一 Docker 容器,K3s 的 Datastore 也依賴它(ADR-033)。資料庫掛掉 = K3s 控制面 + AWOOOI 資料同時失效。
與整體整合的依賴:
- PostgreSQL 是 AWOOOI 的 Episodic Memory(Incidents、Approvals、Audit Logs)
- PostgreSQL 也是 K3s 的 HA Datastore(120/121 節點的 K3s 元數據)
- Auto-Repair 對 PostgreSQL 執行
docker restart是危險的(可能 Dirty Page)
修復路線圖(三階段):
Phase DB-A(1週,低風險):
監控補強
├── 啟用 PG slow query log (log_min_duration_statement = 2000ms)
├── 加入 pg_stat_statements extension 並接入 Prometheus
└── 關閉 auto_repair.postgres.restart(防止 Dirty Page)
Phase DB-B(1月,中風險):
備份策略
├── Velero + PostgreSQL Volume Snapshot(已有 Velero,需加 Volume 備份)
└── 確認 WAL archiving 到 MinIO(WAL-E/WAL-G)
Phase DB-C(Q2,需評估):
HA 策略評估
├── 方案 A:CloudNativePG(K8s 原生 PostgreSQL Operator)
│ → 在 K3s 中部署 CloudNativePG,主從自動切換
├── 方案 B:Patroni + keepalived(VM 層 HA)
│ → 在 .188 和備用機上部署 Patroni
└── 方案 C:Citus(分片,過於複雜,暫不考慮)
推薦:方案 A (CloudNativePG),與 K3s 最整合
立即可執行的防護措施:
# k8s/awoooi-prod/manual-remediation/postgres-recovery.yaml
# 建立 PostgreSQL 緊急修復 Playbook(人工操作)
# 事件:PostgreSQL 掛了
# 動作:
# 1. OpenClaw 發告警 + Telegram
# 2. AlterManager 生成 CRITICAL Approval(不自動修復)
# 3. 統帥核准後,執行以下指令:
# ssh root@192.168.0.188 'docker restart awoooi-postgres'
# kubectl rollout restart deployment/awoooi-api -n awoooi-prod
# kubectl rollout restart deployment/awoooi-worker -n awoooi-prod
缺口 5:Redis HA(Sentinel 模式)
問題根源:Redis 在 .188 上是單一容器(port 6380),無備援。Redis 同時承載:
- Working Memory(Incident 聚合狀態)
- SSE Terminal Event Store(未來的 Event Source)
- Sentry Dedup Cache(10分鐘去重 TTL)
- Anomaly Counter(ADR-037 核心數據)
與整體整合的依賴:
- Redis 掛掉 = Signal Worker 無法消費事件 = 整個告警鏈路中斷
- AOF 啟用對性能有影響,需要評估
修復路線圖:
Phase Redis-A(立即,0風險):
確認 AOF 配置
├── ssh root@192.168.0.188 'docker exec awoooi-redis redis-cli CONFIG GET appendonly'
└── 確認 appendonly yes(否則重啟後 Working Memory 歸零)
Phase Redis-B(1月,中等工程量):
Redis Sentinel 部署(在 .110 上部署 Sentinel + Replica)
├── .188:Master(現有)
├── .110:Replica + Sentinel
└── OpenClaw 使用 redis-sentinel:// URI,自動發現 Master
配置變更:
# AWOOOI API 連線改用 Sentinel
REDIS_URL=redis-sentinel://sentinel1:26379/awoooi-master
立即可執行的防護措施:
# 確認 Redis AOF 狀態
ssh root@192.168.0.188 'docker exec awoooi-redis redis-cli CONFIG GET appendonly; \
docker exec awoooi-redis redis-cli CONFIG GET appendfsync; \
docker exec awoooi-redis redis-cli INFO persistence | grep aof'
# 若 appendonly = no,立即啟用(需重啟 Redis)
ssh root@192.168.0.188 'docker exec awoooi-redis redis-cli CONFIG SET appendonly yes'
# 注意:CONFIG SET 是即時生效的,不需要重啟
缺口 6:SSE Event Sourcing(Terminal 狀態不丟失)
問題根源:Omni-Terminal 的 SSE 串流是無狀態的,F5 刷新後所有 render_ui GenUI 卡片消失。
與整體整合的依賴:
- 這是 Agentic Workspace 用戶體驗的底層設施
- 依賴 Redis List 作為 Event Store(如果 Redis 無 AOF,重啟後也丟)
- 必須與 SSE 三種事件類型設計同步建立
修復:三層機制
Layer 1: 後端 Event Store(Redis List)
terminal.py → 每個 SSE 事件同步寫入 Redis List
Key: terminal:events:{command_id}
TTL: 3600 秒(1小時)
Layer 2: 前端 Reconnect(Last-Event-ID)
useTerminalSSE → EventSource 自動帶 Last-Event-ID
後端收到後:從 Redis 撈出錯過的事件 → Replay → 接上即時 Stream
Layer 3: 本地 Zustand 持久化
useTerminalStore → 用 zustand/middleware/persist 持久化到 sessionStorage
F5 刷新 → 從 sessionStorage 恢復 GenUI 卡片(UI 層快速恢復)
同時 → SSE 重連補齊 Server 端新事件
後端實作關鍵代碼:
# apps/api/src/api/v1/terminal.py 補充 Event Store 機制
import json
from src.core.redis_client import get_redis
async def stream_with_persistence(command_id: str, event_type: str, data: dict):
"""
SSE 事件輸出 + 同步寫入 Redis Event Store
確保 F5 刷新後可以 Replay
"""
redis = get_redis()
event_payload = {
"type": event_type,
"data": data,
"timestamp": now_taipei_iso()
}
# 寫入 Redis List(RPUSH append to right)
key = f"terminal:events:{command_id}"
await redis.rpush(key, json.dumps(event_payload))
await redis.expire(key, 3600) # 1 小時後自動清理
# 返回 SSE 格式字串
return f"id: {redis.llen(key)}\nevent: {event_type}\ndata: {json.dumps(data)}\n\n"
@router.get("/stream/{command_id}/replay")
async def replay_terminal_events(command_id: str, last_event_id: int = 0):
"""
從指定 ID 開始 Replay 錯過的事件(用於 F5 重連)
"""
redis = get_redis()
key = f"terminal:events:{command_id}"
# 取出 last_event_id 之後的所有事件
events = await redis.lrange(key, last_event_id, -1)
async def generate():
for i, event_json in enumerate(events):
event = json.loads(event_json)
yield f"id: {last_event_id + i + 1}\nevent: {event['type']}\ndata: {json.dumps(event['data'])}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
第四部分:整合依賴關係圖(執行順序)
┌─────────────────────────────────────────────────────────────────────┐
│ 整合執行優先序 │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ 🔴 P0 立即執行(本週,阻塞後續工作) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. 確認 Redis AOF 狀態(5min) │ │
│ │ 2. Worker terminationGracePeriodSeconds 修正(1h) │ │
│ │ 3. i18n 清零(4h)← 必須先於 ESLint Plugin 安裝 │ │
│ │ 4. ESLint i18n Plugin 安裝並啟用(1h) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓(完成後解鎖) │
│ │
│ 🟠 P1 短期(2-3週) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 5. Sentry Comment Token 配置(2h) │ │
│ │ 6. SignOz 告警規則部署到 .188(2h) │ │
│ │ 7. Worker HPA YAML 部署(30min) │ │
│ │ 8. E2E CI Weekly 排程(30min) │ │
│ │ 9. Visual Regression Docker 基準線建立(2h) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓(完成後解鎖) │
│ │
│ 🟡 P2 中期(Month 2) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 10. Omni-Terminal SSE Event Sourcing(8h) │ │
│ │ 11. Storybook 10 心組件(8h) │ │
│ │ 12. Nexus AI 自治率 UI(8h) │ │
│ │ 13. FinOps Dashboard UI(8h) │ │
│ │ 14. Redis Sentinel 部署(1天) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↓(完成後解鎖) │
│ │
│ ⚪ P3 長期(Q2-Q3) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 15. CloudNativePG 評估與導入(2天+) │ │
│ │ 16. Kali SecurityAgent(MCP Tool 化) │ │
│ │ 17. Knowledge Base 後端全建 │ │
│ │ 18. Phase 4 視覺靈魂注入 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
第五部分:整合風險矩陣
| 風險 | 可能性 | 影響 | 緩解措施 |
|---|---|---|---|
| ESLint 啟用後大量報錯 | 100%(有 40+ 違規) | CI 完全阻塞 | 先清零再啟用,正確順序執行 |
| Worker timeout 修改引發 Pod 啟動異常 | 低 | 服務中斷 | 先在 Dev namespace 測試 |
| Redis AOF 啟用影響性能 | 中 | 延遲微增 | 使用 appendfsync everysec(非 always) |
| Visual Regression Docker 第一次 Baseline | — | 需要 1-2h 產生基準線 | 排在非尖峰時段執行 |
| PostgreSQL 無 HA 期間主庫故障 | 低 | 完全停機 | 備份策略(Velero)+ Playbook 就位 |
| SSE Event Sourcing Redis 依賴 | — | Redis 故障時 Event 丟失 | 先解決 Redis AOF,再實作 Event Sourcing |
第六部分:監控機制與前端的整合設計原則
(承接 MONITORING_ARCHITECTURE_DEEP_DIVE.md 的「三義分離原則」)
整合設計的核心約束:
1. 監控數據 → 後台靜默消化,不直接呈現給統帥
└─ 99%:Prometheus/SigNoz/Sentry 原始數據
└─ 1%:AI 無法自動處理 → 浮現為 ApprovalCard
2. 前端不直接查詢 Prometheus/SigNoz
└─ 所有監控數據透過 AWOOOI API 統一封裝
└─ API 層:/api/v1/stats/overview, /api/v1/slo, /api/v1/finops
3. 深度調查只能透過「智能跳脫」(新分頁)
└─ GenUI 卡片提供 ExternalLinks 按鈕
└─ 絕對禁止 iframe 嵌入 Grafana/SigNoz
4. AI 自治率指數是前端唯一的「監控摘要」入口
└─ Dashboard (/) 最頂部:AutonomyIndexPanel
└─ 用一個數字替代所有圖表
「整合不是把所有工具接在一起,而是讓所有工具服務同一個大腦。」 🦞