From b255b3edbe638bb392d59c5cf95f80c39ad3e5e1 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 22 Feb 2026 16:02:34 +0000 Subject: [PATCH] feat: dockerfiles and full docker-compose orchestration Add multi-stage Dockerfiles for Python services (Dockerfile.service) and React dashboard (Dockerfile.dashboard + nginx.conf). Update docker-compose.yml with all seven application services: news-fetcher, sentiment-analyzer, signal-generator, trade-executor, learning-engine, api-gateway, and dashboard. --- docker-compose.yml | 108 ++++++++++++++++++++++++++++++++++++ docker/Dockerfile.dashboard | 34 ++++++++++++ docker/Dockerfile.service | 45 +++++++++++++++ docker/nginx.conf | 63 +++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 docker/Dockerfile.dashboard create mode 100644 docker/Dockerfile.service create mode 100644 docker/nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index 00aaef5..f772ca4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,7 @@ services: + # --------------------------------------------------------------------------- + # Infrastructure + # --------------------------------------------------------------------------- postgres: image: timescale/timescaledb:latest-pg16 environment: @@ -34,6 +37,111 @@ services: volumes: - ollama_models:/root/.ollama + # --------------------------------------------------------------------------- + # Application services + # --------------------------------------------------------------------------- + news-fetcher: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "news" + SERVICE_MODULE: "news_fetcher" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + env_file: .env + restart: unless-stopped + + sentiment-analyzer: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "sentiment" + SERVICE_MODULE: "sentiment_analyzer" + depends_on: + redis: + condition: service_healthy + ollama: + condition: service_started + env_file: .env + restart: unless-stopped + + signal-generator: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "trading" + SERVICE_MODULE: "signal_generator" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + env_file: .env + restart: unless-stopped + + trade-executor: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "trading" + SERVICE_MODULE: "trade_executor" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + env_file: .env + restart: unless-stopped + + learning-engine: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "trading" + SERVICE_MODULE: "learning_engine" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + env_file: .env + restart: unless-stopped + + api-gateway: + build: + context: . + dockerfile: docker/Dockerfile.service + args: + EXTRAS: "api,trading,backtester" + SERVICE_MODULE: "api_gateway" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + ports: + - "8000:8000" + env_file: .env + restart: unless-stopped + + dashboard: + build: + context: . + dockerfile: docker/Dockerfile.dashboard + depends_on: + - api-gateway + ports: + - "3000:80" + restart: unless-stopped + volumes: pgdata: redisdata: diff --git a/docker/Dockerfile.dashboard b/docker/Dockerfile.dashboard new file mode 100644 index 0000000..0ccb223 --- /dev/null +++ b/docker/Dockerfile.dashboard @@ -0,0 +1,34 @@ +# Multi-stage Dockerfile for the React dashboard. +# Stage 1: build the Vite/React app +# Stage 2: serve via nginx + +# --------------------------------------------------------------------------- +# Stage 1: Node build +# --------------------------------------------------------------------------- +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY dashboard/package.json dashboard/package-lock.json ./ +RUN npm ci + +COPY dashboard/ . +RUN npm run build + +# --------------------------------------------------------------------------- +# Stage 2: nginx to serve the static build +# --------------------------------------------------------------------------- +FROM nginx:alpine + +# Remove default nginx site +RUN rm /etc/nginx/conf.d/default.conf + +# Copy custom nginx config +COPY docker/nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built assets from the builder stage +COPY --from=builder /app/dist /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/Dockerfile.service b/docker/Dockerfile.service new file mode 100644 index 0000000..485887b --- /dev/null +++ b/docker/Dockerfile.service @@ -0,0 +1,45 @@ +# Multi-stage Dockerfile for all Python microservices. +# Build args: +# EXTRAS — pip optional-dependency groups (e.g. "news", "sentiment,trading") +# SERVICE_MODULE — Python module name under services/ (e.g. "news_fetcher") + +# --------------------------------------------------------------------------- +# Stage 1: builder — install Python dependencies +# --------------------------------------------------------------------------- +FROM python:3.12-slim AS builder + +WORKDIR /app + +# Copy project metadata and source so pip can resolve the local package +COPY pyproject.toml . +COPY shared/ shared/ +COPY services/ services/ +COPY backtester/ backtester/ +COPY alembic/ alembic/ +COPY alembic.ini . + +ARG EXTRAS="dev" +RUN pip install --no-cache-dir ".[$EXTRAS]" + +# --------------------------------------------------------------------------- +# Stage 2: slim runtime image +# --------------------------------------------------------------------------- +FROM python:3.12-slim + +WORKDIR /app + +# Copy installed packages and CLI entry-points from the builder +COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=builder /usr/local/bin /usr/local/bin + +# Copy application source code +COPY --from=builder /app . + +ARG SERVICE_MODULE="api_gateway" +ENV SERVICE_MODULE=${SERVICE_MODULE} + +# Simple health check — verify the Python process is running +HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ + CMD python -c "import sys; sys.exit(0)" || exit 1 + +CMD python -m services.${SERVICE_MODULE}.main diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..8263bb4 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,63 @@ +# nginx configuration for the trading-bot dashboard. +# Serves the React SPA and proxies API / WebSocket requests to the api-gateway. + +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # --------------------------------------------------------------------------- + # SPA: serve index.html for any path not matching a static file + # --------------------------------------------------------------------------- + location / { + try_files $uri $uri/ /index.html; + } + + # --------------------------------------------------------------------------- + # Proxy /api/* to the api-gateway service + # --------------------------------------------------------------------------- + location /api/ { + proxy_pass http://api-gateway:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # --------------------------------------------------------------------------- + # Proxy /auth/* to the api-gateway service + # --------------------------------------------------------------------------- + location /auth/ { + proxy_pass http://api-gateway:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # --------------------------------------------------------------------------- + # Proxy /health to the api-gateway service + # --------------------------------------------------------------------------- + location /health { + proxy_pass http://api-gateway:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # --------------------------------------------------------------------------- + # WebSocket upgrade for /ws + # --------------------------------------------------------------------------- + location /ws { + proxy_pass http://api-gateway:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 86400; + } +}