Files
awoooi/docs/runbooks/HOST-ROLE-LOAD-BALANCING-ASSESSMENT.md

248 lines
15 KiB
Markdown
Raw 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.
# AWOOOI 主機角色與負載平衡評估
> Version: v1.1
> Last updated: 2026-06-13 Asia/Taipei
> Scope: 110 / 120 / 121 / 188112 是 Kali不納入本輪服務搬遷。
---
## 1. 結論
120 / 121 目前是 K3s control-plane AA不是傳統一主一從 AS。
但這只代表 Kubernetes 控制面雙節點可用,不代表業務 workload 已經 AA。2026-06-13 00:13 live check 顯示:
| 項目 | 120 `mon` | 121 `mon1` | 判讀 |
|------|-----------|------------|------|
| K3s unit | `k3s active``k3s-agent inactive` | `k3s active``k3s-agent inactive` | 兩台都是 server / control-plane |
| Kubernetes role | `control-plane` | `control-plane` | 控制面 AA |
| Taint / schedulable | 無 taint、`Unschedulable=false` | 無 taint、`Unschedulable=false` | 120 可以排程 |
| CPU / memory | CPU 約 `2%`、Memory 約 `8%` | CPU 約 `3%`、Memory 約 `20%` | 兩台都有餘裕121 較高 |
| Pod 實際落點 | `awoooi-api` 2 顆、`awoooi-web` 1 顆、`awoooi-worker` 1 顆 | `awoooi-web` 1 顆、auto-repair canary、部分 CronJob 歷史 Pod | API 仍集中在 120Web 已分散 |
專業判定:
- `控制面`AA。
- `業務 workload`Web 已分散,但 API 兩個 replica 都在 120不算完整 workload AA。
- `資料層`:不可直接視為 AA若仍依賴 188 PostgreSQL 或 local-path PVC120/121 只是承載層,不是資料高可用層。
- `下一步`AWOOOI API / Web / Worker topology spread 已進 Gitea main 並由 ArgoCD 套用;後續才能規劃把 110 / 188 無狀態服務搬進 K3s不先搬核心資料庫。
---
## 2. 現況負載與角色
### 2.1 110
110 是目前最混合、最需要拆角色的主機。
| 類別 | Live evidence | 負載/風險 |
|------|---------------|-----------|
| Public gateway / edge | `80/443`、多個 public route upstream | 改 Nginx / firewall 影響面最大 |
| Registry / image supply | Harbor `5000`、registry / portal / jobservice / db / redis | K3s image pull 依賴它,不適合先搬 |
| Source / CI | Gitea `3001`、runner / build 相關目錄 | build / dump 期間會推高 CPU |
| Observability / incident tools | Prometheus、Alertmanager、Sentry / Snuba / Kafka / ClickHouse | 容器多、啟動順序複雜 |
| Product services | StockPlatform v2、agent-bounty、Tsenyang、VTuber、Bitan 等 | 多數可逐步轉 K3s但資料層需先分離 |
| Backup control | `/backup/scripts/*`、每日備份與 offsite sync | 本輪恢復完成前不可搬 |
2026-06-12 15:19 live check110 RAM `62Gi`、root disk `89%`、load 約 `2.79 / 3.01 / 3.25`;正在跑 Gitea dump 時 top CPU 包含 `gitea dump`、ClickHouse、Docker、Kafka。
2026-06-13 00:14 live check110 `systemctl is-system-running=running`、failed units `0`、load 約 `4.45 / 3.28 / 2.89`、Swap `166Mi/7.8GiB`top CPU 主要是 Gitea、ClickHouse、Kafka、Docker 與一個 `prisma db push` 工作。判讀110 不在服務故障狀態,但仍是高混合負載主機,第一批應先移走無狀態 app 層與 build/preview 類工作。
### 2.2 188
188 是資料、AI、觀測與自動化中心。
| 類別 | Live evidence | 負載/風險 |
|------|---------------|-----------|
| Data layer | PostgreSQL `5432`、Redis `6379`、momo DB、product DBs | 多服務依賴,不可直接熱搬 |
| momo | `momo-scheduler``momo-pro-system`、Telegram bot | 有資料一致性與匯入流程風險 |
| AI / workflow | OpenClaw、LiteLLM、Open WebUI、n8n | 可拆,但需 provider route / secrets / queue 邊界 |
| Observability | SignOz、ClickHouse、Zookeeper、Grafana、Prometheus、Alertmanager、Loki | stateful 且啟動重,不適合第一批 |
| Product services | VibeWork、Tsenyang、agent-bounty 等 | 可按 stateless / stateful 拆分 |
2026-06-12 15:19 live check188 RAM `62Gi`、root disk `52%`、Swap 約 `2Gi/8Gi`top CPU 包含 `momo-scheduler`、Next.js、ClickHouse。
2026-06-13 00:14 live check188 `systemctl is-system-running=degraded`failed units 是 `certbot.service``snap.certbot.renew.service`cold-start 仍確認 PostgreSQL、Redis、momo、SignOz、Docker services 綠。判讀:這是 TLS 自動續期治理債,不是本輪 K3s workload balancing 的業務服務阻斷。
### 2.3 120 / 121
120 / 121 是 K3s control-plane pair目前最適合承接標準化容器 workload。
| 類別 | 目前狀態 | 判讀 |
|------|----------|------|
| K3s control-plane | 120 / 121 都 `Ready control-plane` | 控制面 AA |
| Workload placement | API / Web 已分散在 120 / 121Worker 單副本在 121 | AWOOOI core workload 已完成第一階段均衡 |
| 記憶體餘裕 | 120 約 `29Gi` available121 約 `27Gi` available | 有承接空間 |
| Storage | root 各約 490G本輪 120 剛做 root fsck 後恢復 | 不應立刻承接核心 stateful |
| Network | VIP `192.168.0.125` / NodePort 已用於 AWOOOI | 適合承接 ingress 後方 app |
---
## 3. 可以搬、暫緩搬、禁止直接搬
### 3.1 第一批:可優先搬到 K3s 的服務
條件:無狀態、可重新 build、已有容器 image、資料庫可先留在原 host、health endpoint 清楚。
| 優先序 | 候選 | 來源主機 | 建議策略 | 原因 |
|--------|------|----------|----------|------|
| P1-A | Tsenyang website | 110 / 188 都有部署痕跡 | 先做單一 canonical deployment再進 K3s | 靜態/Next 類服務,資料風險低 |
| P1-A | VTuber web/admin/api | 110 | app 先搬DB 留 110備份通過後再評估 DB | 可降低 110 Docker container 數 |
| P1-A | Bitan app / PostgREST edge | 110 | app edge 先搬PostgreSQL 暫留 110 | public route 可用 K3s NodePort / ingress |
| P1-B | StockPlatform web/admin/api/edge | 110 | app 層先搬Postgres/Redis 暫留 110 | 目前 110 服務多,拆 app 有效 |
| P1-B | agent-bounty web / worker | 110 / 188 | 先統一來源與 port再搬 appDB/Redis 暫留 | 兩台都有部署痕跡,需先消除重複角色 |
| P1-B | 輕量內部工具 / preview | 188 | 容器標準化後搬 | 對核心資料面影響小 |
第一批完成標準:
- 每個 deployment 有 `requests` / `limits`
- replicas >= 2 的服務必須有 pod anti-affinity 或 topology spread。
- 120 / 121 上 Pod 落點 max skew <= 1。
- public route、internal VIP、health endpoint、Alertmanager visibility、備份矩陣都通過。
### 3.2 第二批:可以規劃,但不能在恢復期直接搬
| 候選 | 來源主機 | 需要先完成 |
|------|----------|------------|
| Product-specific PostgreSQL / Redis | 110 / 188 | backup / restore drill、RPO/RTO、PVC 策略、回滾路徑 |
| Langfuse / analytics 類服務 | 110 | DB / object storage / secret route 梳理 |
| n8n / workflow | 188 | workflow export、credential escrow evidence、callback URL 切換 |
| Open WebUI / AI 工具 | 188 | model provider route、volume backup、外部 endpoint fallback |
| Docker registry mirror / cache | 110 / 188 | image retention、auth、K3s pull secret、Harbor 分工 |
### 3.3 不建議直接搬的核心
| 對象 | 原因 | 正確路徑 |
|------|------|----------|
| Harbor | K3s image supply 依賴它;搬壞會反過來讓 K3s deploy 失敗 | 先做 replica / mirror / restore 演練 |
| Gitea | source-of-truth / CI 影響大dump / runner / SSH / webhook 邊界多 | 先獨立備份與 restore drill |
| Sentry / Snuba / Kafka / ClickHouse | stateful、容器多、啟動順序敏感 | 先拆 consumer load再評估專用 observability host |
| SignOz ClickHouse / Zookeeper | stateful 且重 IO | 需要獨立 storage 與 retention plan |
| AWOOOI / momo 主 PostgreSQL | 多系統依賴且資料一致性要求高 | 先建立 replica / tested restore不做熱搬 |
| `/backup` 控制面 | 本輪恢復與 DR 證據中心 | 等 DR scorecard complete 後另開遷移案 |
---
## 4. 新資料庫能不能放 120 / 121
可以,但要分等級。
| 等級 | 是否可放 | 條件 |
|------|----------|------|
| Dev / staging / demo DB | 可以 | 明確標示非 production每日備份可接受重建 |
| 新產品的低風險 production DB | 可以試點 | 必須先完成 backup、restore drill、PVC 保留策略、監控與 offsite sync |
| 核心 production DB | 暫不建議 | 目前 120/121 只有兩節點 control-plane若使用 local-path PVCPod 可重建不代表資料可 failover |
| 高可用 production DB | 需要另案 | 需要 PostgreSQL operator、replica、storage replication 或外部 DB HA不是單純 `kubectl apply postgres` |
判定原則:
```text
Kubernetes 可以重排 Pod不等於資料會跟著安全重排。
沒有 restore drill 的 DB不算可遷移完成。
沒有 storage replication 的 stateful workload不算 AA。
```
---
## 5. 120 / 121 Workload 均衡修正
目前不是 taint 或 cordon 問題,因為 120 / 121 都 schedulable。主要原因是 120 長時間不可用後,既有 Running Pod 不會自動搬回來live deployment 也尚未帶 `topologySpreadConstraints`,所以 scheduler 只有 soft anti-affinity仍可能把同一服務副本放在同一台。
### 5.1 立即可做的只讀檢查
```bash
ssh wooo@192.168.0.120 '
sudo kubectl get nodes -o wide
sudo kubectl get pods -A -o wide
sudo kubectl top nodes
sudo kubectl top pods -A --sort-by=cpu
'
```
### 5.2 受控修正順序
1. 等 P0 備份鏈、offsite verifier、cold-start scorecard 通過。
2. 在 AWOOOI `awoooi-prod` API / Web / Worker 補 `topologySpreadConstraints`
3. replicas >= 2 的服務使用 `topologyKey: kubernetes.io/hostname`,目標 120 / 121 分散。
4. 單副本服務先不強制搬,除非有明確重啟窗口。
5. 以 rolling restart 或下一次 deploy 觸發重排,不用暴力 delete 全部 Pod。
6. 觀察 `kubectl get pods -A -o wide`、public route、Alertmanager、cold-start scorecard。
### 5.3 2026-06-13 Gitea / ArgoCD Live 狀態
已在 Gitea main commit `acaae999 fix(k8s): add prod workload topology spread` 補上:
- `k8s/awoooi-prod/05-deployment-web.yaml`
- `k8s/awoooi-prod/06-deployment-api.yaml`
- `k8s/awoooi-prod/08-deployment-worker.yaml`
2026-06-13 12:43 live refresh 發現 `ScheduleAnyway` 仍可能讓 `awoooi-api` 兩顆副本合法同落 120cold-start 仍綠,但不能宣稱 API workload balanced。
修正策略改為 `maxSkew: 1``minDomains: 2``topologyKey: kubernetes.io/hostname``whenUnsatisfiable: DoNotSchedule`。120 / 121 都 Ready 時API / Web replicas=2 必須跨節點Worker 單副本仍可跑,未來擴副本時也必須分散。
2026-06-13 13:00 追加判讀:只把 spread 改硬仍不夠。API rollout 時舊 Pod 仍在 120 terminating新 ReplicaSet 兩顆都排到 121scheduler 當下因舊 Pod 尚在而視為不違反 skew舊 Pod 消失後 live API 反而變成 121 集中。因此 API / Web 也必須使用 `maxSurge: 0``maxUnavailable: 1`,讓 rollout 先釋出一格再排新 Pod避免新舊 ReplicaSet 交錯造成最終偏斜。
2026-06-13 12:59 live verificationArgoCD revision `60f653a0` syncedAPI / Web 均 `ready=2/2`strategy `maxSurge=0 / maxUnavailable=1`spread `minDomains=2 / DoNotSchedule``awoooi-api` pod 分別在 `mon` / `mon1``awoooi-web` pod 分別在 `mon` / `mon1`。完整 cold-start rerun `PASS=83 WARN=0 BLOCKED=0`
驗證:
- `kubectl kustomize k8s/awoooi-prod` 可渲染三個 `topologySpreadConstraints`
- `git diff --check` 通過。
- ArgoCD `awoooi-prod` 已同步到 revision `acaae99986aee2e1f5630984981ccb0f2b676bb8`operation `Succeeded`
- Live deployment 的 API / Web / Worker 均需有 `topologySpreadConstraints`
- `awoooi-api` replicas=2120 / 121 各一顆,否則是 `SERVICE_OK_PLACEMENT_DRIFT`
- `awoooi-web` replicas=2120 / 121 各一顆。
- `awoooi-worker` replicas=1可在任一節點若 replicas > 1必須跨 120 / 121 分散。
- Rollout 後冷啟動2026-06-13 00:34 final scorecard `PASS=83 WARN=0 BLOCKED=0`。00:33 曾因 110 `known_hosts` 對 120 / 188 過期而短暫 blocked已在備份後刷新 host key trust。
補充rolling update 當下 API 新 Pod 先集中到 121因為舊 Pod 正在 120 Terminating後續以單顆 stateless API Pod 受控重排,最終達到 120 / 121 各一顆。沒有執行全 namespace delete、Docker restart、Nginx reload 或 firewall 變更。
建議的分散目標:
| Workload | 目標 |
|----------|------|
| `awoooi-api` replicas=2 | 120 / 121 各一個,`whenUnsatisfiable=DoNotSchedule` |
| `awoooi-web` replicas=2 | 120 / 121 各一個,`whenUnsatisfiable=DoNotSchedule` |
| `awoooi-worker` replicas=1 | 可留任一節點;多副本時 `whenUnsatisfiable=DoNotSchedule` |
| ArgoCD / Velero / kube-system 單副本 | 不急搬;先保持穩定 |
| DaemonSet | 120 / 121 各一個,已符合 |
---
## 6. 遷移階段與完成度
| 階段 | 狀態 | 完成度 | 說明 |
|------|------|--------|------|
| A. 角色判定 | DONE | 100% | 120/121 已確認是 K3s control-plane AAworkload 尚未均衡 |
| B. 服務盤點 | DONE | 100% | 110 / 188 主要服務、port、container 類型已盤點 |
| C. 遷移分層 | DONE | 100% | 分為可優先搬、可規劃、禁止直接搬 |
| D. 實際 workload 均衡 | LIVE_VERIFIED | 100% | API / Web hard topology spread 與 no-surge rollout 已進 Gitea main / ArgoCD liveAPI/Web 皆 120 / 121 各一顆 |
| E. 第一批無狀態服務遷移 | NOT_STARTED | 0% | 需另開變更窗口與 rollback plan |
| F. Stateful 遷移 | BLOCKED_BY_DESIGN | 0% | 需要 storage / backup / restore / HA 設計 |
目前總結:評估完成 `100%`AWOOOI core workload 均衡實作 `100%`;第一批服務遷移實作 `0%`。下一階段仍只應先搬無狀態 app 層,不搬核心資料服務。
---
## 7. 建議優先順序
| 優先序 | 工作 | 目的 |
|--------|------|------|
| P0 | 完成 120 回復後備份鏈與 cold-start scorecard | 先證明平台回到可恢復基線 |
| P1 | 保持 AWOOOI prod topology spread觀察後續 deploy 是否維持 120 / 121 分散 | 防止 API/Web 再度單邊集中 |
| P1 | 建立「第一批無狀態服務」K3s 遷移清單與 rollback plan | 安全釋放 110 Docker 壓力 |
| P2 | 先搬 app 層,不搬 DBDB 仍走既有備份與連線 | 降低變更風險 |
| P2 | 對新 DB 建立 backup / restore drill 模板 | 讓未來新資料庫可安全落 K3s |
| P3 | 評估 Harbor / Gitea / Sentry / SignOz 的專用節點或 HA 架構 | 不把重 stateful 混進恢復期 |
---
## 8. 禁止事項
- 不在 P0 恢復鏈未完成時搬資料庫。
- 不用 `kubectl delete pod --all` 當成重平衡手段。
- 不把 local-path PVC 的 Pod 重建誤認為資料高可用。
- 不把 Harbor / Gitea / Sentry / SignOz 直接丟進 120 / 121 來「平均負載」。
- 不在未補 backup / restore drill 前宣稱新 DB production ready。
- 不因為 120 / 121 是 control-plane AA就宣稱整體服務 AA。