Files
CFDivePlatform/docker-compose.yml
T
a620906209 6a7d98b0fe
Run Tests / test (pull_request) Successful in 32s
feat(docker): 移除 bind mount,程式碼與 vendor 烘進 image
Dockerfile 改 multi-stage(app + web),app/scheduler/queue-worker/
reverb/nginx 五個服務都不再依賴 ./:/var/www bind mount 取得程式碼;
nginx 改本地 build 從 app 階段 COPY --from 取得 public/,public/storage
symlink 在兩階段各自於 build 時建立。docker-compose.yml 新增具名 volume
storage-app-public 讓上傳檔案跨 image 重建保留;env_file: 取代原本
bind mount 帶入 .env 的方式,只給四個 Laravel runtime 服務。
compose.override.yml 補上開發用 bind mount 維持改 code 立即生效;
docker-entrypoint.sh 收斂到只剩目錄權限設定,migration/cache/swagger
移到 deploy.yml 統一負責,避免職責重疊。

實作驗證時發現並修復三個規劃階段沒預見的問題:app 服務的 build: 缺
target: app 導致預設建到最後一個 stage(image 實際變成 nginx);
.dockerignore 需排除本機既有的 public/storage 符號連結(Windows 上
會讓 build context 打包失敗);env_file: 讓 .env 全部變數變成真實
環境變數、蓋掉 phpunit.xml 的測試隔離設定,已擴大 tests/bootstrap.php
的強制覆蓋清單修復(239 tests 全綠)。

本機 dev 模式與 production 模式(無 override)皆完整驗證:healthcheck、
migration/cache/swagger、上傳檔案持久性、nginx 獨立於 app 服務靜態檔
與 storage 檔案、.env 熱更新、nginx 無 .env 存取權。

openspec change: bake-image-remove-bind-mount

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142LC1kQb8EyV59TjHDkhWS
2026-08-04 16:59:18 +08:00

187 lines
4.8 KiB
YAML
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.
services:
# PHP-FPM 服務
app:
# 構建配置
build:
context: . # 使用當前目錄作為構建上下文
dockerfile: Dockerfile # 指定 Dockerfile 路徑
target: app # 明確指定 build target,避免預設建到最後一個 stageweb/nginx
image: cfdive-platform # 構建的鏡像名稱
container_name: cfdive-app # 容器名稱
restart: unless-stopped # 自動重啟策略:除非手動停止,否則自動重啟
working_dir: /var/www/ # 工作目錄
# 上傳檔案在 image 重建後仍保留(過渡方案,待正式切 S3 後可移除)
volumes:
- storage-app-public:/var/www/storage/app/public
# 環境變數:以 env_file 注入 .env 內容,不會在容器內生成實體 .env 檔案
env_file:
- .env
# 網絡配置
networks:
- cfdive-network # 連接到自定義網絡
- proxy_net
# 依賴關係:確保 db 和 redis 服務先啟動
depends_on:
db: # 依賴 db 服務
condition: service_healthy # 等待數據庫健康檢查通過
redis: # 依賴 redis 服務
condition: service_started # 等待服務啟動
# 健康檢查配置
healthcheck:
test: ["CMD-SHELL", "nc -z 127.0.0.1 9000 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
nginx:
# 從 Dockerfile 的 web 階段本地建置,取得 public/ 靜態資源,
# 不再拉取官方 nginx:alpine image、也不依賴任何執行期共享機制取得程式碼
build:
context: .
dockerfile: Dockerfile
target: web
image: cfdive-nginx-web
container_name: cfdive-nginx
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
volumes:
- storage-app-public:/var/www/storage/app/public
networks:
- cfdive-network
- proxy_net
depends_on:
app:
condition: service_healthy
healthcheck:
# busybox wget 對 localhost 會先解析到 IPv6 ::1,但 nginx 只 listen IPv4,須用 127.0.0.1
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/"]
interval: 30s
timeout: 10s
retries: 3
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
VITE_API_URL: ${VITE_API_URL:-https://api.hank-space.com}
VITE_REVERB_APP_KEY: ${VITE_REVERB_APP_KEY}
VITE_REVERB_HOST: ${VITE_REVERB_HOST:-localhost}
VITE_REVERB_PORT: ${VITE_REVERB_PORT:-8085}
VITE_REVERB_SCHEME: ${VITE_REVERB_SCHEME:-http}
image: cfdive-frontend
container_name: cfdive-frontend
restart: unless-stopped
ports:
- "127.0.0.1:5173:80"
networks:
- cfdive-network
- proxy_net
db:
image: mysql:8.0
container_name: cfdive-db
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE:-CFDivePlatform}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${DB_USERNAME:-cfdiveuser}
MYSQL_PASSWORD: ${DB_PASSWORD}
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- mysql-data:/var/lib/mysql
networks:
- cfdive-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
ports:
- "127.0.0.1:3306:3306" # 只綁 localhost,不對外
scheduler:
image: cfdive-platform
container_name: cfdive-scheduler
restart: unless-stopped
working_dir: /var/www/
command: php artisan schedule:work
env_file:
- .env
networks:
- cfdive-network
depends_on:
app:
condition: service_healthy
queue-worker:
image: cfdive-platform
container_name: cfdive-queue
restart: unless-stopped
working_dir: /var/www/
command: php artisan queue:work --sleep=3 --tries=3 --timeout=60
env_file:
- .env
networks:
- cfdive-network
depends_on:
app:
condition: service_healthy
redis:
image: redis:alpine
container_name: cfdive-redis
restart: unless-stopped
networks:
- cfdive-network
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
reverb:
image: cfdive-platform
container_name: cfdive-reverb
restart: unless-stopped
working_dir: /var/www/
entrypoint: ["php", "artisan", "reverb:start", "--host=0.0.0.0", "--port=8080"]
ports:
- "127.0.0.1:8085:8080"
env_file:
- .env
networks:
- cfdive-network
- proxy_net
depends_on:
app:
condition: service_healthy
networks:
cfdive-network:
driver: bridge
proxy_net:
external: true
volumes:
mysql-data:
driver: local
redis-data:
driver: local
storage-app-public:
driver: local