# Photography Portfolio - Complete Docker Compose # 完整的生产环境容器编排配置 version: '3.8' services: # PostgreSQL 数据库 db: image: postgres:16-alpine container_name: photography-db environment: POSTGRES_DB: photography POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres123} POSTGRES_INITDB_ARGS: "--encoding=UTF-8" ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./backend/configs/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro networks: - photography-network restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d photography"] interval: 30s timeout: 10s retries: 5 deploy: resources: limits: cpus: '1.0' memory: 1G reservations: cpus: '0.5' memory: 512M # Redis 缓存 redis: image: redis:7-alpine container_name: photography-redis ports: - "6379:6379" volumes: - redis_data:/data command: redis-server --requirepass ${REDIS_PASSWORD:-redis123} --maxmemory 256mb --maxmemory-policy allkeys-lru networks: - photography-network restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "auth", "${REDIS_PASSWORD:-redis123}", "ping"] interval: 30s timeout: 10s retries: 3 # 后端API服务 api: build: context: ./backend dockerfile: Dockerfile container_name: photography-api environment: # 数据库配置 DB_HOST: db DB_PORT: 5432 DB_NAME: photography DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_SSL_MODE: disable DB_MAX_CONNECTIONS: 100 DB_MAX_IDLE_CONNECTIONS: 10 # Redis配置 REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-redis123} REDIS_DB: 0 # JWT配置 JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-change-in-production} JWT_EXPIRE: 24h # 服务配置 APP_ENV: ${APP_ENV:-production} APP_PORT: 8080 APP_HOST: 0.0.0.0 APP_VERSION: 1.0.0 # CORS配置 CORS_ORIGINS: ${CORS_ORIGINS:-https://photography.iriver.top,https://admin.photography.iriver.top} # 文件上传配置 UPLOAD_PATH: /app/uploads UPLOAD_MAX_SIZE: 10485760 # 10MB UPLOAD_ALLOWED_TYPES: "image/jpeg,image/png,image/gif,image/webp" # 日志配置 LOG_LEVEL: ${LOG_LEVEL:-info} LOG_FORMAT: json LOG_FILE: /app/logs/api.log # 监控配置 ENABLE_METRICS: true METRICS_PORT: 9090 ports: - "8080:8080" - "9090:9090" # 监控端口 volumes: - uploads_data:/app/uploads - logs_data:/app/logs networks: - photography-network depends_on: db: condition: service_healthy redis: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "/photography-api", "--health-check"] interval: 30s timeout: 10s retries: 3 start_period: 30s deploy: resources: limits: cpus: '2.0' memory: 2G reservations: cpus: '1.0' memory: 1G # 前端展示网站 frontend: build: context: ./frontend dockerfile: Dockerfile container_name: photography-frontend environment: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-https://api.photography.iriver.top} NEXT_PUBLIC_APP_NAME: "Photography Portfolio" NEXT_PUBLIC_APP_VERSION: "1.0.0" NEXT_PUBLIC_ENABLE_ANALYTICS: ${NEXT_PUBLIC_ENABLE_ANALYTICS:-false} NEXT_PUBLIC_MAX_FILE_SIZE: 10485760 ports: - "3000:80" networks: - photography-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M # 管理后台 admin: build: context: ./admin dockerfile: Dockerfile container_name: photography-admin environment: REACT_APP_API_URL: ${REACT_APP_API_URL:-https://api.photography.iriver.top} REACT_APP_APP_NAME: "Photography Admin" REACT_APP_VERSION: "1.0.0" ports: - "3001:80" networks: - photography-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M # 数据库迁移 (一次性服务) migrate: build: context: ./backend dockerfile: Dockerfile container_name: photography-migrate environment: DB_HOST: db DB_PORT: 5432 DB_NAME: photography DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_SSL_MODE: disable networks: - photography-network depends_on: db: condition: service_healthy entrypoint: ["/migrate", "up"] restart: "no" # 监控服务 (可选) prometheus: image: prom/prometheus:latest container_name: photography-prometheus ports: - "9091:9090" volumes: - ./configs/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus networks: - photography-network restart: unless-stopped profiles: - monitoring # 日志收集 (可选) grafana: image: grafana/grafana:latest container_name: photography-grafana ports: - "3002:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} volumes: - grafana_data:/var/lib/grafana networks: - photography-network restart: unless-stopped profiles: - monitoring # 反向代理 (可选,推荐使用Caddy) nginx: image: nginx:1.25-alpine container_name: photography-nginx ports: - "80:80" - "443:443" volumes: - ./configs/nginx.conf:/etc/nginx/nginx.conf:ro - ./configs/sites-enabled:/etc/nginx/sites-enabled:ro - ssl_certs:/etc/nginx/ssl:ro networks: - photography-network depends_on: - frontend - admin - api restart: unless-stopped profiles: - proxy volumes: postgres_data: driver: local redis_data: driver: local uploads_data: driver: local logs_data: driver: local prometheus_data: driver: local grafana_data: driver: local ssl_certs: driver: local networks: photography-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16 labels: - "com.photography.network=main" # 健康检查配置 x-healthcheck-defaults: &healthcheck-defaults interval: 30s timeout: 10s retries: 3 start_period: 30s