From 74879df442ce8fe135c889ff2ec67b66fc816878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Tue, 28 Feb 2023 20:22:58 +0100 Subject: [PATCH] feat: add basic production Dockerfiles closes #8 --- Makefile | 10 ++--- app/shared/db/base.py | 3 +- docker/dev/web.Dockerfile | 10 ++--- docker/dev/worker.Dockerfile | 10 ++--- docker/prod/.env.example | 2 + docker/prod/docker-compose.yml | 69 ++++++++++++++++++++++++++++++++++ docker/prod/web.Dockerfile | 23 ++++++++++++ docker/prod/worker.Dockerfile | 30 +++++++++++++++ pyproject.toml | 3 +- 9 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 docker/prod/.env.example create mode 100644 docker/prod/docker-compose.yml create mode 100644 docker/prod/web.Dockerfile create mode 100644 docker/prod/worker.Dockerfile diff --git a/Makefile b/Makefile index 3ad6fc6..eb571dc 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ clean: - docker-compose -f docker/dev/docker-compose.yml down --volumes --remove-orphans + docker compose -f docker/dev/docker-compose.yml down --volumes --remove-orphans dev: - docker-compose -f docker/dev/docker-compose.yml build --progress tty - docker-compose -f docker/dev/docker-compose.yml up --remove-orphans + docker compose -f docker/dev/docker-compose.yml build + docker compose -f docker/dev/docker-compose.yml up --remove-orphans fmt: black app @@ -17,5 +17,5 @@ test: pytest run: - docker-compose -f docker/prod/docker-compose.yml build --progress tty - docker-compose -f docker/prod/docker-compose.yml up --remove-orphans + docker compose -f docker/prod/docker-compose.yml build + docker compose -f docker/prod/docker-compose.yml up --remove-orphans diff --git a/app/shared/db/base.py b/app/shared/db/base.py index 2c732df..e949340 100644 --- a/app/shared/db/base.py +++ b/app/shared/db/base.py @@ -1,6 +1,5 @@ from typing import Any, Generator -from sqlalchemy.engine import Connection from sqlalchemy import create_engine, event from sqlalchemy.orm import Session, sessionmaker @@ -10,7 +9,7 @@ engine = create_engine(settings.DATABASE_URI, connect_args={"check_same_thread": @event.listens_for(engine, "connect") -def set_sqlite_pragma(conn: Connection, _: Any) -> None: +def set_sqlite_pragma(conn: Any, _: Any) -> None: cursor = conn.cursor() cursor.execute("PRAGMA journal_mode=WAL") cursor.close() diff --git a/docker/dev/web.Dockerfile b/docker/dev/web.Dockerfile index 77e2d12..b373bec 100644 --- a/docker/dev/web.Dockerfile +++ b/docker/dev/web.Dockerfile @@ -1,13 +1,11 @@ -FROM python:3.10 AS compile-image +FROM python:3.10 WORKDIR /code +ENV PYTHONIOENCODING=utf-8 +ENV PATH=/root/.local/bin:$PATH + COPY pyproject.toml . RUN pip install --no-cache-dir --user .[web] -ENV PYTHONIOENCODING=utf-8 -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 -ENV PATH=/root/.local/bin:$PATH - CMD alembic upgrade head && uvicorn app.web.main:app --reload --host ${HOST:-0.0.0.0} --port ${PORT:-80} --log-level info diff --git a/docker/dev/worker.Dockerfile b/docker/dev/worker.Dockerfile index dd19ddb..8ad134f 100644 --- a/docker/dev/worker.Dockerfile +++ b/docker/dev/worker.Dockerfile @@ -1,9 +1,12 @@ -FROM python:3.10 AS compile-image +FROM python:3.10 ARG WHISPER_MODEL WORKDIR /code +ENV PYTHONIOENCODING=utf-8 +ENV PATH=/root/.local/bin:$PATH + COPY --from=mwader/static-ffmpeg:5.1.2 /ffmpeg /usr/local/bin/ COPY --from=mwader/static-ffmpeg:5.1.2 /ffprobe /usr/local/bin/ @@ -13,9 +16,4 @@ RUN pip install --no-cache-dir --user .[worker,worker_dev] COPY scripts/download_model.py . RUN chmod +x download_model.py && python download_model.py ${WHISPER_MODEL} -ENV PYTHONIOENCODING=utf-8 -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 -ENV PATH=/root/.local/bin:$PATH - ENTRYPOINT ["watchmedo", "auto-restart", "-d" , "app/worker", "-p", "*.py", "--recursive", "celery", "--", "--app=app.worker.main.celery", "worker", "--loglevel=info", "--concurrency=1"] diff --git a/docker/prod/.env.example b/docker/prod/.env.example new file mode 100644 index 0000000..801b483 --- /dev/null +++ b/docker/prod/.env.example @@ -0,0 +1,2 @@ +API_SECRET="change_me" +WHISPER_MODEL="small" diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml new file mode 100644 index 0000000..1afc165 --- /dev/null +++ b/docker/prod/docker-compose.yml @@ -0,0 +1,69 @@ +version: "3.8" + +x-app-variables: &app-variables + DATABASE_URI: sqlite:////etc/whisperbox/data/whisperbox.sqlite + ENVIRONMENT: production + BROKER_URL: redis://redis:6379/0 + +services: + redis: + container_name: whisperbox_redis + image: redis:7-alpine + ports: + - 6379:6379 + networks: + - app + deploy: + resources: + limits: + memory: 128M + + worker: + container_name: whisperbox_worker + build: + context: ../../ + # ENABLE GPU SUPPORT + # dockerfile: docker/prod/worker.gpu.Dockerfile + dockerfile: docker/prod/worker.Dockerfile + environment: *app-variables + env_file: .env + volumes: + - whisperbox-data:/etc/whisperbox/data + networks: + - app + depends_on: + - redis + # ENABLE GPU SUPPORT + # deploy: + # resources: + # reservations: + # devices: + # - driver: nvidia + # count: 1 + # capabilities: [gpu] + + # TODO: reverse proxy + web: + container_name: whisperbox_web + build: + context: ../../ + dockerfile: docker/prod/web.Dockerfile + environment: *app-variables + env_file: .env + ports: + - "8000:8000" + networks: + - app + depends_on: + - worker + volumes: + - whisperbox-data:/etc/whisperbox/data + - whisper-models:/models + +volumes: + whisperbox-data: + whisper-models: + +networks: + app: + driver: bridge diff --git a/docker/prod/web.Dockerfile b/docker/prod/web.Dockerfile new file mode 100644 index 0000000..c1ebec2 --- /dev/null +++ b/docker/prod/web.Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.10 as python-build + +WORKDIR /etc/whisperbox + +COPY pyproject.toml . + +RUN python -m venv /opt/venv && \ + /opt/venv/bin/pip install -U pip wheel && \ + /opt/venv/bin/pip install -U .[web] + +FROM python:3.10 as python-deploy + +WORKDIR /etc/whisperbox + +COPY --from=python-build /opt/venv /opt/venv + +COPY app ./app +COPY alembic.ini ./ + +ENV VIRTUAL_ENV /opt/venv +ENV PATH /opt/venv/bin:$PATH + +CMD alembic upgrade head && gunicorn -k uvicorn.workers.UvicornWorker app.web.main:app --bind ${HOST:-0.0.0.0}:${PORT:-8000} --log-level info diff --git a/docker/prod/worker.Dockerfile b/docker/prod/worker.Dockerfile new file mode 100644 index 0000000..73f8dc3 --- /dev/null +++ b/docker/prod/worker.Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.10 AS python-build + +WORKDIR /etc/whisperbox + +COPY pyproject.toml . + +RUN python -m venv /opt/venv && \ + /opt/venv/bin/pip install -U pip wheel && \ + /opt/venv/bin/pip install -U .[worker] + +FROM python:3.10 as python-deploy + +ARG WHISPER_MODEL + +WORKDIR /etc/whisperbox + +COPY --from=python-build /opt/venv /opt/venv + +COPY --from=mwader/static-ffmpeg:latest /ffmpeg /usr/local/bin/ +COPY --from=mwader/static-ffmpeg:latest /ffprobe /usr/local/bin/ + +COPY app ./app + +ENV VIRTUAL_ENV /opt/venv +ENV PATH /opt/venv/bin:$PATH + +# COPY scripts/download_model.py . +# RUN chmod +x download_model.py && python download_model.py ${WHISPER_MODEL:-small} + +CMD celery --app=app.worker.main.celery worker --loglevel=info --concurrency=1 diff --git a/pyproject.toml b/pyproject.toml index d81262d..35217de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,8 @@ dependencies=[ web=[ "alembic ==1.9.3", "fastapi ==0.90.0", - "uvicorn[standard] ==0.20.0" + "uvicorn[standard] ==0.20.0", + "gunicorn ==20.1.0" ] worker=[