|
1 | | -# Use an official Python runtime as a parent image |
2 | | -ARG PYTHON_VERSION=3.11 |
3 | | -FROM python:${PYTHON_VERSION}-slim-bullseye AS builder |
| 1 | +# ============================================================================= |
| 2 | +# Stage 1: Build stage - install dependencies using Debian Python for distroless compatibility |
| 3 | +# ============================================================================= |
| 4 | +FROM debian:bookworm-slim AS builder |
| 5 | + |
| 6 | +# Install Python, pip, git and build essentials in a single layer |
| 7 | +RUN apt-get update && \ |
| 8 | + apt-get install -y --no-install-recommends \ |
| 9 | + python3 \ |
| 10 | + python3-pip \ |
| 11 | + python3-venv \ |
| 12 | + git \ |
| 13 | + ca-certificates && \ |
| 14 | + rm -rf /var/lib/apt/lists/* |
4 | 15 |
|
5 | | -# Set the working directory in the container to /app |
6 | 16 | WORKDIR /app |
7 | 17 |
|
8 | | -ARG GO_VERSION=1.21.6 |
| 18 | +# Install Poetry in a separate venv to avoid PEP 668 issues |
| 19 | +RUN python3 -m venv /opt/poetry && \ |
| 20 | + /opt/poetry/bin/pip install --no-cache-dir poetry && \ |
| 21 | + ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry |
| 22 | + |
| 23 | +# Configure Poetry to create venv in project |
| 24 | +ENV POETRY_NO_INTERACTION=1 \ |
| 25 | + POETRY_VIRTUALENVS_IN_PROJECT=true \ |
| 26 | + POETRY_VIRTUALENVS_CREATE=true \ |
| 27 | + POETRY_CACHE_DIR=/tmp/poetry_cache |
| 28 | + |
| 29 | +# Copy only dependency files first (better layer caching) |
| 30 | +COPY pyproject.toml poetry.lock* ./ |
| 31 | + |
| 32 | +# Install dependencies (without dev dependencies) |
| 33 | +RUN poetry install --only main --no-root --no-ansi && \ |
| 34 | + rm -rf $POETRY_CACHE_DIR && \ |
| 35 | + # Remove pip + wheel (keep setuptools for runtime compatibility) |
| 36 | + /app/.venv/bin/pip uninstall -y pip wheel && \ |
| 37 | + find /app/.venv -type d -name "__pycache__" -exec rm -rf {} + && \ |
| 38 | + # Trim non-runtime content from installed packages |
| 39 | + SITE_PACKAGES="$(/app/.venv/bin/python -c 'import site; print(site.getsitepackages()[0])')" && \ |
| 40 | + find "$SITE_PACKAGES" -type d \( \ |
| 41 | + -name "tests" -o -name "test" -o \ |
| 42 | + -name "docs" -o -name "doc" -o \ |
| 43 | + -name "examples" -o -name "example" -o \ |
| 44 | + -name "__pycache__" \ |
| 45 | + \) -prune -exec rm -rf '{}' + && \ |
| 46 | + find "$SITE_PACKAGES" -type f -name "*.pyc" -delete |
| 47 | + |
| 48 | +# Copy application code (kept after dependency install for better caching) |
| 49 | +COPY . /app |
| 50 | + |
| 51 | +# Create files/dirs that the distroless runtime cannot create (no shell) |
| 52 | +RUN touch /app/app.log |
| 53 | + |
| 54 | +# ============================================================================= |
| 55 | +# Stage 2: Runtime stage - Google distroless Python image |
| 56 | +# ============================================================================= |
| 57 | +# NOTE: This image does NOT include Git or Hugo. The 'ingest' operation |
| 58 | +# will NOT work. Use the 'runtime-full' target if you need ingest capability. |
| 59 | +# ============================================================================= |
| 60 | +FROM gcr.io/distroless/python3-debian12:nonroot AS runtime |
| 61 | + |
| 62 | +# Copy the prebuilt app (code + venv + log file) from the builder. |
| 63 | +# Use UID/GID 1000 to stay compatible with typical host-mounted volumes. |
| 64 | +COPY --from=builder --chown=1000:1000 /app /app |
| 65 | + |
| 66 | +WORKDIR /app |
| 67 | + |
| 68 | +# Set PATH to use the venv's python executable |
| 69 | +ENV PATH="/app/.venv/bin:$PATH" \ |
| 70 | + PYTHONDONTWRITEBYTECODE=1 \ |
| 71 | + PYTHONUNBUFFERED=1 \ |
| 72 | + PYTHONPATH="/app/.venv/lib/python3.11/site-packages:/app" |
| 73 | + |
| 74 | +# Explicitly define the user |
| 75 | +USER 1000:1000 |
| 76 | + |
| 77 | +# Run main.py using the venv python |
| 78 | +ENTRYPOINT ["/app/.venv/bin/python", "main.py"] |
| 79 | + |
| 80 | +# ============================================================================= |
| 81 | +# Stage 3: Full runtime with Git and Hugo (for ingest capability) |
| 82 | +# ============================================================================= |
| 83 | +FROM debian:bookworm-slim AS runtime-full |
| 84 | + |
9 | 85 | ARG HUGO_VERSION=0.121.2 |
10 | 86 | ARG TARGETARCH |
11 | 87 |
|
12 | | -# install wget and git |
13 | | -RUN apt-get update -y \ |
14 | | - && apt-get upgrade -y \ |
15 | | - && apt-get install -y git wget \ |
16 | | - && apt-get clean \ |
17 | | - && rm -rf /var/lib/apt/lists/* |
| 88 | +WORKDIR /app |
| 89 | + |
| 90 | +# Install runtime dependencies |
| 91 | +RUN apt-get update && \ |
| 92 | + apt-get install -y --no-install-recommends \ |
| 93 | + python3 \ |
| 94 | + git \ |
| 95 | + ca-certificates && \ |
| 96 | + rm -rf /var/lib/apt/lists/* |
18 | 97 |
|
19 | | -RUN wget https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz && tar -C /usr/local -xzf go${GO_VERSION}.linux-${TARGETARCH}.tar.gz |
| 98 | +# Install Hugo |
| 99 | +ADD https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-${TARGETARCH}.tar.gz /tmp/hugo.tar.gz |
| 100 | +RUN tar -C /usr/local/bin -xzf /tmp/hugo.tar.gz hugo \ |
| 101 | + && rm /tmp/hugo.tar.gz \ |
| 102 | + && hugo version |
20 | 103 |
|
21 | | -ENV PATH="/usr/local/go/bin:/usr/local:${PATH}" |
22 | | -RUN go version |
| 104 | +# Create non-root user |
| 105 | +RUN useradd --create-home --uid 1000 --shell /bin/bash appuser |
23 | 106 |
|
24 | | -RUN wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-${TARGETARCH}.tar.gz \ |
25 | | - && tar -C /bin -xzf hugo_extended_${HUGO_VERSION}_linux-${TARGETARCH}.tar.gz \ |
26 | | - && rm hugo_extended_${HUGO_VERSION}_linux-${TARGETARCH}.tar.gz |
| 107 | +# Copy virtual environment from builder |
| 108 | +COPY --from=builder --chown=appuser:appuser /app/.venv /app/.venv |
27 | 109 |
|
28 | | -RUN hugo version |
| 110 | +# Copy application code |
| 111 | +COPY --chown=appuser:appuser . . |
29 | 112 |
|
30 | | -# Install Poetry |
31 | | -RUN pip install poetry |
| 113 | +# Create a writable log file for the appuser |
| 114 | +RUN touch /app/app.log && chown appuser:appuser /app/app.log |
32 | 115 |
|
33 | | -# Copy the current directory contents into the container at /app |
34 | | -COPY . /app |
| 116 | +# Set environment variables |
| 117 | +ENV VIRTUAL_ENV=/app/.venv \ |
| 118 | + PATH="/app/.venv/bin:$PATH" \ |
| 119 | + PYTHONDONTWRITEBYTECODE=1 \ |
| 120 | + PYTHONUNBUFFERED=1 |
35 | 121 |
|
36 | | -# # Use Poetry to install dependencies |
37 | | -RUN poetry config virtualenvs.create true && poetry install --no-interaction --no-ansi |
| 122 | +# Switch to non-root user |
| 123 | +USER appuser |
38 | 124 |
|
39 | 125 | # Run main.py when the container launches |
40 | | -CMD ["poetry", "run", "python", "main.py"] |
| 126 | +ENTRYPOINT ["/app/.venv/bin/python", "main.py"] |
0 commit comments