248 lines
15 KiB
Markdown
248 lines
15 KiB
Markdown
# AWOOOI 主機角色與負載平衡評估
|
||
|
||
> Version: v1.1
|
||
> Last updated: 2026-06-13 Asia/Taipei
|
||
> Scope: 110 / 120 / 121 / 188;112 是 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 仍集中在 120,Web 已分散 |
|
||
|
||
專業判定:
|
||
|
||
- `控制面`:AA。
|
||
- `業務 workload`:Web 已分散,但 API 兩個 replica 都在 120,不算完整 workload AA。
|
||
- `資料層`:不可直接視為 AA;若仍依賴 188 PostgreSQL 或 local-path PVC,120/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 check:110 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 check:110 `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 check:188 RAM `62Gi`、root disk `52%`、Swap 約 `2Gi/8Gi`;top CPU 包含 `momo-scheduler`、Next.js、ClickHouse。
|
||
|
||
2026-06-13 00:14 live check:188 `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 / 121;Worker 單副本在 121 | AWOOOI core workload 已完成第一階段均衡 |
|
||
| 記憶體餘裕 | 120 約 `29Gi` available;121 約 `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,再搬 app;DB/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 PVC,Pod 可重建不代表資料可 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` 兩顆副本合法同落 120;cold-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 兩顆都排到 121,scheduler 當下因舊 Pod 尚在而視為不違反 skew;舊 Pod 消失後 live API 反而變成 121 集中。因此 API / Web 也必須使用 `maxSurge: 0`、`maxUnavailable: 1`,讓 rollout 先釋出一格再排新 Pod,避免新舊 ReplicaSet 交錯造成最終偏斜。
|
||
|
||
2026-06-13 12:59 live verification:ArgoCD revision `60f653a0` synced;API / 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=2:120 / 121 各一顆,否則是 `SERVICE_OK_PLACEMENT_DRIFT`。
|
||
- `awoooi-web` replicas=2:120 / 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 AA;workload 尚未均衡 |
|
||
| 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 live;API/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 層,不搬 DB;DB 仍走既有備份與連線 | 降低變更風險 |
|
||
| 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。
|