diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5a2cb28 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.github +.env +.ruff_cache +.DS_Store +**/.venv +**/__pycache__ +**/node_modules +data_local +data/processed +ml/data +frontend/dist diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..a18d01b --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,7 @@ +.venv +__pycache__ +**/__pycache__ +.env +.ruff_cache +Dockerfile +.dockerignore diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..50eb893 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.12-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["sh", "-c", "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000"] diff --git a/docker-compose.yml b/docker-compose.yml index dd4364d..26f1c6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,9 @@ +x-app-env: &app-env + env_file: .env + environment: + DATABASE_URL: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} + MODEL_PATH: /models/model.json + services: db: image: postgres:16 @@ -15,5 +21,67 @@ services: timeout: 3s retries: 10 + backend: + build: ./backend + <<: *app-env + ports: + - "8000:8000" + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 10s + restart: unless-stopped + + alert_sender: + build: ./backend + <<: *app-env + command: ["python", "-m", "app.workers.alert_sender"] + depends_on: + backend: + condition: service_healthy + restart: unless-stopped + + simulator: + build: + context: . + dockerfile: ml/Dockerfile + <<: *app-env + command: ["python", "simulator.py", "/app/data/train_FD001.txt"] + depends_on: + backend: + condition: service_healthy + restart: on-failure + + worker: + build: + context: . + dockerfile: ml/Dockerfile + <<: *app-env + command: + - sh + - -c + - "[ -f \"$$MODEL_PATH\" ] || python baseline_model.py /app/data/train_FD001.txt; exec python worker.py" + volumes: + - models:/models + depends_on: + backend: + condition: service_healthy + restart: unless-stopped + + frontend: + build: ./frontend + ports: + - "5173:80" + depends_on: + backend: + condition: service_healthy + restart: unless-stopped + volumes: pgdata: + models: diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..1bf2fcf --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +Dockerfile +.dockerignore diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..556415f --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,16 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM nginx:1.27-alpine + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 80 diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..b8b6758 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 80; + + root /usr/share/nginx/html; + index index.html; + + location /api/ { + proxy_pass http://backend:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location / { + try_files $uri /index.html; + } +} diff --git a/ml/Dockerfile b/ml/Dockerfile new file mode 100644 index 0000000..1e4f3ae --- /dev/null +++ b/ml/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends libgomp1 \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app/ml + +COPY ml/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY data/train_FD001.txt /app/data/train_FD001.txt +COPY ml/ .