From 7bd96ef092f452930206c600b9a4cb0ac37073da Mon Sep 17 00:00:00 2001 From: Hank Date: Thu, 29 Oct 2026 12:57:13 +0800 Subject: [PATCH] =?UTF-8?q?docs(analysis):=20=E6=96=B0=E5=A2=9E=20K8s=20?= =?UTF-8?q?=E6=9C=AC=E6=A9=9F=E6=A8=A1=E6=93=AC=E5=BE=85=E8=BE=A6=E8=A8=98?= =?UTF-8?q?=E9=8C=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- .../2026-10-29-k8s-local-simulation-todo.md | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/analysis/2026-10-29-k8s-local-simulation-todo.md diff --git a/docs/analysis/2026-10-29-k8s-local-simulation-todo.md b/docs/analysis/2026-10-29-k8s-local-simulation-todo.md new file mode 100644 index 0000000..8d6c802 --- /dev/null +++ b/docs/analysis/2026-10-29-k8s-local-simulation-todo.md @@ -0,0 +1,132 @@ +# CFDivePlatform K8s 本機模擬待辦 + +> 記錄日期:2026-10-29 +> 狀態:**僅記錄,尚未規劃、尚未動工**(Hank 指示「先記待辦」)。 +> 範圍:在本機以 Docker Desktop Kubernetes 模擬部署,驗證 manifest 與流程;`docker-compose.yml` 維持本地開發用,不更動。 +> 實作原則:涉及行為或部署流程變更,先走 OpenSpec(`/opsx:propose`);本文的決策與清單是規劃輸入,不是 spec。 +> 搭配閱讀:`docs/analysis/2026-10-14-project-review-todo.md`(M6、M8 與本項有相依)。 + +--- + +## 已做的決策(2026-10-29 討論) + +| 項目 | 決策 | 理由 | +|---|---|---| +| 模擬環境 | Docker Desktop 內建 Kubernetes,**Kubeadm(單節點)模式** | 目的是驗證 manifest 與部署流程,不需要多節點;設定最簡單 | +| 與 Compose 的關係 | Compose 照舊作為本地開發環境,K8s 另放獨立目錄(暫定 `k8s/`) | 兩邊互不影響,避免動到現有 `deploy.yml` | +| image | 沿用現有 `Dockerfile` 的 `app`、`web` 兩個 target | 與正式環境的 compose(不含 `compose.override.yml`)同一種形態,落差最小 | +| 隔離 | 每個專案一個 namespace | 另一個 .NET 專案會一起模擬 | +| 上機器的時機 | 本機模擬驗證完成後,再決定實際叢集 | VPS 資源不足,見下節 | + +## 為什麼不直接在現有 VPS 上裝 K3s + +2026-10-29 實測:2 核、1.9GB RAM(可用約 770MB)、磁碟剩 9.2GB(72%),且已跑 13 個容器(含 Gitea、Gitea runner、Nginx Proxy Manager)。 +K3s 單節點自身約需 0.5–1GB,會讓 OOM 風險落在現有正式站與 CI。若之後要上實體叢集,建議另租至少 2 核 4GB、40GB 磁碟的機器。 + +--- + +## 總覽 + +| # | 項目 | 依賴 | 是否走 OpenSpec | +|---|------|------|----------------| +| K1 | 決定 Ingress controller | — | 否(決策) | +| K2 | 盤點 CFDive 的 K8s 資源清單 | — | 否(盤點) | +| K3 | 撰寫 CFDive manifest(`k8s/`) | K1、K2 | 是 | +| K4 | 本機部署與驗證 | K3 | 是(含驗證項目) | +| K5 | 另一個 .NET 專案納入同一叢集 | K1、K4 | 另案(該專案的 repo) | +| K6 | 上傳檔案切 S3/R2 | 外部帳號 | 是(既有暫緩項目) | +| K7 | 上實體叢集前的差距清單 | K4 | 否(文件) | + +--- + +## K1 決定 Ingress controller + +**現況** +Docker Desktop 沒有內建 Ingress。K3s 預設是 Traefik;原本考慮的 ingress-nginx,**據我所知 Kubernetes 社群已宣布它在 2026 年 3 月退役,此點為記憶,尚未查證**。 + +**待辦** +- [ ] 查證 ingress-nginx 目前的維護狀態(官方公告) +- [ ] 在 Traefik 與 Gateway API 實作之間決定(暫定傾向 Traefik:與 K3s 預設一致,之後搬 K3s 的 manifest 改動最少) +- [ ] 確認 Reverb(WebSocket)在所選 Ingress 上的路由與逾時設定 + +## K2 盤點 CFDive 的 K8s 資源清單 + +**現況(依 `docker-compose.yml` 服務名稱,2026-10-29 核對)** +`app`、`nginx`、`frontend`、`db`、`scheduler`、`queue-worker`、`redis`、`reverb`。volumes 為 `mysql-data`、`redis-data`、`storage-app-public`。 + +**暫定對應(未驗證)** +- `app`、`nginx`、`frontend`、`queue-worker`、`reverb`:各一個 Deployment+必要的 Service +- `scheduler`:Deployment,**副本固定 1**(多副本會讓排程重複執行) +- migration/cache/swagger:目前在 `deploy.yml`,需改為 Job(或 initContainer) +- `.env`:拆成 ConfigMap(非機密)與 Secret(機密);`env_file:` 在 K8s 沒有對等物 +- `db`、`redis`:本機模擬時先決定放叢集內,或沿用 Compose 容器經 `host.docker.internal` 連線 + +**待辦** +- [ ] 逐一核對各服務的 port、healthcheck、啟動指令、依賴順序 +- [ ] 決定 MySQL/Redis 放叢集內還是沿用 Compose(叢集內較貼近正式環境,Compose 較省事) +- [ ] 注意 `tests/bootstrap.php` 的覆蓋清單:擴大環境變數注入範圍時需同步檢查(見專案記憶中 `env_file` 事件) + +## K3 撰寫 CFDive manifest + +**待辦** +- [ ] 目錄結構(`k8s/` 下用 Kustomize 或純 YAML,擇一) +- [ ] 各 Deployment 的探針(readiness/liveness)對應現有 healthcheck +- [ ] 加上 `requests`/`limits`,總量抓接近目標機器(例如 4GB 節點),避免本機資源充裕而漏掉 OOM 問題 +- [ ] Secret 結構與正式環境相同,本機只用假值 +- [ ] 環境值用正式設定:`APP_ENV=production`、`APP_DEBUG=false`、MySQL `8.0`、Redis `alpine` +- [ ] 不帶入 `compose.override.yml` 的 bind mount + +## K4 本機部署與驗證 + +**待辦** +- [ ] Docker Desktop 啟用 Kubernetes(Kubeadm 模式),確認 `kubectl` 指向正確 context +- [ ] 本機 build `app`、`web`、`frontend` image,`imagePullPolicy` 設為 `IfNotPresent`(或 `Never`) +- [ ] 驗證:登入、預約、評價主流程;Reverb 即時訊息(WebSocket 經 Ingress);queue job 實際被消化;scheduler 只有一個實例 +- [ ] 驗證:migration Job 失敗時的行為(不應讓新版本 Pod 起來) +- [ ] 驗證:滾動更新與回滾(`kubectl rollout`) +- [ ] 記錄與 Compose 行為不一致之處 + +## K5 另一個 .NET 專案納入同一叢集 + +**注意事項(屬該專案 repo,這裡只記備忘)** +- ASP.NET Core(.NET 8 以後)官方 image 預設非 root、監聽 8080,Service 的 `targetPort` 要對 +- 巢狀設定以環境變數覆蓋時用雙底線,例如 `ConnectionStrings__Default` +- 需自行提供健康檢查端點供探針使用 +- EF migration 用 Job,不要在應用啟動時跑 +- 多副本時需處理 Data Protection 金鑰共用,否則副本間 cookie/antiforgery 互相無法解密 + +**待辦** +- [ ] 兩個專案用不同 namespace 與 hostname(例如 `cfdive.localhost`、`other.localhost`),共用同一個 Ingress controller + +## K6 上傳檔案切 S3/R2 + +**現況** +`storage:migrate-to-s3` 指令與 `FILESYSTEM_DISK` 設定已完成(PR #55、#56),但 R2/S3 帳號尚未申請,VPS 仍是 `FILESYSTEM_DISK=local`,上傳檔案存在具名 volume `storage-app-public`。 +多副本時 ReadWriteMany 儲存是最大阻礙;**本機 hostpath 單機無法代表這個問題**,所以這項要在上實體叢集前完成。 + +**待辦** +- [ ] 取得 S3 相容服務帳號後,另開 OpenSpec change 接續(不要回頭改已歸檔的 `cloud-ready-s3-storage`) + +## K7 上實體叢集前的差距清單 + +本機模擬**無法**驗證的項目,上實體叢集後需另外確認: + +| 項目 | 原因 | +|---|---| +| 多節點調度、節點故障 | Kubeadm 模式為單節點 | +| 檔案儲存的 ReadWriteMany | 本機 hostpath 僅單機 | +| TLS/憑證、真實網域 | 本機用 `*.localhost`,無 Let's Encrypt | +| 真實流量與資源競爭 | 本機資源充裕 | +| Gitea Actions 到叢集的部署流程 | 需等有目標叢集;另需 image registry(目前 image 在 VPS 上 build,是否有 registry 未確認) | + +**待辦** +- [ ] 確認 VPS 現行部署是否有 image registry;沒有的話,上叢集前要決定放哪裡 +- [ ] 決定實體叢集(另租 K3s 機器,或雲端託管) + +--- + +## 開放問題 + +- 本機機器規格(Docker Desktop 分配給 WSL2 的記憶體)未確認;兩個專案加 MySQL/Redis 同時跑,建議 8GB 以上 +- Kubeadm 模式下本機 image 是否可直接被 Pod 使用,依記憶判斷為可,**尚未在實際版本驗證** +- Ingress 最終選哪個(K1)