# ------------------------------------------------------------------ # CreditRisk AI - full stack # docker compose --profile app up -d # app stack only # docker compose --profile app --profile monitoring up -d # + Prometheus/Grafana # # Memory footprint (with JVM tuning below): ~5.5-6.5 GB total, # fits an Oracle Cloud Always Free ARM VM (2 OCPU / 12 GB). # ------------------------------------------------------------------ services: # Infrastructure postgres: image: postgres:16-alpine environment: POSTGRES_USER: creditrisk POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} POSTGRES_DB: creditrisk ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./infrastructure/docker/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh healthcheck: test: ["CMD-SHELL", "pg_isready -U creditrisk"] interval: 10s timeout: 5s retries: 5 # Keep postgres on the host's native arch so DB performance isn't emulated zookeeper: image: confluentinc/cp-zookeeper:7.5.0 ports: - "2181:2181" environment: ZOOKEEPER_CLIENT_PORT: 2181 ZOOKEEPER_TICK_TIME: 2000 healthcheck: test: ["CMD-SHELL", "echo ruok | nc localhost 2181 || exit 1"] interval: 10s timeout: 5s retries: 5 kafka: image: confluentinc/cp-kafka:7.5.0 depends_on: zookeeper: condition: service_healthy ports: - "9092:9092" environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" KAFKA_HEAP_OPTS: "-Xmx512M -Xms256M" healthcheck: test: ["CMD-SHELL", "kafka-topics --bootstrap-server localhost:9092 --list || exit 1"] interval: 15s timeout: 10s retries: 5 # ML Service ml-service: build: context: ./ml-service ports: - "8001:8001" healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8001/health')"] interval: 15s timeout: 10s retries: 3 # Backend Services customer-service: build: context: . dockerfile: services/customer-service/Dockerfile ports: - "8081:8081" environment: DATABASE_URL: jdbc:postgresql://postgres:5432/creditrisk DATABASE_USERNAME: creditrisk DATABASE_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} KAFKA_BOOTSTRAP_SERVERS: kafka:29092 JWT_SECRET: ${JWT_SECRET:-default-secret-key-that-is-at-least-32-bytes} KAFKA_TOPIC_APPLICATION_EVENTS: application-events KAFKA_TOPIC_RISK_EVENTS: risk-events KAFKA_TOPIC_DECISION_EVENTS: decision-events KAFKA_TOPIC_AUDIT_EVENTS: audit-events JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: postgres: condition: service_healthy kafka: condition: service_healthy application-service: build: context: . dockerfile: services/application-service/Dockerfile ports: - "8082:8082" environment: DATABASE_URL: jdbc:postgresql://postgres:5432/creditrisk_app DATABASE_USERNAME: creditrisk DATABASE_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} KAFKA_BOOTSTRAP_SERVERS: kafka:29092 JWT_SECRET: ${JWT_SECRET:-default-secret-key-that-is-at-least-32-bytes} CUSTOMER_SERVICE_URL: http://customer-service:8081 KAFKA_TOPIC_APPLICATION_EVENTS: application-events KAFKA_TOPIC_RISK_EVENTS: risk-events KAFKA_TOPIC_DECISION_EVENTS: decision-events KAFKA_TOPIC_AUDIT_EVENTS: audit-events JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: postgres: condition: service_healthy kafka: condition: service_healthy risk-service: build: context: . dockerfile: services/risk-service/Dockerfile ports: - "8083:8083" environment: DATABASE_URL: jdbc:postgresql://postgres:5432/creditrisk_risk DATABASE_USERNAME: creditrisk DATABASE_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} KAFKA_BOOTSTRAP_SERVERS: kafka:29092 ML_SERVICE_URL: http://ml-service:8001 KAFKA_TOPIC_APPLICATION_EVENTS: application-events KAFKA_TOPIC_RISK_EVENTS: risk-events KAFKA_TOPIC_DECISION_EVENTS: decision-events KAFKA_TOPIC_AUDIT_EVENTS: audit-events JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: postgres: condition: service_healthy kafka: condition: service_healthy ml-service: condition: service_healthy decision-service: build: context: . dockerfile: services/decision-service/Dockerfile ports: - "8084:8084" environment: DATABASE_URL: jdbc:postgresql://postgres:5432/creditrisk_decision DATABASE_USERNAME: creditrisk DATABASE_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} KAFKA_BOOTSTRAP_SERVERS: kafka:29092 APPLICATION_SERVICE_URL: http://application-service:8082 KAFKA_TOPIC_APPLICATION_EVENTS: application-events KAFKA_TOPIC_RISK_EVENTS: risk-events KAFKA_TOPIC_DECISION_EVENTS: decision-events KAFKA_TOPIC_AUDIT_EVENTS: audit-events JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: postgres: condition: service_healthy kafka: condition: service_healthy audit-service: build: context: . dockerfile: services/audit-service/Dockerfile ports: - "8085:8085" environment: DATABASE_URL: jdbc:postgresql://postgres:5432/creditrisk_audit DATABASE_USERNAME: creditrisk DATABASE_PASSWORD: ${DATABASE_PASSWORD:-creditrisk_secret} KAFKA_BOOTSTRAP_SERVERS: kafka:29092 KAFKA_TOPIC_APPLICATION_EVENTS: application-events KAFKA_TOPIC_RISK_EVENTS: risk-events KAFKA_TOPIC_DECISION_EVENTS: decision-events KAFKA_TOPIC_AUDIT_EVENTS: audit-events JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: postgres: condition: service_healthy kafka: condition: service_healthy api-gateway: build: context: . dockerfile: services/api-gateway/Dockerfile ports: - "8080:8080" environment: CUSTOMER_SERVICE_URL: http://customer-service:8081 APPLICATION_SERVICE_URL: http://application-service:8082 RISK_SERVICE_URL: http://risk-service:8083 DECISION_SERVICE_URL: http://decision-service:8084 AUDIT_SERVICE_URL: http://audit-service:8085 CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000,http://localhost:3001} JAVA_TOOL_OPTIONS: ${JAVA_TOOL_OPTIONS:--Xms256m -Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=75.0} depends_on: customer-service: condition: service_started application-service: condition: service_started # Observability (optional profile - disable to save ~400MB on a small VM) prometheus: image: prom/prometheus:v2.48.0 profiles: ["monitoring"] ports: - "9090:9090" volumes: - ./infrastructure/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' grafana: image: grafana/grafana:10.2.2 profiles: ["monitoring"] ports: - "3001:3000" environment: GF_SECURITY_ADMIN_PASSWORD: admin GF_USERS_ALLOW_SIGN_UP: "false" volumes: - ./infrastructure/grafana/provisioning:/etc/grafana/provisioning - grafana_data:/var/lib/grafana # Frontend frontend: build: context: ./frontend ports: - "3000:3000" depends_on: - api-gateway volumes: postgres_data: grafana_data: