diff --git a/Dockerfile b/Dockerfile index f3b0c9a..9eb9911 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,52 @@ -# Stage 1: Build -FROM python:3.12-slim-bookworm AS build +# - Stage 1 -------------------------------------------------------------------- -WORKDIR /app + FROM python:3.12-slim-bookworm AS build -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt + WORKDIR /app -COPY . . + # Install build tools needed to compile some Python packages + RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential gcc && \ + rm -rf /var/lib/apt/lists/* -# Stage 2: Runtime -FROM python:3.12-slim-bookworm AS runtime + # Copy and build all required packages (with dependencies) into wheels + COPY requirements.txt . + RUN pip wheel --no-cache -r requirements.txt -w /app/wheelhouse -WORKDIR /app + # Copy full app source (not strictly needed in build stage unless building static assets) + COPY . . -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +# - Stage 2 -------------------------------------------------------------------- -COPY models ./models -COPY routes ./routes -COPY schemas ./schemas -COPY services ./services -COPY data ./data -COPY main.py . + FROM python:3.12-slim-bookworm AS runtime -# Add non-root 'fastapi' user (optional for hardening) -RUN adduser --disabled-password --gecos '' fastapi \ - && chown -R fastapi:fastapi /app -USER fastapi + WORKDIR /app -EXPOSE 9000 -ENV PYTHONUNBUFFERED=1 + # Only bring in requirements and prebuilt wheels from build stage + COPY requirements.txt . + COPY --from=build /app/wheelhouse /app/wheelhouse -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"] + # Install app dependencies from local wheelhouse + RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt + + # Copy only the necessary runtime source files + COPY models ./models + COPY routes ./routes + COPY schemas ./schemas + COPY services ./services + COPY data ./data + COPY main.py . + + # Add non-root user for security hardening + RUN adduser --disabled-password --gecos '' fastapi && \ + chown -R fastapi:fastapi /app + USER fastapi + + # Prevent Python from buffering stdout/stderr + ENV PYTHONUNBUFFERED=1 + + # Expose FastAPI port + EXPOSE 9000 + + # Start the FastAPI app with Uvicorn + CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"] diff --git a/README.md b/README.md index abbbd42..37c613b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ pip install -r requirements-test.txt uvicorn main:app --reload --port 9000 ``` -## Documentation +## Docs ```console http://localhost:9000/docs @@ -41,6 +41,22 @@ http://localhost:9000/docs ![API Documentation](assets/images/swagger.png) +## Docker + +This project includes a multi-stage `Dockerfile` for local development and production builds. + +### Build the image + +```bash +docker build -t fastapi-app . +``` + +### Run the container + +```bash +docker run -p 9000:9000 fastapi-app +``` + ## Credits The solution has been coded using [Visual Studio Code](https://code.visualstudio.com/) with the official [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) extension.