feat(infra): 雲端就緒零影響改動 (compose overlay + healthcheck + env)
Run Tests / test (pull_request) Successful in 30s

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 01:48:24 +08:00
parent 998f34139a
commit be629feef9
10 changed files with 248 additions and 30 deletions
+4 -4
View File
@@ -16,7 +16,7 @@ APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12 BCRYPT_ROUNDS=12
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOG_STACK=daily LOG_STACK=daily # 雲端建議改為 stderr 以接入雲端 logging aggregator
LOG_DAILY_DAYS=14 LOG_DAILY_DAYS=14
LOG_DEPRECATIONS_CHANNEL=null LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug LOG_LEVEL=debug
@@ -29,15 +29,15 @@ DB_CONNECTION=sqlite
# DB_PASSWORD= # DB_PASSWORD=
# MYSQL_ROOT_PASSWORD= # MYSQL_ROOT_PASSWORD=
SESSION_DRIVER=database SESSION_DRIVER=database # 雲端建議改為 redis
SESSION_LIFETIME=120 SESSION_LIFETIME=120
SESSION_ENCRYPT=false SESSION_ENCRYPT=false
SESSION_PATH=/ SESSION_PATH=/
SESSION_DOMAIN=null SESSION_DOMAIN=null
BROADCAST_CONNECTION=log BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local FILESYSTEM_DISK=local # 雲端應改為 s3(local 在容器重啟後會遺失上傳檔案)
QUEUE_CONNECTION=database QUEUE_CONNECTION=redis # 雲端建議值;database 效能較差且不利擴容
CACHE_STORE=redis CACHE_STORE=redis
CACHE_PREFIX= CACHE_PREFIX=
+27
View File
@@ -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
+5 -26
View File
@@ -38,11 +38,11 @@ services:
# 健康檢查配置 # 健康檢查配置
healthcheck: healthcheck:
test: ["CMD", "php", "-v"] # 檢查 PHP 版本確認服務正常 test: ["CMD-SHELL", "nc -z 127.0.0.1 9000 || exit 1"]
interval: 30s # 每30秒檢查一次 interval: 30s
timeout: 10s # 超時時間10秒 timeout: 10s
retries: 3 # 重試3次 retries: 3
start_period: 40s # 容器啟動後40秒開始檢查 start_period: 40s
nginx: nginx:
image: nginx:alpine image: nginx:alpine
@@ -109,21 +109,6 @@ services:
ports: ports:
- "127.0.0.1:3306:3306" # 只綁 localhost,不對外 - "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: queue-worker:
image: cfdive-platform image: cfdive-platform
@@ -146,12 +131,6 @@ services:
app: app:
condition: service_healthy condition: service_healthy
mailpit:
image: axllent/mailpit
container_name: cfdive-mailpit
restart: unless-stopped
networks:
- cfdive-network
redis: redis:
image: redis:alpine image: redis:alpine
+25
View File
@@ -1,5 +1,30 @@
# CFDivePlatform 啟動指令 # 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 ```powershell
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-17
@@ -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
### D1healthcheck 用 `nc -z 127.0.0.1 9000` 檢查 PHP-FPM port
**為什麼不用 `/health` HTTP 端點:**
app container 跑的是 PHP-FPMFastCGIport 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-wgetAlpine),兩者環境不同,不可混用。
**選定方案:**
```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,但不驗證 NginxNginx 有自己的獨立 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
無。本次改動範圍明確,無待決事項。
@@ -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
@@ -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 0container 標記為 healthy
#### Scenario: PHP-FPM 未啟動時 healthcheck 失敗
- **WHEN** `app` container 啟動但 PHP-FPM 程序尚未就緒(port 9000 未開)
- **THEN** `nc -z 127.0.0.1 9000` 回傳非 0 exit codecontainer 維持 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 或資源
@@ -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 使用 Redisqueue 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** 每個雲端關鍵變數旁有說明,提示預設值在雲端環境的限制與建議替代值
@@ -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`,確認 phpmyadmin8081)與 mailpit8025)仍可正常存取
- [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` 查看)