# =================================================================== # TopFans Docker Compose - Production (4G RAM, 2 CPU) # =================================================================== # Usage: # docker-compose -f docker-compose.prod.yml --profile prod up -d # =================================================================== # 显式声明 project 名称,与 TopFans-activity-admin 仓库的 docker-compose.admin.yml # 完全隔离。这样 `docker ps --filter project=topfans-main` / `down --remove-orphans` # 永远只动本仓库的容器,不会误删 admin 仓的 topfans-adminbackend / topfans-adminfrontend。 # (历史教训:2026-06-12 因 deploy.sh 用 name=topfans- 前缀过滤误删 admin 容器 18 分钟) name: topfans-main x-common-env: &common-env GIN_MODE: release ENV: production LOG_LEVEL: info DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans DB_SSLMODE: disable x-postgres-env: &postgres-env POSTGRES_DB: topfans POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres123} POSTGRES_MAX_CONNECTIONS: 100 POSTGRES_SHARED_BUFFERS: 128MB POSTGRES_WORK_MEM: 8MB POSTGRES_MAINTENANCE_WORK_MEM: 64MB POSTGRES_EFFECTIVE_CACHE_SIZE: 512MB POSTGRES_CHECKPOINT_COMPLETION_TARGET: 0.9 POSTGRES_WAL_BUFFERS: 8MB x-healthcheck: &healthcheck interval: 30s timeout: 10s retries: 5 start_period: 60s services: # ==================== Database ==================== postgres: image: postgres:latest container_name: topfans-postgres restart: always environment: <<: *postgres-env volumes: - postgres_data:/var/lib/postgresql ports: - "5432:5432" networks: - topfans-net healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d topfans"] <<: *healthcheck deploy: resources: limits: memory: 400M reservations: memory: 128M # ==================== Redis ==================== redis: image: redis:latest container_name: topfans-redis restart: always environment: REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} ports: - "6379:6379" networks: - topfans-net healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-123456}", "ping"] interval: 30s timeout: 10s retries: 5 deploy: resources: limits: memory: 256M reservations: memory: 64M # ==================== Flyway Migration ==================== # flyway: # image: flyway/flyway:10 # container_name: topfans-flyway # restart: "no" # depends_on: # postgres: # condition: service_healthy # environment: # - FLYWAY_URL=jdbc:postgresql://postgres:5432/topfans # - FLYWAY_USER=postgres # - FLYWAY_PASSWORD=${DB_PASSWORD:-postgres123} # - FLYWAY_SCHEMAS=public # - FLYWAY_PLACEHOLDER_REPLACEMENT=true # volumes: # - ./sql/migrations:/flyway/sql # - flyway_data:/flyway/data # command: migrate -baselineOnMigrate=true # networks: # - topfans-net # ==================== Dubbo Services ==================== userservice: image: topfans/userservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: userservice container_name: topfans-userservice restart: always env_file: - .env.prod environment: <<: *common-env PORT: 20000 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans REDIS_HOST: topfans-redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} REDIS_DB: 0 depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - topfans-net expose: - "20000" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21000/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 150M cpus: '0.5' reservations: memory: 64M cpus: '0.25' assetservice: image: topfans/assetservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: assetservice container_name: topfans-assetservice restart: always env_file: - .env.prod environment: <<: *common-env PORT: 20003 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans USER_SERVICE_URL: tri://userservice:20000 depends_on: userservice: condition: service_started networks: - topfans-net expose: - "20003" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21003/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 200M cpus: '0.5' reservations: memory: 64M cpus: '0.25' socialservice: image: topfans/socialservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: socialservice container_name: topfans-socialservice restart: always environment: <<: *common-env PORT: 20002 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans USER_SERVICE_URL: tri://userservice:20000 ASSET_SERVICE_URL: tri://assetservice:20003 depends_on: userservice: condition: service_started assetservice: condition: service_started networks: - topfans-net expose: - "20002" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21002/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 150M cpus: '0.5' reservations: memory: 64M cpus: '0.25' galleryservice: image: topfans/galleryservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: galleryservice container_name: topfans-galleryservice restart: always environment: <<: *common-env PORT: 20001 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans USER_SERVICE_URL: tri://userservice:20000 ASSET_SERVICE_URL: tri://assetservice:20003 TASK_SERVICE_URL: tri://taskservice:20006 depends_on: userservice: condition: service_started assetservice: condition: service_started networks: - topfans-net expose: - "20001" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21001/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 150M cpus: '0.5' reservations: memory: 64M cpus: '0.25' activityservice: image: topfans/activityservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: activityservice container_name: topfans-activityservice restart: always environment: <<: *common-env PORT: 20004 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans USER_SERVICE_URL: tri://userservice:20000 REDIS_HOST: topfans-redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} REDIS_DB: 0 depends_on: userservice: condition: service_started redis: condition: service_healthy networks: - topfans-net expose: - "20004" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21004/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 100M cpus: '0.5' reservations: memory: 32M cpus: '0.25' taskservice: image: topfans/taskservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: taskservice container_name: topfans-taskservice restart: always environment: <<: *common-env PORT: 20006 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans USER_SERVICE_URL: tri://userservice:20000 GALLERY_SERVICE_URL: tri://galleryservice:20001 depends_on: userservice: condition: service_started networks: - topfans-net expose: - "20006" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21006/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 150M cpus: '0.5' reservations: memory: 64M cpus: '0.25' starbookservice: image: topfans/starbookservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: starbookservice container_name: topfans-starbookservice restart: always environment: <<: *common-env PORT: 20005 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans ASSET_SERVICE_URL: tri://assetservice:20003 depends_on: userservice: condition: service_started assetservice: condition: service_started networks: - topfans-net expose: - "20005" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21005/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 150M cpus: '0.5' reservations: memory: 64M cpus: '0.25' # ==================== AI Chat Service ==================== aichatservice: image: topfans/aichatservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: aichatservice container_name: topfans-aichatservice restart: always env_file: - .env.prod environment: <<: *common-env PORT: 20008 DB_HOST: postgres DB_PORT: 5432 DB_USER: postgres DB_PASSWORD: ${DB_PASSWORD:-postgres123} DB_NAME: topfans REDIS_HOST: topfans-redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} REDIS_DB: 0 depends_on: userservice: condition: service_started redis: condition: service_healthy networks: - topfans-net expose: - "20008" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21008/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 512M cpus: '1' reservations: memory: 256M cpus: '0.5' # ==================== Laser Compositor Service (镭射卡 6 层合成) ==================== lasercompositor: image: topfans/lasercompositor:latest build: context: .. dockerfile: docker/Dockerfile.services target: lasercompositor # 跳过 pull(本地无 push 到 registry),直接走 build pull_policy: never container_name: topfans-lasercompositor restart: always env_file: - .env.prod environment: <<: *common-env COMPOSITOR_PORT: 7002 # OSS_* 全部走 env_file: .env.prod networks: - topfans-net expose: - "7002" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:7002/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 300M cpus: '0.5' reservations: memory: 128M cpus: '0.25' # ==================== Statistic Service (数据看板微服务) ==================== statisticservice: image: topfans/statisticservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: statisticservice container_name: topfans-statisticservice restart: always env_file: - .env.prod environment: <<: *common-env PORT: 20009 # statistic 服务使用独立 schema(与 common-env 中的 DB_* 解耦) STATISTIC_DB_HOST: postgres STATISTIC_DB_PORT: 5432 STATISTIC_DB_USER: postgres STATISTIC_DB_PASSWORD: ${DB_PASSWORD:-postgres123} STATISTIC_DB_NAME: topfans STATISTIC_DB_SSLMODE: disable STATISTIC_DB_SCHEMA: statistic # Redis STATISTIC_REDIS_HOST: topfans-redis STATISTIC_REDIS_PORT: 6379 STATISTIC_REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} STATISTIC_REDIS_DB: 0 # 跨服务调用 userService(看板 GetTodayOverview 需要 crystal_balance) USER_SERVICE_URL: tri://userservice:20000 depends_on: userservice: condition: service_started redis: condition: service_healthy networks: - topfans-net expose: - "20009" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21009/healthz || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 300M cpus: '0.5' reservations: memory: 128M cpus: '0.25' # 通知服务(手机推送 / 通知中心读写) # 推送走阿里云 uniCloud sendMessage,生产 URL 在 .env.prod 注入。 # 留空 PUSH_URL 会让 main.go 降级为 NoopPusher(只写库,不真推手机)。 notificationservice: image: topfans/notificationservice:latest build: context: .. dockerfile: docker/Dockerfile.services target: notificationservice container_name: topfans-notificationservice restart: always env_file: - .env.prod environment: <<: *common-env PORT: 20010 # 默认从 .env.prod 读取;部署时如需覆盖可在 docker-compose 启动时 # 用 NOTIFICATION_PUSH_URL=... 注入(只对当前启动生效,不入 git)。 PUSH_ENABLED: ${NOTIFICATION_PUSH_ENABLED:-true} PUSH_URL: ${NOTIFICATION_PUSH_URL:-https://env-00jy6bcqqwy6.dev-hz.cloudbasefunction.cn/sendMessage} PUSH_TIMEOUT_MS: ${NOTIFICATION_PUSH_TIMEOUT_MS:-4000} depends_on: postgres: condition: service_healthy networks: - topfans-net expose: - "20010" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21010/healthz || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 300M cpus: '0.5' reservations: memory: 128M cpus: '0.25' # ==================== API Gateway ==================== gateway: image: topfans/gateway:latest build: context: .. dockerfile: docker/Dockerfile.services target: gateway container_name: topfans-gateway restart: always env_file: - .env.prod environment: <<: *common-env SERVER_PORT: 8080 DUBBO_USER_SERVICE_URL: tri://userservice:20000 DUBBO_SOCIAL_SERVICE_URL: tri://socialservice:20002 DUBBO_ASSET_SERVICE_URL: tri://assetservice:20003 DUBBO_GALLERY_SERVICE_URL: tri://galleryservice:20001 DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20004 DUBBO_TASK_SERVICE_URL: tri://taskservice:20006 DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20005 DUBBO_AI_CHAT_SERVICE_URL: tri://aichatservice:20008 DUBBO_STATISTIC_SERVICE_URL: tri://statisticservice:20009 DUBBO_NOTIFICATION_SERVICE_URL: tri://notificationservice:20010 LASER_COMPOSITOR_URL: http://lasercompositor:7002 # 抠图(人像扣底)、OSS、Dify、JWT、Redis 全部走 env_file: .env.prod REDIS_HOST: topfans-redis REDIS_PORT: 6379 REDIS_DB: 0 depends_on: userservice: condition: service_started assetservice: condition: service_started socialservice: condition: service_started galleryservice: condition: service_started activityservice: condition: service_started taskservice: condition: service_started starbookservice: condition: service_started aichatservice: condition: service_started statisticservice: condition: service_started notificationservice: condition: service_started lasercompositor: condition: service_started redis: condition: service_healthy networks: topfans-net: aliases: - newbackend ports: - "8080:8080" healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"] <<: *healthcheck deploy: resources: limits: memory: 300M cpus: '0.5' reservations: memory: 128M cpus: '0.25' volumes: postgres_data: flyway_data: networks: topfans-net: driver: bridge external: true