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.
|
||||
|
||||
@@ -141,7 +143,7 @@ STATIC_URL = 'static/'
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# 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_TASK_SERIALIZER = 'json'
|
||||
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/
|
||||
|
||||
### Environment Variables ###
|
||||
.env
|
||||
.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;"]
|
||||
|
||||
+20
-15
@@ -1,20 +1,25 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
export default defineConfig(({ mode }) => {
|
||||
// Load env file based on mode (e.g. development, production, docker)
|
||||
const env = loadEnv(mode, process.cwd())
|
||||
|
||||
return {
|
||||
plugins: [react()],
|
||||
server: {
|
||||
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,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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