# AWOOOI 部署契約 (Deployment Contracts)

> **版本**: v1.0
> **建立日期**: 2026-03-20
> **負責人**: CIO
> **強制等級**: 施工前必須遵守

---

## 概述

此文件定義 AWOOOI 部署的「鐵律級」配置規範。
**施工前必須確認此契約，否則禁止開始基建工作。**

---

## 環境架構 (CEO 指示 #3)

> ⚠️ **重要**: AWOOOI 只有兩個環境，不設 UAT

| 環境 | 用途 | 域名 | K8s Namespace | 備註 |
|------|------|------|---------------|------|
| **Dev** | 本機開發 | `localhost:3000` | - | 開發者本機 |
| **Prod** | 生產環境 | `awoooi.wooo.work` | `awoooi-prod` | 唯一線上環境 |

### 與舊系統完全隔離

| 項目 | AWOOOI (新) | Legacy (舊) |
|------|-------------|-------------|
| 域名 | `awoooi.wooo.work` | `aiops.wooo.work` |
| Namespace | `awoooi-prod` | `wooo-aiops` |
| Frontend Port | 32335 | 31235 |
| API Port | 32334 | 31234 |
| OpenClaw Port | 8089 | 8088 |
| Redis DB | 10-15 | 0-9 |

---

## CIO-001: K8s Namespace 資源配額

> 🎯 **顧問深度討論 #3**: 防止 Memory Leak 拖垮叢集

### ResourceQuota 配置

```yaml
# k8s/quotas/awoooi-prod-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: awoooi-prod-quota
  namespace: awoooi-prod
spec:
  hard:
    # 計算資源上限 (叢集 40%)
    requests.cpu: "4"           # 4 cores
    requests.memory: "8Gi"      # 8GB
    limits.cpu: "8"             # 8 cores
    limits.memory: "16Gi"       # 16GB

    # Pod 數量限制
    pods: "50"

    # 儲存限制
    persistentvolumeclaims: "10"
    requests.storage: "100Gi"
```

### LimitRange 配置

```yaml
# k8s/quotas/awoooi-prod-limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: awoooi-prod-limits
  namespace: awoooi-prod
spec:
  limits:
    # 預設容器限制
    - type: Container
      default:
        cpu: "500m"
        memory: "512Mi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "2"
        memory: "4Gi"
      min:
        cpu: "50m"
        memory: "64Mi"

    # Pod 總限制
    - type: Pod
      max:
        cpu: "4"
        memory: "8Gi"
```

### 強制規則

1. **新 Deployment 必須指定 resources**: 沒有 requests/limits 將被拒絕
2. **禁止 BestEffort QoS**: 所有 Pod 必須有明確資源定義
3. **定期檢查**: 每週檢查資源使用率，超過 70% 發出告警

---

## CIO-002: Nginx SSE 長連線配置

> 🎯 **顧問深度討論 #2**: 防止 SSE 每 60 秒斷線

### Nginx 配置範本

```nginx
# k8s/nginx/awoooi-prod.conf

# 上游服務定義
upstream awoooi-api {
    server awoooi-api-service:8000;
    keepalive 32;
}

upstream awoooi-web {
    server awoooi-web-service:3000;
    keepalive 16;
}

server {
    listen 443 ssl http2;
    server_name awoooi.wooo.work;

    # SSL 配置
    ssl_certificate /etc/nginx/ssl/awoooi.crt;
    ssl_certificate_key /etc/nginx/ssl/awoooi.key;

    # === SSE 專用路由 (AI 思考串流) ===
    location ~ ^/api/v1/(agent|dashboard)/stream {
        proxy_pass http://awoooi-api;

        # ⚠️ 關鍵: SSE 必要配置
        proxy_buffering off;              # 禁用緩衝 (打字機效果零延遲)
        proxy_read_timeout 3600s;         # 1 小時長連線
        proxy_send_timeout 3600s;
        proxy_connect_timeout 60s;

        # HTTP/1.1 長連線
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header X-Accel-Buffering no;

        # 標準 Headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # === 一般 API 路由 ===
    location /api/ {
        proxy_pass http://awoooi-api;

        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # === 前端靜態資源 ===
    location / {
        proxy_pass http://awoooi-web;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 健康檢查 (不經認證)
    location /health {
        proxy_pass http://awoooi-api/health;
        proxy_read_timeout 5s;
    }
}
```

### SSE 測試腳本

```bash
#!/bin/bash
# scripts/test-sse.sh
# 測試 SSE 連線是否正常

echo "Testing SSE connection to awoooi.wooo.work..."

timeout 120 curl -N \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $TOKEN" \
  "https://awoooi.wooo.work/api/v1/agent/stream?prompt=test"

if [ $? -eq 124 ]; then
  echo "✅ SSE connection held for 2 minutes (test passed)"
else
  echo "❌ SSE connection dropped unexpectedly"
  exit 1
fi
```

---

## CIO-003: NetworkPolicy 零信任邊界

> 🎯 **顧問深度討論 #1**: Default Deny All 策略

### 預設拒絕策略

```yaml
# k8s/network-policies/default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: awoooi-prod
spec:
  podSelector: {}  # 套用到所有 Pod
  policyTypes:
    - Ingress
    - Egress
```

### 允許清單 - Ingress

```yaml
# k8s/network-policies/allow-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-nginx-to-services
  namespace: awoooi-prod
spec:
  podSelector:
    matchLabels:
      app: awoooi-api
  policyTypes:
    - Ingress
  ingress:
    # 只允許 Nginx Ingress Controller
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8000

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-nginx-to-web
  namespace: awoooi-prod
spec:
  podSelector:
    matchLabels:
      app: awoooi-web
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 3000
```

### 允許清單 - Egress

```yaml
# k8s/network-policies/allow-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-services
  namespace: awoooi-prod
spec:
  podSelector:
    matchLabels:
      app: awoooi-api
  policyTypes:
    - Egress
  egress:
    # 允許訪問 PostgreSQL (192.168.0.188:5432)
    - to:
        - ipBlock:
            cidr: 192.168.0.188/32
      ports:
        - protocol: TCP
          port: 5432

    # 允許訪問 Redis DB 10-15 (192.168.0.188:6380)
    - to:
        - ipBlock:
            cidr: 192.168.0.188/32
      ports:
        - protocol: TCP
          port: 6380

    # 允許訪問 Ollama (192.168.0.188:11434)
    - to:
        - ipBlock:
            cidr: 192.168.0.188/32
      ports:
        - protocol: TCP
          port: 11434

    # 允許訪問 OpenClaw AWOOOI (192.168.0.188:8089)
    - to:
        - ipBlock:
            cidr: 192.168.0.188/32
      ports:
        - protocol: TCP
          port: 8089

    # 允許訪問 Kali Scanner (192.168.0.112:8080)
    - to:
        - ipBlock:
            cidr: 192.168.0.112/32
      ports:
        - protocol: TCP
          port: 8080

    # 允許 DNS 解析
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

    # 允許訪問外部 AI API (雲端備援)
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - protocol: TCP
          port: 443
```

### 禁止訪問 Legacy Namespace

```yaml
# k8s/network-policies/deny-legacy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-access-to-legacy
  namespace: awoooi-prod
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    # 明確拒絕 Legacy Namespace
    - to:
        - namespaceSelector:
            matchLabels:
              name: wooo-aiops
      # 沒有 ports = 全部拒絕
```

---

## 監控與告警配置

### Prometheus 告警規則

```yaml
# k8s/monitoring/prometheus/awoooi-alerts.yaml
groups:
  - name: awoooi-resource-alerts
    rules:
      # CPU 使用率告警
      - alert: AWOOOIHighCPUUsage
        expr: |
          sum(rate(container_cpu_usage_seconds_total{namespace="awoooi-prod"}[5m]))
          / sum(kube_resourcequota{namespace="awoooi-prod", resource="limits.cpu"})
          > 0.7
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "AWOOOI CPU 使用率超過 70%"
          description: "Namespace awoooi-prod 的 CPU 使用率已達 {{ $value | humanizePercentage }}"

      # Memory 使用率告警
      - alert: AWOOOIHighMemoryUsage
        expr: |
          sum(container_memory_working_set_bytes{namespace="awoooi-prod"})
          / sum(kube_resourcequota{namespace="awoooi-prod", resource="limits.memory"})
          > 0.7
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "AWOOOI Memory 使用率超過 70%"

      # Pod 重啟告警
      - alert: AWOOOIPodRestarting
        expr: |
          increase(kube_pod_container_status_restarts_total{namespace="awoooi-prod"}[1h]) > 3
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "AWOOOI Pod 頻繁重啟"
          description: "Pod {{ $labels.pod }} 在過去 1 小時重啟超過 3 次"
```

---

## 驗收清單

### 施工前確認

- [ ] ResourceQuota 已套用
- [ ] LimitRange 已套用
- [ ] Default Deny NetworkPolicy 已套用
- [ ] 允許清單 NetworkPolicy 已套用
- [ ] Nginx SSE 配置已驗證
- [ ] 告警規則已部署

### 施工後驗證

```bash
# 驗證 ResourceQuota
kubectl describe quota awoooi-prod-quota -n awoooi-prod

# 驗證 LimitRange
kubectl describe limitrange awoooi-prod-limits -n awoooi-prod

# 驗證 NetworkPolicy
kubectl get networkpolicy -n awoooi-prod

# 測試 SSE 連線
./scripts/test-sse.sh

# 測試 Legacy 隔離
kubectl exec -it deploy/awoooi-api -n awoooi-prod -- \
  curl -s http://wooo-aiops-api.wooo-aiops:8000/health
# 預期: 連線失敗 (被 NetworkPolicy 阻擋)
```

---

## 變更記錄

| 日期 | 版本 | 變更 | 作者 |
|------|------|------|------|
| 2026-03-20 | v1.0 | 初版建立 | CIO |

---

*此文件由 CIO 維護，基建施工前必須完整遵守。*
