diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..203b846 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS builder +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy + +# Disable Python downloads, because we want to use the system interpreter +# across both images. If using a managed Python version, it needs to be +# copied from the build image into the final image; see `standalone.Dockerfile` +# for an example. +ENV UV_PYTHON_DOWNLOADS=0 + +# Install git +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --locked --no-install-project --no-dev +COPY . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --locked --no-dev + +# Then, use a final image without uv +FROM python:3.13-slim-bookworm +# It is important to use the image that matches the builder, as the path to the +# Python executable must be the same, e.g., using `python:3.11-slim-bookworm` +# will fail. +RUN apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/* + +# Copy the application from the builder +COPY --from=builder --chown=app:app /app /app +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# Place executables in the environment at the front of the path +ENV PATH="/app/.venv/bin:$PATH" +WORKDIR /app +# Run the FastAPI application by default +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/pyproject.toml b/pyproject.toml index 9c2427e..9239c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,9 +14,9 @@ dependencies = [ "python-dotenv>=1.1.1", "redis[hiredis]>=6.2.0", "sqlalchemy[asyncio]>=2.0.41", - "uvicorn>=0.35.0", - "fastapi-endpoints @ git+https://github.com/vladNed/fastapi-endpoints.git@main" - + "uvicorn[standard]>=0.35.0", + "fastapi-endpoints @ git+https://github.com/vladNed/fastapi-endpoints.git@main", + "uvicorn-worker>=0.3.0", ] [dependency-groups] diff --git a/run/.gitkeep b/run/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..6784366 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,10 @@ +[supervisord] + +[program:uvicorn] +command=/app/.venv/bin/uvicorn main:app --uds /app/run/socket%(process_num)d.sock +numprocs=4 +process_name=uvicorn-%(process_num)d +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stderr_logfile=/dev/stderr diff --git a/uv.lock b/uv.lock index 2019c53..36ce676 100644 --- a/uv.lock +++ b/uv.lock @@ -241,7 +241,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "redis", extra = ["hiredis"] }, { name = "sqlalchemy", extra = ["asyncio"] }, - { name = "uvicorn" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "uvicorn-worker" }, ] [package.dev-dependencies] @@ -262,7 +263,8 @@ requires-dist = [ { name = "python-dotenv", specifier = ">=1.1.1" }, { name = "redis", extras = ["hiredis"], specifier = ">=6.2.0" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.41" }, - { name = "uvicorn", specifier = ">=0.35.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.35.0" }, + { name = "uvicorn-worker", specifier = ">=0.3.0" }, ] [package.metadata.requires-dev] @@ -1088,6 +1090,19 @@ standard = [ { name = "websockets" }, ] +[[package]] +name = "uvicorn-worker" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gunicorn" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/c0/b5df8c9a31b0516a47703a669902b362ca1e569fed4f3daa1d4299b28be0/uvicorn_worker-0.3.0.tar.gz", hash = "sha256:6baeab7b2162ea6b9612cbe149aa670a76090ad65a267ce8e27316ed13c7de7b", size = 9181 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/1f/4e5f8770c2cf4faa2c3ed3c19f9d4485ac9db0a6b029a7866921709bdc6c/uvicorn_worker-0.3.0-py3-none-any.whl", hash = "sha256:ef0fe8aad27b0290a9e602a256b03f5a5da3a9e5f942414ca587b645ec77dd52", size = 5346 }, +] + [[package]] name = "uvloop" version = "0.21.0"