Dockerize project with client and backend support (#55)

* set redis backend url automatically for docker builds

* initial docker build config

* rename docker scripts

* fix script paths

* remove old Dockerfiles

* set vite proxy base url depending on mode

* docker build config for client/

* docker production build for client

* refactor docker files

* update nginx config to set maximum file size

* reduce docker image size

* fix demucs bug in docker

* fix proxy timeout

* add gpu capabality for api container

* add compose files for dev and prod

* add healthcheck for freqsplit-api

* add model checkpoints to api image

* set healthcheck retries to 24
This commit is contained in:
2025-08-04 01:43:40 +05:30
committed by GitHub
parent 70818ca117
commit 2d9297d9a3
16 changed files with 278 additions and 17 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules
.git
dist
*.env
+68
View File
@@ -0,0 +1,68 @@
# ---------------------
# Stage 1: Builder
# ---------------------
FROM python:3.12.7 AS builder
# Install build deps
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential libpq-dev git curl \
libffi-dev libssl-dev rustc cargo \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY src/ src/
# Copy metadata and install deps
COPY pyproject.toml requirements.txt ./
RUN pip install --upgrade pip && \
pip install --prefix=/install --no-cache-dir -e .
# --- Patch Demucs bug here ---
# Fixes the following error when using htdemucs model:
# RuntimeError: unsupported operation: more than one element of the written-to tensor refers to a single memory location. Please clone() the tensor before performing the operation.
RUN find /install -type f -path "*/site-packages/demucs/separate.py" \
-exec sed -i 's/wav -= ref.mean()/wav = (wav - ref.mean()).clone()/' {} \;
# Copy source for build-time extras (if needed)
COPY api/ api/
COPY docker/daphne api/
COPY docker/celery api/
COPY docker/wrapper api/
# ---------------------
# Stage 2: Runtime
# ---------------------
FROM python:3.12.7
# Install runtime system packages (only whats needed)
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev curl libffi-dev libssl-dev wget\
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy only installed site-packages and app
COPY --from=builder /install /usr/local
COPY api/ api/
COPY src/ src/
COPY pytest.ini .
COPY tests/ tests/
COPY LICENSE .
COPY docker/daphne api/
COPY docker/celery api/
COPY docker/wrapper api/
# Test packages and download model checkpoints
RUN pytest
RUN rm pytest.ini
RUN rm -rf tests/
# Set working dir for backend
WORKDIR /app/api
ENV CELERY_BROKER_URL=redis://redis:6379/0
EXPOSE 8000
CMD ./wrapper
+3 -1
View File
@@ -1,3 +1,5 @@
import os
""" """
Django settings for backend project. Django settings for backend project.
@@ -141,7 +143,7 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# COnfigure Redis as message broker # COnfigure Redis as message broker
CELERY_BROKER_URL = 'redis://localhost:6379/0' CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', 'redis://localhost:6379/0')
CELERY_ACCEPT_CONTENT = ['json'] CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json' CELERY_TASK_SERIALIZER = 'json'
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
+2
View File
@@ -0,0 +1,2 @@
VITE_API_BASE_URL=http://localhost:8000
VITE_WS_BASE_URL=ws://localhost:8000
+2
View File
@@ -0,0 +1,2 @@
VITE_API_BASE_URL=http://backend:8000
VITE_WS_BASE_URL=ws://backend:8000
-1
View File
@@ -35,7 +35,6 @@ Thumbs.db
.vite/ .vite/
### Environment Variables ### ### Environment Variables ###
.env
.env.local .env.local
.env.*.local .env.*.local
+27
View File
@@ -0,0 +1,27 @@
FROM node:20 AS builder
WORKDIR /app
# Copy and install dependencies
COPY client/package.json client/package-lock.json* ./
RUN npm install
# Copy the source code
COPY client/ .
# Build the app using docker mode env
ENV NODE_ENV=production
RUN npm run build -- --mode docker
# Stage 2: Serve with nginx
FROM nginx:stable-alpine AS production
# Copy built frontend from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+20 -15
View File
@@ -1,20 +1,25 @@
import { defineConfig } from 'vite' import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
// https://vite.dev/config/ export default defineConfig(({ mode }) => {
export default defineConfig({ // Load env file based on mode (e.g. development, production, docker)
plugins: [react()], const env = loadEnv(mode, process.cwd())
server: {
proxy: { return {
'/api': { plugins: [react()],
target: 'http://localhost:8000', server: {
changeOrigin: true, proxy: {
'/api': {
target: env.VITE_API_BASE_URL,
changeOrigin: true,
},
'/ws': {
target: env.VITE_WS_BASE_URL,
ws: true,
changeOrigin: true,
},
}, },
'/ws': {
target: "ws://localhost:8000",
ws: true,
changeOrigin: true,
}
}, },
}, }
}) })
+48
View File
@@ -0,0 +1,48 @@
services:
backend:
build:
context: .
dockerfile: api/Dockerfile
image: joelmathewthomas/freqsplit-api:latest
container_name: freqsplit-api
depends_on:
- redis
networks:
- freqnet
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/ping"]
interval: 5s
timeout: 3s
retries: 24
redis:
image: redis:7
container_name: freqsplit-redis
networks:
- freqnet
frontend:
build:
context: .
dockerfile: client/Dockerfile
image: joelmathewthomas/freqsplit-client:latest
container_name: freqsplit-client
ports:
- "80:80"
networks:
- freqnet
depends_on:
backend:
condition: service_healthy
networks:
freqnet:
driver: bridge
+40
View File
@@ -0,0 +1,40 @@
services:
backend:
build:
context: .
dockerfile: api/Dockerfile
image: joelmathewthomas/freqsplit-api:latest
container_name: freqsplit-api
depends_on:
- redis
networks:
- freqnet
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/ping"]
interval: 5s
timeout: 3s
retries: 24
redis:
image: redis:7
container_name: freqsplit-redis
networks:
- freqnet
frontend:
build:
context: .
dockerfile: client/Dockerfile
image: joelmathewthomas/freqsplit-client:latest
container_name: freqsplit-client
ports:
- "80:80"
networks:
- freqnet
depends_on:
backend:
condition: service_healthy
networks:
freqnet:
driver: bridge
+9
View File
@@ -0,0 +1,9 @@
services:
backend:
image: ghcr.io/joelmathewthomas/freqsplit-api:latest
build: null
frontend:
image: ghcr.io/joelmathewthomas/freqsplit-client:latest
build: null
+9
View File
@@ -0,0 +1,9 @@
services:
backend:
image: ghcr.io/joelmathewthomas/freqsplit-api:latest
build: null
frontend:
image: ghcr.io/joelmathewthomas/freqsplit-client:latest
build: null
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
celery -A backend worker --loglevel=info
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
daphne -b 0.0.0.0 -p 8000 --proxy-headers backend.asgi:application
+28
View File
@@ -0,0 +1,28 @@
server {
listen 80;
server_name _;
client_max_body_size 100M;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://backend:8000;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
location /ws/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# Start the first process
./celery &
# Start the second process
./daphne &
# Wait for any process to exit
wait -n
# Exit with status of process that exited first
exit $?