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:
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
dist
|
||||||
|
*.env
|
||||||
|
|
||||||
@@ -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 what’s 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_BASE_URL=http://localhost:8000
|
||||||
|
VITE_WS_BASE_URL=ws://localhost:8000
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_BASE_URL=http://backend:8000
|
||||||
|
VITE_WS_BASE_URL=ws://backend:8000
|
||||||
@@ -35,7 +35,6 @@ Thumbs.db
|
|||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
### Environment Variables ###
|
### Environment Variables ###
|
||||||
.env
|
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
|||||||
@@ -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;"]
|
||||||
|
|
||||||
+12
-7
@@ -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)
|
||||||
|
const env = loadEnv(mode, process.cwd())
|
||||||
|
|
||||||
|
return {
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8000',
|
target: env.VITE_API_BASE_URL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: "ws://localhost:8000",
|
target: env.VITE_WS_BASE_URL,
|
||||||
ws: true,
|
ws: true,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
celery -A backend worker --loglevel=info
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
daphne -b 0.0.0.0 -p 8000 --proxy-headers backend.asgi:application
|
||||||
@@ -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
@@ -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 $?
|
||||||
Reference in New Issue
Block a user