version: '3.8' services: mysql: image: mysql:8.0 container_name: harborforge-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-harborforge_root} MYSQL_DATABASE: ${MYSQL_DATABASE:-harborforge} MYSQL_USER: ${MYSQL_USER:-harborforge} MYSQL_PASSWORD: ${MYSQL_PASSWORD:-harborforge_pass} volumes: - mysql_data:/var/lib/mysql ports: - "${MYSQL_PORT:-3306}:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 deploy: resources: limits: cpus: '0.5' memory: 512M # AbstractWizard — 初始化配置管理 wizard: image: git.hangman-lab.top/hzhang/abstract-wizard:latest container_name: harborforge-wizard restart: unless-stopped volumes: - wizard_config:/config - ./init-config:/init-config:ro environment: CONFIG_DIR: /config LISTEN_ADDR: "0.0.0.0:8080" MAX_BACKUPS: "5" # distroless image — no shell for healthcheck # wizard-init will retry until wizard is reachable deploy: resources: limits: cpus: '0.1' memory: 64M # 初始化 — 将默认配置写入 AbstractWizard wizard-init: image: curlimages/curl:latest container_name: harborforge-wizard-init depends_on: - wizard volumes: - ./init-config:/init-config:ro entrypoint: ["/bin/sh", "-c"] command: - | echo "Waiting for AbstractWizard to be ready..." for i in $$(seq 1 30); do if curl -sf http://wizard:8080/health > /dev/null 2>&1; then break fi echo " attempt $$i/30..." sleep 2 done echo "Checking if harborforge.json exists in wizard..." STATUS=$$(curl -s -o /dev/null -w '%%{http_code}' http://wizard:8080/api/v1/config/harborforge.json) if [ "$$STATUS" = "404" ]; then echo "Config not found, uploading init-config/harborforge.json..." curl -s -X PUT http://wizard:8080/api/v1/config/harborforge.json \ -H "Content-Type: application/json" \ -d @/init-config/harborforge.json echo "" echo "Init config uploaded successfully." else echo "Config already exists (status=$$STATUS), skipping upload." fi backend: build: context: ./HarborForge.Backend dockerfile: Dockerfile container_name: harborforge-backend restart: unless-stopped environment: DATABASE_URL: mysql+pymysql://${MYSQL_USER:-harborforge}:${MYSQL_PASSWORD:-harborforge_pass}@mysql:3306/${MYSQL_DATABASE:-harborforge} SECRET_KEY: ${SECRET_KEY:-change_me_in_production} LOG_LEVEL: ${LOG_LEVEL:-INFO} WIZARD_URL: http://wizard:8080 WIZARD_CONFIG: harborforge.json ports: - "${BACKEND_PORT:-8000}:8000" depends_on: mysql: condition: service_healthy wizard-init: condition: service_completed_successfully deploy: resources: limits: cpus: '0.5' memory: 512M healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s frontend: build: context: ./HarborForge.Frontend dockerfile: Dockerfile args: VITE_API_BASE: ${VITE_API_BASE:-/api} container_name: harborforge-frontend restart: unless-stopped ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: backend: condition: service_healthy deploy: resources: limits: cpus: '0.25' memory: 128M healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3 volumes: mysql_data: driver: local wizard_config: driver: local