version: '3.8' services: # Nginx Load Balancer nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/ssl:/etc/nginx/ssl depends_on: - api-gateway-1 - api-gateway-2 restart: always # API Gateway Cluster api-gateway-1: image: women-safety/api-gateway:${TAG:-latest} environment: - NODE_ID=1 - USER_SERVICE_URL=http://user-service-1:8001,http://user-service-2:8001 - EMERGENCY_SERVICE_URL=http://emergency-service-1:8002,http://emergency-service-2:8002 - LOCATION_SERVICE_URL=http://location-service-1:8003,http://location-service-2:8003 - CALENDAR_SERVICE_URL=http://calendar-service-1:8004,http://calendar-service-2:8004 - NOTIFICATION_SERVICE_URL=http://notification-service-1:8005,http://notification-service-2:8005 - REDIS_URL=redis://redis-cluster:6379/0 depends_on: - redis-cluster restart: always deploy: resources: limits: cpus: '1.0' memory: 1G reservations: cpus: '0.5' memory: 512M api-gateway-2: image: women-safety/api-gateway:${TAG:-latest} environment: - NODE_ID=2 - USER_SERVICE_URL=http://user-service-1:8001,http://user-service-2:8001 - EMERGENCY_SERVICE_URL=http://emergency-service-1:8002,http://emergency-service-2:8002 - LOCATION_SERVICE_URL=http://location-service-1:8003,http://location-service-2:8003 - CALENDAR_SERVICE_URL=http://calendar-service-1:8004,http://calendar-service-2:8004 - NOTIFICATION_SERVICE_URL=http://notification-service-1:8005,http://notification-service-2:8005 - REDIS_URL=redis://redis-cluster:6379/0 depends_on: - redis-cluster restart: always deploy: resources: limits: cpus: '1.0' memory: 1G # User Service Cluster user-service-1: image: women-safety/user-service:${TAG:-latest} environment: - NODE_ID=1 - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-primary:5432/women_safety_prod - DATABASE_REPLICA_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-replica:5432/women_safety_prod - REDIS_URL=redis://redis-cluster:6379/1 - JWT_SECRET_KEY=${JWT_SECRET_KEY} depends_on: - postgres-primary - redis-cluster restart: always deploy: resources: limits: cpus: '1.5' memory: 2G reservations: cpus: '0.5' memory: 512M user-service-2: image: women-safety/user-service:${TAG:-latest} environment: - NODE_ID=2 - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-primary:5432/women_safety_prod - DATABASE_REPLICA_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-replica:5432/women_safety_prod - REDIS_URL=redis://redis-cluster:6379/1 - JWT_SECRET_KEY=${JWT_SECRET_KEY} depends_on: - postgres-primary - redis-cluster restart: always deploy: resources: limits: cpus: '1.5' memory: 2G # Emergency Service Cluster (High Priority) emergency-service-1: image: women-safety/emergency-service:${TAG:-latest} environment: - NODE_ID=1 - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-primary:5432/women_safety_prod - REDIS_URL=redis://redis-cluster:6379/2 - KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092 - LOCATION_SERVICE_URL=http://location-service-1:8003,http://location-service-2:8003 - NOTIFICATION_SERVICE_URL=http://notification-service-1:8005,http://notification-service-2:8005 depends_on: - postgres-primary - redis-cluster - kafka-1 restart: always deploy: resources: limits: cpus: '2.0' memory: 3G reservations: cpus: '1.0' memory: 1G emergency-service-2: image: women-safety/emergency-service:${TAG:-latest} environment: - NODE_ID=2 - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres-primary:5432/women_safety_prod - REDIS_URL=redis://redis-cluster:6379/2 - KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092 - LOCATION_SERVICE_URL=http://location-service-1:8003,http://location-service-2:8003 - NOTIFICATION_SERVICE_URL=http://notification-service-1:8005,http://notification-service-2:8005 depends_on: - postgres-primary - redis-cluster - kafka-1 restart: always deploy: resources: limits: cpus: '2.0' memory: 3G # Database Cluster postgres-primary: image: postgres:15 environment: POSTGRES_DB: women_safety_prod POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_REPLICATION_MODE: master POSTGRES_REPLICATION_USER: replicator POSTGRES_REPLICATION_PASSWORD: ${REPLICATION_PASSWORD} volumes: - postgres_primary_data:/var/lib/postgresql/data - ./postgres/postgresql.conf:/etc/postgresql/postgresql.conf command: postgres -c config_file=/etc/postgresql/postgresql.conf restart: always deploy: resources: limits: cpus: '4.0' memory: 8G reservations: cpus: '2.0' memory: 4G postgres-replica: image: postgres:15 environment: POSTGRES_DB: women_safety_prod POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_MASTER_SERVICE: postgres-primary POSTGRES_REPLICATION_MODE: slave POSTGRES_REPLICATION_USER: replicator POSTGRES_REPLICATION_PASSWORD: ${REPLICATION_PASSWORD} volumes: - postgres_replica_data:/var/lib/postgresql/data depends_on: - postgres-primary restart: always deploy: resources: limits: cpus: '3.0' memory: 6G # Redis Cluster redis-cluster: image: redis:7-alpine command: redis-server --appendonly yes --cluster-enabled yes volumes: - redis_data:/data restart: always deploy: resources: limits: cpus: '1.0' memory: 2G # Kafka Cluster kafka-1: image: confluentinc/cp-kafka:latest environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-1:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_MIN_INSYNC_REPLICAS: 2 volumes: - kafka_1_data:/var/lib/kafka/data depends_on: - zookeeper restart: always kafka-2: image: confluentinc/cp-kafka:latest environment: KAFKA_BROKER_ID: 2 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-2:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_MIN_INSYNC_REPLICAS: 2 volumes: - kafka_2_data:/var/lib/kafka/data depends_on: - zookeeper restart: always kafka-3: image: confluentinc/cp-kafka:latest environment: KAFKA_BROKER_ID: 3 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-3:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_MIN_INSYNC_REPLICAS: 2 volumes: - kafka_3_data:/var/lib/kafka/data depends_on: - zookeeper restart: always zookeeper: image: confluentinc/cp-zookeeper:latest environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_TICK_TIME: 2000 volumes: - zookeeper_data:/var/lib/zookeeper restart: always # Monitoring prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus restart: always grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} volumes: - grafana_data:/var/lib/grafana - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards depends_on: - prometheus restart: always volumes: postgres_primary_data: postgres_replica_data: redis_data: kafka_1_data: kafka_2_data: kafka_3_data: zookeeper_data: prometheus_data: grafana_data: