From 8dca6cc8971f2e0963ef6892cda8f86d63334aa7 Mon Sep 17 00:00:00 2001 From: pluja Date: Tue, 21 Nov 2023 17:29:16 +0100 Subject: [PATCH] enhancement: Docker image consolidation --- .dockerignore | 37 +++++++++ Dockerfile | 76 ++++++++++++++++++ Dockerfile.gpu | 94 +++++++++++++++++++++++ backend/Dockerfile | 1 - backend/database/mongo.go | 3 +- backend/main.go | 2 +- docker-compose.dev.yml | 31 ++------ docker-compose.ferret.yml | 158 ++------------------------------------ docker-compose.gpu.yml | 80 +++++++++++++++++++ docker-compose.yml | 144 +++++----------------------------- nginx.conf | 6 +- supervisord.conf | 29 +++++++ 12 files changed, 353 insertions(+), 308 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 Dockerfile.gpu create mode 100644 docker-compose.gpu.yml create mode 100644 supervisord.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..173d42f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,37 @@ +# General files +*.yml +*.log +*.bat +*.md +*.txt +.env* +Dockerfile +.gitignore +.eslintignore +.eslintrc.js +.prettierrc + +# Directories +.github +.git +node_modules +.svelte-kit +__pycache__ +.ruff_cache +.venv +.vscode +whisper_models +whishper_data +misc + +# Recursive patterns +**/node_modules/ +**/__pycache__/ +**/.ruff_cache +**/.venv +**/.vscode +**/whisper_models +**/whishper_data +**/.github +**/.git +**/.svelte-kit \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0ecb9da --- /dev/null +++ b/Dockerfile @@ -0,0 +1,76 @@ +# YT-DLP Download and setup +FROM --platform=$BUILDPLATFORM golang:bookworm AS ytdlp_cache +ARG TARGETOS +ARG TARGETARCH +RUN apt update && apt install -y wget +RUN wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp +RUN chmod a+rx /usr/local/bin/yt-dlp + +# Backend setup +FROM devopsworks/golang-upx:latest as backend-builder + +ENV DEBIAN_FRONTEND noninteractive +WORKDIR /app +COPY ./backend /app +RUN go mod tidy +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o whishper . && \ + upx whishper +RUN chmod a+rx whishper + +# Frontend setup +FROM node:alpine as frontend +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable +COPY ./frontend /app +WORKDIR /app + +FROM frontend AS frontend-prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +FROM frontend AS frontend-build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +ENV BODY_SIZE_LIMIT=0 +RUN pnpm run build + +# Base container +FROM python:3.11-slim as base + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get -qq update \ + && apt-get -qq install --no-install-recommends \ + ffmpeg curl nodejs nginx supervisor \ + && rm -rf /var/lib/apt/lists/* + +# Python service setup +COPY ./transcription-api /app/transcription +WORKDIR /app/transcription +RUN pip3 install -r requirements.txt +RUN pip3 install python-multipart + +# Node.js service setup +ENV BODY_SIZE_LIMIT=0 +COPY ./frontend /app/frontend +COPY --from=frontend-build /app/build /app/frontend +COPY --from=frontend-prod-deps /app/node_modules /app/frontend/node_modules + +# Golang service setup +COPY --from=backend-builder /app/whishper /bin/whishper +RUN chmod a+rx /bin/whishper +COPY --from=ytdlp_cache /usr/local/bin/yt-dlp /bin/yt-dlp + +# Nginx setup +COPY ./nginx.conf /etc/nginx/nginx.conf + +# Set workdir and entrypoint +WORKDIR /app +RUN mkdir /app/uploads + +# Cleanup to make the image smaller +RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* ~/.cache /var/cache + +COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf +ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + +# Expose ports for each service and Nginx +EXPOSE 8080 3000 5000 80 \ No newline at end of file diff --git a/Dockerfile.gpu b/Dockerfile.gpu new file mode 100644 index 0000000..7715fe8 --- /dev/null +++ b/Dockerfile.gpu @@ -0,0 +1,94 @@ +# YT-DLP Download and setup +FROM --platform=$BUILDPLATFORM golang:bookworm AS ytdlp_cache +ARG TARGETOS +ARG TARGETARCH +RUN apt update && apt install -y wget +RUN wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp +RUN chmod a+rx /usr/local/bin/yt-dlp + +# Backend setup +FROM devopsworks/golang-upx:latest as backend-builder + +ENV DEBIAN_FRONTEND noninteractive +WORKDIR /app +COPY ./backend /app +RUN go mod tidy +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o whishper . && \ + upx whishper +RUN chmod a+rx whishper + +# Frontend setup +FROM node:alpine as frontend +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable +COPY ./frontend /app +WORKDIR /app + +FROM frontend AS frontend-prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +FROM frontend AS frontend-build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +ENV BODY_SIZE_LIMIT=0 +RUN pnpm run build + +# Base container +FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04 as base + +ENV PYTHON_VERSION=3.10 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get -qq update \ + && apt-get -qq install --no-install-recommends \ + python${PYTHON_VERSION} \ + python${PYTHON_VERSION}-venv \ + python3-pip \ + ffmpeg \ + curl wget xz-utils \ + ffmpeg nginx supervisor \ + && rm -rf /var/lib/apt/lists/* + +RUN ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python3 && \ + ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \ + ln -s -f /usr/bin/pip3 /usr/bin/pip + +# Python service setup +COPY ./transcription-api /app/transcription +WORKDIR /app/transcription +RUN pip install -r requirements.txt && \ + pip install python-multipart && \ + pip3 install torch --index-url https://download.pytorch.org/whl/cu118 + +# Node.js service setup +RUN wget https://nodejs.org/dist/v20.9.0/node-v20.9.0-linux-x64.tar.xz && \ + tar -xf node-v20.9.0-linux-x64.tar.xz && \ + mv node-v20.9.0-linux-x64 /usr/local/lib/node && \ + rm node-v20.9.0-linux-x64.tar.xz +ENV PATH="/usr/local/lib/node/bin:${PATH}" + +ENV BODY_SIZE_LIMIT=0 +COPY ./frontend /app/frontend +COPY --from=frontend-build /app/build /app/frontend +COPY --from=frontend-prod-deps /app/node_modules /app/frontend/node_modules + +# Golang service setup +COPY --from=backend-builder /app/whishper /bin/whishper +RUN chmod a+rx /bin/whishper +COPY --from=ytdlp_cache /usr/local/bin/yt-dlp /bin/yt-dlp + +# Nginx setup +COPY ./nginx.conf /etc/nginx/nginx.conf + +# Set workdir and entrypoint +WORKDIR /app +RUN mkdir /app/uploads + +# Cleanup to make the image smaller +RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* ~/.cache /var/cache + +COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf +ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + +# Expose ports for each service and Nginx +EXPOSE 8080 3000 5000 80 \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 86b136a..483ebc7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,7 +22,6 @@ ARG TARGETARCH WORKDIR /app COPY --from=builder /app/whishper ./whishper RUN chmod a+rx ./whishper - COPY --from=ytdlp_cache /usr/local/bin/yt-dlp /bin/yt-dlp RUN mkdir /app/uploads diff --git a/backend/database/mongo.go b/backend/database/mongo.go index 165f81b..bd09403 100644 --- a/backend/database/mongo.go +++ b/backend/database/mongo.go @@ -32,7 +32,8 @@ func NewMongoDb() *MongoDb { } // Set auth method to PLAIN if using FerretDB - if os.Getenv("FERRETDB_MONGO_URL") != "" { + if os.Getenv("FERRETDB_ENABLED") != "" { + log.Printf("FerretDB enabled, setting auth mechanism to PLAIN...") credentials.AuthMechanism = "PLAIN" } diff --git a/backend/main.go b/backend/main.go index b3bf2f8..827bff2 100644 --- a/backend/main.go +++ b/backend/main.go @@ -19,7 +19,7 @@ func main() { listenAddr := flag.String("addr", ":8080", "server listen address") uploadDir := flag.String("updir", "/app/uploads", "upload directory") - asrEndpoint := flag.String("asr", "transcription-api:8000", "asr endpoint, i.e. localhost:9888") + asrEndpoint := flag.String("asr", "127.0.0.1:8000", "asr endpoint, i.e. localhost:9888") dbHost := flag.String("db", "mongo:27017", "database endpoint host, i.e. localhost:27017") dbUser := flag.String("dbuser", "root", "database user") dbPass := flag.String("dbpass", "example", "database password") diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 626fd13..cffa703 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,33 +1,14 @@ version: '3.9' -services: - transcription-api-gpu: - build: - context: ./transcription-api - dockerfile: Dockerfile.gpu +services: + translate-cpu: ports: - - 8000:8000 - - transcription-api-cpu: - build: - context: ./transcription-api - dockerfile: Dockerfile - ports: - - 8000:8000 - - translate: + - 5000:5000 + + translate-gpu: ports: - 5000:5000 mongo: ports: - - 27017:27017 - - #whishper-backend: - # build: ./backend - # ports: - # - 8080:8080 - - #whishper-frontend: - # build: - # context: ./frontend \ No newline at end of file + - 27017:27017 \ No newline at end of file diff --git a/docker-compose.ferret.yml b/docker-compose.ferret.yml index fa7027f..fcfd1a3 100644 --- a/docker-compose.ferret.yml +++ b/docker-compose.ferret.yml @@ -15,160 +15,14 @@ services: mongo: image: ghcr.io/ferretdb/ferretdb restart: on-failure - ports: - - 27017:27017 - environment: - - FERRETDB_POSTGRESQL_URL=postgres://postgres:5432/whishper - - translate-cpu: - container_name: whisper-libretranslate - profiles: ["cpu"] - image: libretranslate/libretranslate:latest - restart: unless-stopped - volumes: - - ./whishper_data/libretranslate/data:/home/libretranslate/.local/share - - ./whishper_data/libretranslate/cache:/home/libretranslate/.local/cache + expose: + - 27017 env_file: - .env - tty: true environment: - LT_DISABLE_WEB_UI: True - #LT_UPDATE_MODELS: True - expose: - - 5000 - networks: - default: - aliases: - - translate - healthcheck: - test: ['CMD-SHELL', './venv/bin/python scripts/healthcheck.py'] - interval: 2s - timeout: 3s - retries: 5 + - FERRETDB_POSTGRESQL_URL=postgres://${DB_USER:-whishper}:${DB_PASS:-whishper}@postgres:5432/whishper + command: ["--telemetry=disable"] - translate-gpu: - container_name: whisper-libretranslate - image: libretranslate/libretranslate:latest-cuda - profiles: ["gpu"] - restart: unless-stopped - volumes: - - ./whishper_data/libretranslate/data:/home/libretranslate/.local/share - - ./whishper_data/libretranslate/cache:/home/libretranslate/.local/cache - env_file: - - .env - user: root - tty: true + whishper: environment: - LT_DISABLE_WEB_UI: True - LT_LOAD_ONLY: ${LT_LOAD_ONLY:-en,fr,es} - #LT_UPDATE_MODELS: True - expose: - - 5000 - networks: - default: - aliases: - - translate - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - healthcheck: - test: ['CMD-SHELL', './venv/bin/python scripts/healthcheck.py'] - interval: 2s - timeout: 3s - retries: 5 - - transcription-api-cpu: - container_name: whisper-api - profiles: ["cpu"] - image: pluja/whishper-transcription-api:latest - env_file: - - .env - volumes: - - ./whishper_data/whisper_models:/app/models - - ./whishper_data/uploads:/app/uploads - environment: - - WHISPER_MODELS_DIR=/app/models - - UPLOAD_DIR=/app/uploads - networks: - default: - aliases: - - transcription-api - restart: unless-stopped - expose: - - 8000 - - transcription-api-gpu: - container_name: whisper-api - profiles: ["gpu"] - image: pluja/whishper-transcription-api:latest-gpu - env_file: - - .env - volumes: - - ./whishper_data/whisper_models:/app/models - - ./whishper_data/uploads:/app/uploads - environment: - WHISPER_MODELS_DIR: /app/models - UPLOAD_DIR: /app/uploads - restart: unless-stopped - expose: - - 8000 - networks: - default: - aliases: - - transcription-api - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - - whishper-backend: - container_name: whishper-backend - image: pluja/whishper-backend:latest - env_file: - - .env - volumes: - - ./whishper_data/uploads:/app/uploads - restart: unless-stopped - depends_on: - mongo: - condition: service_healthy - healthcheck: - test: curl --fail http://localhost:8080/api/transcriptions || exit 1 - interval: 2s - timeout: 2s - retries: 5 - expose: - - 8080 - - whishper-frontend: - container_name: whishper-web - image: pluja/whishper-frontend:latest - env_file: - - .env - restart: unless-stopped - environment: - PUBLIC_INTERNAL_API_HOST: "http://nginx:80" - PUBLIC_TRANSLATION_API_HOST: "" - PUBLIC_API_HOST: ${WHISHPER_HOST:-} - PUBLIC_WHISHPER_PROFILE: ${COMPOSE_PROFILES:-false} - expose: - - 3000 - - nginx: - image: nginx:1 - restart: unless-stopped - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./whishper_data/uploads:/app/uploads - depends_on: - whishper-backend: - condition: service_healthy - ports: - - 8082:80 + FERRETDB_ENABLED: "true" diff --git a/docker-compose.gpu.yml b/docker-compose.gpu.yml new file mode 100644 index 0000000..f06ca7f --- /dev/null +++ b/docker-compose.gpu.yml @@ -0,0 +1,80 @@ +version: "3.9" + +services: + mongo: + image: mongo + env_file: + - .env + restart: unless-stopped + volumes: + - ./whishper_data/db_data:/data/db + - ./whishper_data/db_data/logs/:/var/log/mongodb/ + environment: + MONGO_INITDB_ROOT_USERNAME: ${DB_USER:-whishper} + MONGO_INITDB_ROOT_PASSWORD: ${DB_PASS:-whishper} + expose: + - 27017 + command: ['--logpath', '/var/log/mongodb/mongod.log'] + + translate: + container_name: whisper-libretranslate + image: libretranslate/libretranslate:latest-cuda + restart: unless-stopped + volumes: + - ./whishper_data/libretranslate/data:/home/libretranslate/.local/share + - ./whishper_data/libretranslate/cache:/home/libretranslate/.local/cache + env_file: + - .env + user: root + tty: true + environment: + LT_DISABLE_WEB_UI: True + LT_LOAD_ONLY: ${LT_LOAD_ONLY:-en,fr,es} + LT_UPDATE_MODELS: True + expose: + - 5000 + networks: + default: + aliases: + - translate + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + + whishper: + pull_policy: always + image: pluja/whishper:${WHISHPER_VERSION:-latest-gpu} + env_file: + - .env + volumes: + - ./whishper_data/uploads:/app/uploads + - ./whishper_data/logs:/var/log/whishper + container_name: whishper + restart: unless-stopped + networks: + default: + aliases: + - whishper + ports: + - 8082:80 + depends_on: + - mongo + - translate + environment: + PUBLIC_INTERNAL_API_HOST: "http://127.0.0.1:80" + PUBLIC_TRANSLATION_API_HOST: "" + PUBLIC_API_HOST: ${WHISHPER_HOST:-} + PUBLIC_WHISHPER_PROFILE: gpu + WHISPER_MODELS_DIR: /app/models + UPLOAD_DIR: /app/uploads + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] diff --git a/docker-compose.yml b/docker-compose.yml index 42f54c6..26f515c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,15 +15,9 @@ services: expose: - 27017 command: ['--logpath', '/var/log/mongodb/mongod.log'] - healthcheck: - test: mongosh --eval 'db.runCommand("ping").ok' --quiet - interval: 2s - timeout: 3s - retries: 5 - translate-cpu: + translate: container_name: whisper-libretranslate - profiles: ["cpu"] image: libretranslate/libretranslate:latest restart: unless-stopped volumes: @@ -34,7 +28,7 @@ services: tty: true environment: LT_DISABLE_WEB_UI: True - #LT_UPDATE_MODELS: True + LT_UPDATE_MODELS: True expose: - 5000 networks: @@ -47,129 +41,29 @@ services: timeout: 3s retries: 5 - translate-gpu: - container_name: whisper-libretranslate - image: libretranslate/libretranslate:latest-cuda - profiles: ["gpu"] - restart: unless-stopped - volumes: - - ./whishper_data/libretranslate/data:/home/libretranslate/.local/share - - ./whishper_data/libretranslate/cache:/home/libretranslate/.local/cache - env_file: - - .env - user: root - tty: true - environment: - LT_DISABLE_WEB_UI: True - LT_LOAD_ONLY: ${LT_LOAD_ONLY:-en,fr,es} - #LT_UPDATE_MODELS: True - expose: - - 5000 - networks: - default: - aliases: - - translate - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - healthcheck: - test: ['CMD-SHELL', './venv/bin/python scripts/healthcheck.py'] - interval: 2s - timeout: 3s - retries: 5 - - transcription-api-cpu: - container_name: whisper-api - profiles: ["cpu"] - image: pluja/whishper-transcription-api:latest - env_file: - - .env - volumes: - - ./whishper_data/whisper_models:/app/models - - ./whishper_data/uploads:/app/uploads - environment: - - WHISPER_MODELS_DIR=/app/models - - UPLOAD_DIR=/app/uploads - networks: - default: - aliases: - - transcription-api - restart: unless-stopped - expose: - - 8000 - - transcription-api-gpu: - container_name: whisper-api - profiles: ["gpu"] - image: pluja/whishper-transcription-api:latest-gpu - env_file: - - .env - volumes: - - ./whishper_data/whisper_models:/app/models - - ./whishper_data/uploads:/app/uploads - environment: - WHISPER_MODELS_DIR: /app/models - UPLOAD_DIR: /app/uploads - restart: unless-stopped - expose: - - 8000 - networks: - default: - aliases: - - transcription-api - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - - whishper-backend: - container_name: whishper-backend - image: pluja/whishper-backend:latest + whishper: + pull_policy: always + image: pluja/whishper:${WHISHPER_VERSION:-latest} env_file: - .env volumes: - ./whishper_data/uploads:/app/uploads + - ./whishper_data/logs:/var/log/whishper + container_name: whishper restart: unless-stopped + networks: + default: + aliases: + - whishper + ports: + - 8082:80 depends_on: - mongo: - condition: service_healthy - healthcheck: - test: curl --fail http://localhost:8080/api/transcriptions || exit 1 - interval: 2s - timeout: 2s - retries: 5 - expose: - - 8080 - - whishper-frontend: - container_name: whishper-web - image: pluja/whishper-frontend:latest - env_file: - - .env - restart: unless-stopped + - mongo + - translate environment: - PUBLIC_INTERNAL_API_HOST: "http://nginx:80" + PUBLIC_INTERNAL_API_HOST: "http://127.0.0.1:80" PUBLIC_TRANSLATION_API_HOST: "" PUBLIC_API_HOST: ${WHISHPER_HOST:-} - PUBLIC_WHISHPER_PROFILE: ${COMPOSE_PROFILES:-false} - expose: - - 3000 - - nginx: - image: nginx:1 - restart: unless-stopped - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./whishper_data/uploads:/app/uploads - depends_on: - whishper-backend: - condition: service_healthy - ports: - - 8082:80 + PUBLIC_WHISHPER_PROFILE: cpu + WHISPER_MODELS_DIR: /app/models + UPLOAD_DIR: /app/uploads \ No newline at end of file diff --git a/nginx.conf b/nginx.conf index dd73289..c779a3d 100644 --- a/nginx.conf +++ b/nginx.conf @@ -18,18 +18,18 @@ http { location /api { client_max_body_size 0; - proxy_pass http://whishper-backend:8080; + proxy_pass http://127.0.0.1:8080; } location /ws { - proxy_pass http://whishper-backend:8080; + proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location / { - proxy_pass http://whishper-frontend:3000; + proxy_pass http://127.0.0.1:3000; } } } \ No newline at end of file diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..55fa007 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,29 @@ +[supervisord] +nodaemon=true + +[program:transcription] +command=python3 /app/transcription/main.py +directory=/app/transcription +autorestart=true +stderr_logfile=/var/log/whishper/transcription.err.log +stdout_logfile=/var/log/whishper/transcription.out.log + +[program:frontend] +command=node /app/frontend/index.js +directory=/app/frontend +autorestart=true +stderr_logfile=/var/log/whishper/frontend.err.log +stdout_logfile=/var/log/whishper/frontend.out.log + +[program:backend] +command=/bin/whishper -addr :8080 +directory=/app/ +autorestart=true +stderr_logfile=/var/log/whishper/backend.err.log +stdout_logfile=/var/log/whishper/backend.out.log + +[program:nginx] +command=nginx -g 'daemon off;' +autorestart=true +stderr_logfile=/var/log/whishper/nginx.err.log +stdout_logfile=/var/log/whishper/nginx.out.log