From be629feef978ae69c285412e8df0e366f3c06036 Mon Sep 17 00:00:00 2001 From: Hank Date: Thu, 18 Jun 2026 01:48:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(infra):=20=E9=9B=B2=E7=AB=AF=E5=B0=B1?= =?UTF-8?q?=E7=B7=92=E9=9B=B6=E5=BD=B1=E9=9F=BF=E6=94=B9=E5=8B=95=20(compo?= =?UTF-8?q?se=20overlay=20+=20healthcheck=20+=20env)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-compose.yml:app healthcheck 改用 nc -z 127.0.0.1 9000 驗證 PHP-FPM port - docker-compose.yml:移除 phpmyadmin、mailpit(開發工具不屬於正式服務) - compose.override.yml:新增,含 phpmyadmin(8081)、mailpit(8025)port mapping - .env.example:QUEUE_CONNECTION 預設改 redis;補充 FILESYSTEM_DISK、LOG_STACK、SESSION_DRIVER 雲端注意事項 - docs/STARTUP.md:新增「部署模式」章節,明確區分 VPS 與雲端 -f 指令 Co-Authored-By: Claude Sonnet 4.6 --- .env.example | 8 +- compose.override.yml | 27 +++++++ docker-compose.yml | 31 ++------ docs/STARTUP.md | 25 ++++++ .../cloud-ready-zero-impact/.openspec.yaml | 2 + .../changes/cloud-ready-zero-impact/design.md | 76 +++++++++++++++++++ .../cloud-ready-zero-impact/proposal.md | 27 +++++++ .../specs/compose-cloud-baseline/spec.md | 32 ++++++++ .../specs/env-cloud-annotations/spec.md | 19 +++++ .../changes/cloud-ready-zero-impact/tasks.md | 31 ++++++++ 10 files changed, 248 insertions(+), 30 deletions(-) create mode 100644 compose.override.yml create mode 100644 openspec/changes/cloud-ready-zero-impact/.openspec.yaml create mode 100644 openspec/changes/cloud-ready-zero-impact/design.md create mode 100644 openspec/changes/cloud-ready-zero-impact/proposal.md create mode 100644 openspec/changes/cloud-ready-zero-impact/specs/compose-cloud-baseline/spec.md create mode 100644 openspec/changes/cloud-ready-zero-impact/specs/env-cloud-annotations/spec.md create mode 100644 openspec/changes/cloud-ready-zero-impact/tasks.md diff --git a/.env.example b/.env.example index 8489a48..a138440 100644 --- a/.env.example +++ b/.env.example @@ -16,7 +16,7 @@ APP_MAINTENANCE_STORE=database BCRYPT_ROUNDS=12 LOG_CHANNEL=stack -LOG_STACK=daily +LOG_STACK=daily # 雲端建議改為 stderr 以接入雲端 logging aggregator LOG_DAILY_DAYS=14 LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug @@ -29,15 +29,15 @@ DB_CONNECTION=sqlite # DB_PASSWORD= # MYSQL_ROOT_PASSWORD= -SESSION_DRIVER=database +SESSION_DRIVER=database # 雲端建議改為 redis SESSION_LIFETIME=120 SESSION_ENCRYPT=false SESSION_PATH=/ SESSION_DOMAIN=null BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=database +FILESYSTEM_DISK=local # 雲端應改為 s3(local 在容器重啟後會遺失上傳檔案) +QUEUE_CONNECTION=redis # 雲端建議值;database 效能較差且不利擴容 CACHE_STORE=redis CACHE_PREFIX= diff --git a/compose.override.yml b/compose.override.yml new file mode 100644 index 0000000..b41be21 --- /dev/null +++ b/compose.override.yml @@ -0,0 +1,27 @@ +services: + phpmyadmin: + image: phpmyadmin/phpmyadmin + container_name: cfdive-phpmyadmin + restart: unless-stopped + ports: + - "127.0.0.1:8081:80" + environment: + PMA_HOST: db + PMA_PORT: 3306 + PMA_USER: ${DB_USERNAME:-cfdiveuser} + PMA_PASSWORD: ${DB_PASSWORD} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + networks: + - cfdive-network + depends_on: + db: + condition: service_healthy + + mailpit: + image: axllent/mailpit + container_name: cfdive-mailpit + restart: unless-stopped + ports: + - "127.0.0.1:8025:8025" + networks: + - cfdive-network diff --git a/docker-compose.yml b/docker-compose.yml index b38ad9b..2a62a7b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,11 +38,11 @@ services: # 健康檢查配置 healthcheck: - test: ["CMD", "php", "-v"] # 檢查 PHP 版本確認服務正常 - interval: 30s # 每30秒檢查一次 - timeout: 10s # 超時時間10秒 - retries: 3 # 重試3次 - start_period: 40s # 容器啟動後40秒開始檢查 + test: ["CMD-SHELL", "nc -z 127.0.0.1 9000 || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s nginx: image: nginx:alpine @@ -109,21 +109,6 @@ services: ports: - "127.0.0.1:3306:3306" # 只綁 localhost,不對外 - phpmyadmin: - image: phpmyadmin/phpmyadmin - container_name: cfdive-phpmyadmin - restart: unless-stopped - environment: - PMA_HOST: db - PMA_PORT: 3306 - PMA_USER: ${DB_USERNAME:-cfdiveuser} - PMA_PASSWORD: ${DB_PASSWORD} - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - networks: - - cfdive-network - depends_on: - db: - condition: service_healthy queue-worker: image: cfdive-platform @@ -146,12 +131,6 @@ services: app: condition: service_healthy - mailpit: - image: axllent/mailpit - container_name: cfdive-mailpit - restart: unless-stopped - networks: - - cfdive-network redis: image: redis:alpine diff --git a/docs/STARTUP.md b/docs/STARTUP.md index 766283e..14215d8 100644 --- a/docs/STARTUP.md +++ b/docs/STARTUP.md @@ -1,5 +1,30 @@ # CFDivePlatform 啟動指令 +## 部署模式 + +專案根目錄有兩個 compose 檔: + +| 檔案 | 用途 | +|------|------| +| `docker-compose.yml` | 正式服務(app、nginx、frontend、db、redis、reverb、queue-worker) | +| `compose.override.yml` | 開發工具(phpmyadmin、mailpit),已進版控 | + +**本機 / VPS(預設)**:Docker Compose 自動合併兩檔,開發工具一同啟動: + +```bash +docker compose up -d +``` + +**雲端正式環境**:明確指定單一檔案,排除開發工具: + +```bash +docker compose -f docker-compose.yml up -d +``` + +> VPS 首次部署或更新後執行 `git pull` 即可取得最新的 `compose.override.yml`,不需手動建立。 + +--- + ## 專案位置 ```powershell diff --git a/openspec/changes/cloud-ready-zero-impact/.openspec.yaml b/openspec/changes/cloud-ready-zero-impact/.openspec.yaml new file mode 100644 index 0000000..3ac681e --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-17 diff --git a/openspec/changes/cloud-ready-zero-impact/design.md b/openspec/changes/cloud-ready-zero-impact/design.md new file mode 100644 index 0000000..78c50c1 --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/design.md @@ -0,0 +1,76 @@ +## Context + +CFDivePlatform 跑在單台 VPS 上,全部服務以 Docker Compose 管理。現有設定有三個雲端就緒問題: +1. `app` service healthcheck 用 `php -v`,只驗證 binary 存在,無法確認 PHP-FPM 是否監聽 +2. `phpmyadmin`、`mailpit` 這兩個開發/管理工具寫死在 `docker-compose.yml`,雲端正式環境不應存在 +3. `.env.example` 的 `QUEUE_CONNECTION=database` 與 VPS 實際使用 Redis 不一致,新環境按範本初始化會跑在效能較差的 DB queue + +本次只改 compose 設定和 `.env.example`,不觸碰任何 PHP 程式碼。 + +## Goals / Non-Goals + +**Goals:** +- healthcheck 改為確認 PHP-FPM FastCGI port 9000 正常監聽,比 `php -v` 更能反映 FPM 是否就緒 +- 透過 `compose.override.yml` 機制隔離開發工具,VPS 行為不變,雲端部署只需 `-f docker-compose.yml` +- `.env.example` 的 queue 預設值與雲端慣例對齊,並補充雲端必要變數的說明 + +**Non-Goals:** +- 不修改 Session driver(留待 P1 change) +- 不修改 Log 輸出方式(留待 P1 change) +- 不修改 Scheduler 架構(留待 P1 change) +- 不改任何 PHP 程式碼或 image 內容 +- 不處理 S3 檔案儲存(P0 項目,需獨立 change) + +## Decisions + +### D1:healthcheck 用 `nc -z 127.0.0.1 9000` 檢查 PHP-FPM port + +**為什麼不用 `/health` HTTP 端點:** +app container 跑的是 PHP-FPM(FastCGI,port 9000),容器內**沒有 HTTP server**。 +`/health` 路由必須經過 Nginx 才能到達 PHP-FPM,而 Nginx 是另一個 container。 +所以 `curl` 或 `wget` 打 `http://127.0.0.1/health` 在 app container 內部完全打不到任何東西。 + +**為什麼不用 `wget`:** +Dockerfile 安裝清單確認 `wget` 未安裝(僅裝 `curl`)。Nginx container 的 `wget` 是 busybox-wget(Alpine),兩者環境不同,不可混用。 + +**選定方案:** +```yaml +healthcheck: + test: ["CMD-SHELL", "nc -z 127.0.0.1 9000 || exit 1"] +``` +`netcat-openbsd` 已在 Dockerfile 安裝。`nc -z` 只做 TCP connect,確認 PHP-FPM 的 FastCGI port 正在監聽,是 app container 內部能做到最準確的 FPM 就緒判斷。 + +**能力邊界:** 此 healthcheck 確認 PHP-FPM 程序已啟動並監聽 port,但不驗證 Nginx;Nginx 有自己的獨立 healthcheck。 + +### D2:`compose.override.yml` 放在 repo 根目錄並加進版控 + +**選項 A**:放進 `.gitignore`,每台機器手動建立 +**選項 B**:放進 repo,所有環境共用 + +選 B。`compose.override.yml` 只定義開發工具(phpmyadmin、mailpit),不含任何 secret 或環境特定值,放進版控讓新成員 `git clone` 後直接可用。雲端部署 CI 指定 `-f docker-compose.yml` 排除即可。 + +### D3:`mailpit` 同步移入 override + +`mailpit` 與 `phpmyadmin` 性質相同(開發輔助工具),一起移入 override,保持 `docker-compose.yml` 只含正式服務。 + +## Risks / Trade-offs + +- **[Risk] healthcheck 只驗證 TCP 連線,不驗證請求成功**:`nc -z` 確認 port 開著,但不確認 PHP-FPM 能正確處理 FastCGI 請求。 + → **Mitigation**:對 `depends_on` 的用途(等待 FPM 啟動再起 Nginx)已足夠;若要更深層驗證需用 `cgi-fcgi` 工具(過度工程,暫不引入)。 + +- **[Risk] VPS 若沒有 `compose.override.yml`**:移除後首次 `docker compose up` 會少 phpmyadmin/mailpit。 + → **Mitigation**:`compose.override.yml` 已進版控,VPS 執行 `git pull` 即可取得,不需手動建立。STARTUP.md 同步更新說明此流程。 + +## Migration Plan + +1. `docker-compose.yml`:改 healthcheck、移除 phpmyadmin service、移除 mailpit service +2. 新增 `compose.override.yml`:加入 phpmyadmin、mailpit +3. `.env.example`:改 QUEUE_CONNECTION 預設值、補說明 +4. `STARTUP.md`:新增「部署模式」章節,明確區分: + - **VPS(預設)**:`docker compose up -d`,自動合併 override,含 phpmyadmin + mailpit + - **雲端正式環境**:`docker compose -f docker-compose.yml up -d`,排除開發工具 +5. **VPS 部署**:執行 `git pull` 取得版控內的 `compose.override.yml`,不需 rebuild image,`docker compose up -d` 即可 + +## Open Questions + +無。本次改動範圍明確,無待決事項。 diff --git a/openspec/changes/cloud-ready-zero-impact/proposal.md b/openspec/changes/cloud-ready-zero-impact/proposal.md new file mode 100644 index 0000000..f3c60a8 --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/proposal.md @@ -0,0 +1,27 @@ +## Why + +現有 Docker Compose 設定存在若干與雲端部署不相容的配置(開發工具混入正式服務、healthcheck 無法真實反映 app 狀態、queue 預設值不一致),在不動任何 PHP 程式碼、不重建 image、不影響 VPS 現有運作的前提下,先修正這四個零風險項目,作為雲端就緒路線圖的第一步。 + +## What Changes + +- **`.env.example`**:`QUEUE_CONNECTION` 預設值從 `database` 改為 `redis`;補充 `FILESYSTEM_DISK` 與 log 相關變數的說明與分組,標記哪些是雲端部署時必須設定的項目 +- **`docker-compose.yml`**:app service healthcheck 從 `php -v` 改為呼叫 `/health` 端點;移除 `phpmyadmin` service +- **新增 `compose.override.yml`**:將 `phpmyadmin`、`mailpit` 放入 override 檔,VPS/開發環境自動載入,正式雲端部署時只用 `-f docker-compose.yml` 即可排除 + +## Capabilities + +### New Capabilities + +- `compose-cloud-baseline`:Docker Compose 雲端基準設定——healthcheck 使用真實端點、正式 compose 不含開發工具、dev-only 服務透過 override 隔離 +- `env-cloud-annotations`:`.env.example` 補充雲端部署必要環境變數的說明與正確預設值 + +### Modified Capabilities + +(本次不修改任何既有 spec 的需求行為) + +## Impact + +- **`docker-compose.yml`**:healthcheck 指令、移除 phpmyadmin service +- **新增 `compose.override.yml`**:phpmyadmin、mailpit service 定義搬移至此 +- **`.env.example`**:QUEUE_CONNECTION 預設值、新增說明行 +- **不影響**:所有 PHP 程式碼、VPS `.env`、image 內容、Scheduler、Session、Log diff --git a/openspec/changes/cloud-ready-zero-impact/specs/compose-cloud-baseline/spec.md b/openspec/changes/cloud-ready-zero-impact/specs/compose-cloud-baseline/spec.md new file mode 100644 index 0000000..1706dfd --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/specs/compose-cloud-baseline/spec.md @@ -0,0 +1,32 @@ +## ADDED Requirements + +### Requirement: App service healthcheck 驗證 PHP-FPM FastCGI port +`docker-compose.yml` 的 `app` service healthcheck SHALL 使用 `nc -z 127.0.0.1 9000` 確認 PHP-FPM FastCGI port 正在監聽,而非執行 `php -v`(只驗證 binary 存在)。 + +注意:app container 內無 HTTP server,無法透過 HTTP 打 `/health`;`netcat-openbsd` 已在 Dockerfile 安裝。 + +#### Scenario: PHP-FPM 啟動後 healthcheck 通過 +- **WHEN** `app` container 啟動且 PHP-FPM 程序已監聽 port 9000 +- **THEN** `nc -z 127.0.0.1 9000` 回傳 exit code 0,container 標記為 healthy + +#### Scenario: PHP-FPM 未啟動時 healthcheck 失敗 +- **WHEN** `app` container 啟動但 PHP-FPM 程序尚未就緒(port 9000 未開) +- **THEN** `nc -z 127.0.0.1 9000` 回傳非 0 exit code,container 維持 unhealthy 狀態直到重試成功 + +### Requirement: 正式 compose 不含開發工具服務 +`docker-compose.yml` SHALL 不包含 `phpmyadmin` 及 `mailpit` service 定義。這兩個服務屬開發/管理工具,不得出現在正式雲端部署的 compose 設定中。 + +#### Scenario: 只使用 docker-compose.yml 啟動 +- **WHEN** 執行 `docker compose -f docker-compose.yml up -d` +- **THEN** 啟動的服務不包含 `phpmyadmin`(port 8081)及 `mailpit`(port 8025) + +### Requirement: 開發工具服務透過 compose.override.yml 提供 +`compose.override.yml` SHALL 定義 `phpmyadmin` 與 `mailpit` service,供本機開發與 VPS 管理使用。 + +#### Scenario: 有 override 檔時開發工具正常啟動 +- **WHEN** 專案根目錄存在 `compose.override.yml` 且執行 `docker compose up -d`(不指定 -f) +- **THEN** Docker Compose 自動合併 override,`phpmyadmin`(port 8081)與 `mailpit`(port 8025)一同啟動 + +#### Scenario: 雲端部署排除開發工具 +- **WHEN** CI/CD 執行 `docker compose -f docker-compose.yml up -d`(明確指定單一檔案) +- **THEN** `phpmyadmin` 與 `mailpit` 不啟動,不佔用任何 port 或資源 diff --git a/openspec/changes/cloud-ready-zero-impact/specs/env-cloud-annotations/spec.md b/openspec/changes/cloud-ready-zero-impact/specs/env-cloud-annotations/spec.md new file mode 100644 index 0000000..a000d87 --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/specs/env-cloud-annotations/spec.md @@ -0,0 +1,19 @@ +## ADDED Requirements + +### Requirement: QUEUE_CONNECTION 預設值為 redis +`.env.example` 的 `QUEUE_CONNECTION` SHALL 預設為 `redis`,與 VPS 實際運作設定一致,避免新環境按範本初始化後跑在效能較差的 database queue。 + +#### Scenario: 新環境照 .env.example 初始化 +- **WHEN** 開發者或 CI 以 `.env.example` 為基礎建立 `.env`,未手動修改 `QUEUE_CONNECTION` +- **THEN** Laravel queue driver 使用 Redis,queue worker 正常消費 Redis 佇列中的 job + +### Requirement: .env.example 標示雲端必要變數 +`.env.example` SHALL 以行內註解或分組說明標示以下雲端部署時必須明確設定的變數,協助操作者識別必填項目: +- `FILESYSTEM_DISK`:雲端應設為 `s3`(目前預設 `local`,雲端會導致檔案在容器重啟後遺失) +- `LOG_CHANNEL` / `LOG_STACK`:雲端建議改為 `stderr` 以接入雲端 logging aggregator +- `QUEUE_CONNECTION`:應為 `redis` +- `SESSION_DRIVER`:雲端建議改為 `redis` + +#### Scenario: 操作者閱讀 .env.example 進行雲端部署 +- **WHEN** 操作者參照 `.env.example` 設定雲端環境的 `.env` +- **THEN** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值 diff --git a/openspec/changes/cloud-ready-zero-impact/tasks.md b/openspec/changes/cloud-ready-zero-impact/tasks.md new file mode 100644 index 0000000..fb222c5 --- /dev/null +++ b/openspec/changes/cloud-ready-zero-impact/tasks.md @@ -0,0 +1,31 @@ +## 1. docker-compose.yml 調整 + +- [x] 1.1 [後端] 修改 `docker-compose.yml` app service healthcheck:將 `test: ["CMD", "php", "-v"]` 改為 `test: ["CMD-SHELL", "nc -z 127.0.0.1 9000 || exit 1"]`(app container 無 HTTP server,用 nc 驗證 PHP-FPM FastCGI port 9000) +- [x] 1.2 [後端] 從 `docker-compose.yml` 移除 `phpmyadmin` service 完整定義(包含 image、container_name、environment、networks、depends_on) +- [x] 1.3 [後端] 從 `docker-compose.yml` 移除 `mailpit` service 完整定義 + +## 2. 新增 compose.override.yml + +- [x] 2.1 [後端] 在專案根目錄新建 `compose.override.yml`,加入 `phpmyadmin` service(保留原有的 PMA_HOST、PMA_PORT、PMA_USER、PMA_PASSWORD、MYSQL_ROOT_PASSWORD 環境變數設定與 networks、depends_on) +- [x] 2.2 [後端] 在 `compose.override.yml` 加入 `mailpit` service(保留原有設定與 networks) + +## 3. .env.example 更新 + +- [x] 3.1 [後端] 將 `.env.example` 的 `QUEUE_CONNECTION` 值從 `database` 改為 `redis` +- [x] 3.2 [後端] 在 `.env.example` 的 `FILESYSTEM_DISK=local` 旁加上行內說明,標示雲端應改為 `s3` +- [x] 3.3 [後端] 在 `.env.example` 的 `LOG_CHANNEL` / `LOG_STACK` 區段加上說明,標示雲端建議改為 `stderr` +- [x] 3.4 [後端] 在 `.env.example` 的 `SESSION_DRIVER=database` 旁加上說明,標示雲端建議改為 `redis` +- [x] 3.5 [後端] 在 `.env.example` 的 `QUEUE_CONNECTION=redis` 旁加上說明,確認此為雲端建議值 + +## 4. 文件更新 + +- [x] 4.1 [後端] 更新 `docs/STARTUP.md`,新增「部署模式」章節,明確列出兩組指令: + - **本機 / VPS**:`docker compose up -d`(自動合併 override,含 phpmyadmin + mailpit) + - **雲端正式環境**:`docker compose -f docker-compose.yml up -d`(排除開發工具) + - 說明 `compose.override.yml` 已進版控,VPS `git pull` 後即可取得,不需手動建立 + +## 5. 手動驗證 + +- [x] 5.1 [整合測試] 在本機執行 `docker compose up -d`,確認 phpmyadmin(8081)與 mailpit(8025)仍可正常存取 +- [x] 5.2 [整合測試] 執行 `docker compose -f docker-compose.yml up -d --remove-orphans`,確認 phpmyadmin 與 mailpit 不啟動 +- [x] 5.3 [整合測試] 確認 `app` container 的 healthcheck 狀態在啟動後顯示 healthy(`docker compose ps` 查看)