72 lines
2.8 KiB
Docker
72 lines
2.8 KiB
Docker
|
|
# syntax=docker/dockerfile:1.7
|
||
|
|
# =============================================================================
|
||
|
|
# Django API image.
|
||
|
|
#
|
||
|
|
# Two stages: wheels are built with a compiler present, the runtime image gets
|
||
|
|
# only the compiled artefacts. The result is small, has no build toolchain to
|
||
|
|
# exploit, and runs as an unprivileged user on a read-only-friendly layout.
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
# Stage 1 - build wheels
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
FROM python:3.12-slim-bookworm AS builder
|
||
|
|
|
||
|
|
ENV PIP_NO_CACHE_DIR=1 \
|
||
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||
|
|
|
||
|
|
RUN apt-get update \
|
||
|
|
&& apt-get install --no-install-recommends -y build-essential libpq-dev \
|
||
|
|
&& rm -rf /var/lib/apt/lists/*
|
||
|
|
|
||
|
|
# The build context is the repository root so the image can also carry the
|
||
|
|
# dataset; paths below are therefore prefixed with the directory name.
|
||
|
|
WORKDIR /build
|
||
|
|
COPY backend/requirements/ requirements/
|
||
|
|
RUN pip wheel --wheel-dir /wheels -r requirements/base.txt
|
||
|
|
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
# Stage 2 - runtime
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
FROM python:3.12-slim-bookworm AS runtime
|
||
|
|
|
||
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||
|
|
PYTHONUNBUFFERED=1 \
|
||
|
|
PIP_NO_CACHE_DIR=1 \
|
||
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||
|
|
DJANGO_SETTINGS_MODULE=config.settings.prod \
|
||
|
|
PATH="/home/app/.local/bin:${PATH}"
|
||
|
|
|
||
|
|
# libpq5 is the only runtime library psycopg needs; curl backs the healthcheck.
|
||
|
|
RUN apt-get update \
|
||
|
|
&& apt-get install --no-install-recommends -y libpq5 curl \
|
||
|
|
&& rm -rf /var/lib/apt/lists/* \
|
||
|
|
&& groupadd --gid 10001 app \
|
||
|
|
&& useradd --uid 10001 --gid app --create-home --shell /usr/sbin/nologin app
|
||
|
|
|
||
|
|
COPY --from=builder /wheels /wheels
|
||
|
|
COPY backend/requirements/ /tmp/requirements/
|
||
|
|
RUN pip install --no-index --find-links=/wheels -r /tmp/requirements/base.txt \
|
||
|
|
&& rm -rf /wheels /tmp/requirements
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
COPY --chown=app:app backend/ /app/
|
||
|
|
COPY --chown=app:app data/ /data/
|
||
|
|
|
||
|
|
# Static assets are only needed for the admin and the API docs, but baking them
|
||
|
|
# in keeps the container stateless.
|
||
|
|
RUN DJANGO_SECRET_KEY=build-only-not-a-real-secret-000000000000000000000000000000000000 \
|
||
|
|
DATABASE_URL=postgres://build:build@localhost:5432/build \
|
||
|
|
DJANGO_SETTINGS_MODULE=config.settings.dev \
|
||
|
|
python manage.py collectstatic --noinput --clear \
|
||
|
|
&& chmod +x /app/entrypoint.sh
|
||
|
|
|
||
|
|
USER app
|
||
|
|
EXPOSE 8000
|
||
|
|
|
||
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
|
||
|
|
CMD curl -fsS http://localhost:8000/api/v1/health/live/ || exit 1
|
||
|
|
|
||
|
|
ENTRYPOINT ["/app/entrypoint.sh"]
|
||
|
|
CMD ["gunicorn", "config.wsgi:application", "--config", "/app/gunicorn.conf.py"]
|