services: # PHP-FPM 服務 app: # 構建配置 build: context: . # 使用當前目錄作為構建上下文 dockerfile: Dockerfile # 指定 Dockerfile 路徑 target: app # 明確指定 build target,避免預設建到最後一個 stage(web/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