version: '3.9' services: # ============================================================ # Infrastructure Services # ============================================================ postgres: image: postgres:15-alpine container_name: genex-postgres environment: POSTGRES_USER: genex POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: genex ports: - "127.0.0.1:45432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./migrations:/docker-entrypoint-initdb.d command: - "postgres" - "-c" - "wal_level=logical" # Required for Debezium CDC - "-c" - "max_replication_slots=10" # CDC connector slots - "-c" - "max_wal_senders=10" # WAL sender processes - "-c" # WAL 安全阀: 限制单个 replication slot 最多保留 10GB WAL # 超过此值 PostgreSQL 会使 slot 失效, 防止磁盘被吃满 # (rwadurian 事故: 无此限制, 单个 stuck slot 累积 305GB WAL) - "max_slot_wal_keep_size=10GB" healthcheck: test: ["CMD-SHELL", "pg_isready -U genex"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped networks: - genex-network redis: image: redis:7-alpine container_name: genex-redis ports: - "127.0.0.1:46379:6379" volumes: - redis_data:/data command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 restart: unless-stopped networks: - genex-network kafka: image: confluentinc/cp-kafka:7.7.0 container_name: genex-kafka environment: # KRaft mode (no Zookeeper needed since Kafka 3.5+) KAFKA_NODE_ID: 1 KAFKA_PROCESS_ROLES: broker,controller KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093 KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:29092 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:49092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" KAFKA_LOG_DIRS: /var/lib/kafka/data CLUSTER_ID: "genex-kafka-cluster-001" ports: - "127.0.0.1:49092:9092" - "127.0.0.1:49093:29092" volumes: - kafka_data:/var/lib/kafka/data healthcheck: test: ["CMD", "kafka-broker-api-versions", "--bootstrap-server", "localhost:9092"] interval: 10s timeout: 10s retries: 5 restart: unless-stopped networks: - genex-network # MinIO Object Storage (S3-compatible, multi-region replication support) minio: image: minio/minio:latest container_name: genex-minio environment: MINIO_ROOT_USER: genex-admin MINIO_ROOT_PASSWORD: genex-minio-secret ports: - "127.0.0.1:49000:9000" # S3 API - "127.0.0.1:49001:9001" # Console UI volumes: - minio_data:/data command: server /data --console-address ":9001" healthcheck: test: ["CMD", "mc", "ready", "local"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - genex-network # MinIO bucket initialization minio-init: image: minio/mc:latest container_name: genex-minio-init depends_on: minio: condition: service_healthy entrypoint: > /bin/sh -c " mc alias set genex http://minio:9000 genex-admin genex-minio-secret; mc mb --ignore-existing genex/kyc-documents; mc mb --ignore-existing genex/coupon-images; mc mb --ignore-existing genex/issuer-documents; mc mb --ignore-existing genex/sar-reports; mc mb --ignore-existing genex/avatars; mc mb --ignore-existing genex/exports; mc mb --ignore-existing genex/app-releases; mc anonymous set download genex/coupon-images; mc anonymous set download genex/avatars; echo 'MinIO buckets initialized'; " networks: - genex-network # Debezium Kafka Connect (CDC - Change Data Capture) # 版本说明: 必须使用 2.5.1+ (修复 DBZ-7316: searchWalPosition 不推进 confirmed_flush_lsn, 导致 WAL 无限积压) kafka-connect: image: debezium/connect:2.5.4.Final container_name: genex-kafka-connect environment: BOOTSTRAP_SERVERS: kafka:9092 GROUP_ID: genex-connect CONFIG_STORAGE_TOPIC: genex_connect_configs OFFSET_STORAGE_TOPIC: genex_connect_offsets STATUS_STORAGE_TOPIC: genex_connect_statuses CONFIG_STORAGE_REPLICATION_FACTOR: 1 OFFSET_STORAGE_REPLICATION_FACTOR: 1 STATUS_STORAGE_REPLICATION_FACTOR: 1 # Offset 提交频率: 默认 60s, 缩短至 10s 以减少重启后重复处理窗口 OFFSET_FLUSH_INTERVAL_MS: 10000 OFFSET_FLUSH_TIMEOUT_MS: 5000 ports: - "127.0.0.1:48083:8083" healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8083/ || exit 1"] interval: 30s timeout: 10s start_period: 30s retries: 3 depends_on: kafka: condition: service_healthy postgres: condition: service_healthy restart: unless-stopped networks: - genex-network # Kong API Gateway (DB-less / Declarative mode) kong: image: kong:3.5 container_name: genex-kong environment: KONG_DATABASE: "off" KONG_DECLARATIVE_CONFIG: /etc/kong/kong.yml KONG_PROXY_ACCESS_LOG: /dev/stdout KONG_ADMIN_ACCESS_LOG: /dev/stdout KONG_PROXY_ERROR_LOG: /dev/stderr KONG_ADMIN_ERROR_LOG: /dev/stderr KONG_ADMIN_LISTEN: 0.0.0.0:8001 KONG_PROXY_LISTEN: 0.0.0.0:8080 ports: - "48080:8080" # Proxy (frontend connects here) - "127.0.0.1:48001:8001" # Admin API volumes: - ./kong/kong.yml:/etc/kong/kong.yml:ro healthcheck: test: ["CMD", "kong", "health"] interval: 10s timeout: 10s retries: 5 restart: unless-stopped networks: - genex-network # ============================================================ # NestJS Services (9) # ============================================================ user-service: build: context: . dockerfile: services/user-service/Dockerfile container_name: genex-user-service ports: - "4001:3001" environment: - NODE_ENV=development - PORT=3001 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production - JWT_ACCESS_EXPIRY=15m - JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production - JWT_REFRESH_EXPIRY=7d depends_on: postgres: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy networks: - genex-network issuer-service: build: context: . dockerfile: services/issuer-service/Dockerfile container_name: genex-issuer-service ports: - "4002:3002" environment: - NODE_ENV=development - PORT=3002 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy networks: - genex-network clearing-service: build: context: . dockerfile: services/clearing-service/Dockerfile container_name: genex-clearing-service ports: - "4004:3004" environment: - NODE_ENV=development - PORT=3004 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy kafka: condition: service_healthy networks: - genex-network compliance-service: build: context: . dockerfile: services/compliance-service/Dockerfile container_name: genex-compliance-service ports: - "4005:3005" environment: - NODE_ENV=development - PORT=3005 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy kafka: condition: service_healthy networks: - genex-network notification-service: build: context: . dockerfile: services/notification-service/Dockerfile container_name: genex-notification-service ports: - "4008:3008" environment: - NODE_ENV=development - PORT=3008 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - KAFKA_BROKERS=kafka:9092 - REDIS_HOST=redis - REDIS_PORT=6379 depends_on: kafka: condition: service_healthy networks: - genex-network # ============================================================ # Telemetry Service (NestJS :3011) - User presence, events, DAU, Prometheus metrics # ============================================================ telemetry-service: build: context: . dockerfile: services/telemetry-service/Dockerfile container_name: genex-telemetry-service ports: - "4011:3011" environment: - NODE_ENV=development - PORT=3011 - SERVICE_NAME=telemetry-service - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy networks: - genex-network # ============================================================ # Admin Service (NestJS :3012) - App version management, OTA updates # ============================================================ admin-service: build: context: . dockerfile: services/admin-service/Dockerfile container_name: genex-admin-service ports: - "4012:3012" environment: - NODE_ENV=development - PORT=3012 - SERVICE_NAME=admin-service - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - MINIO_ENDPOINT=minio - MINIO_PORT=9000 - MINIO_ACCESS_KEY=genex-admin - MINIO_SECRET_KEY=genex-minio-secret - MINIO_BUCKET=app-releases - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy minio: condition: service_healthy networks: - genex-network # ============================================================ # Go Services (3) # ============================================================ trading-service: build: context: ./services/trading-service dockerfile: Dockerfile container_name: genex-trading-service ports: - "4003:3003" environment: - PORT=3003 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy networks: - genex-network translate-service: build: context: ./services/translate-service dockerfile: Dockerfile container_name: genex-translate-service ports: - "4007:3007" environment: - PORT=3007 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - genex-network chain-indexer: build: context: ./services/chain-indexer dockerfile: Dockerfile container_name: genex-chain-indexer ports: - "4009:3009" environment: - PORT=3009 - KAFKA_BROKERS=kafka:9092 - RPC_URL=http://172.17.0.1:8545 - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex depends_on: kafka: condition: service_healthy networks: - genex-network # ============================================================ # Auth Service (NestJS) - JWT dual-token, registration, login, RBAC # ============================================================ auth-service: build: context: . dockerfile: services/auth-service/Dockerfile container_name: genex-auth-service ports: - "4010:3010" environment: - NODE_ENV=development - PORT=3010 - SERVICE_NAME=auth-service - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - REDIS_HOST=redis - REDIS_PORT=6379 - KAFKA_BROKERS=kafka:9092 - JWT_ACCESS_SECRET=dev-access-secret-change-in-production - JWT_ACCESS_EXPIRY=15m - JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production - JWT_REFRESH_EXPIRY=7d depends_on: postgres: condition: service_healthy redis: condition: service_healthy kafka: condition: service_healthy networks: - genex-network # ============================================================ # AI Service (NestJS) - Anti-corruption layer to external AI agent cluster # ============================================================ ai-service: build: context: . dockerfile: services/ai-service/Dockerfile container_name: genex-ai-service ports: - "4006:3006" environment: - NODE_ENV=development - PORT=3006 - SERVICE_NAME=ai-service - DB_HOST=postgres - DB_PORT=5432 - DB_USERNAME=genex - DB_PASSWORD=${DB_PASSWORD} - DB_NAME=genex - KAFKA_BROKERS=kafka:9092 - REDIS_HOST=redis - REDIS_PORT=6379 - AI_AGENT_CLUSTER_URL=http://external-ai-agents:8000 - AI_AGENT_API_KEY=your-ai-agent-api-key - AI_AGENT_TIMEOUT=30000 depends_on: postgres: condition: service_healthy kafka: condition: service_healthy networks: - genex-network volumes: postgres_data: redis_data: kafka_data: minio_data: networks: genex-network: driver: bridge