|
1 | 1 | # syntax=docker/dockerfile:1
|
2 | 2 |
|
3 |
| -# Comments are provided throughout this file to help you get started. |
4 |
| -# If you need more help, visit the Dockerfile reference guide at |
5 |
| -# https://docs.docker.com/go/dockerfile-reference/ |
| 3 | +# Arguments that can be overridden |
| 4 | +ARG PYTHON_VERSION=3.12 |
| 5 | +ARG PYTHON_BASE_IMAGE=slim |
| 6 | +ARG APP_USER=appuser |
| 7 | +ARG APP_GROUP=appgroup |
| 8 | +ARG APP_UID=10001 |
| 9 | +ARG APP_GID=10001 |
| 10 | +ARG APP_ROOT=/app |
6 | 11 |
|
7 |
| -# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 |
| 12 | +# Build stage |
| 13 | +FROM python:${PYTHON_VERSION}-${PYTHON_BASE_IMAGE} AS builder |
8 | 14 |
|
9 |
| -ARG PYTHON_VERSION=3.12 |
10 |
| -FROM python:${PYTHON_VERSION}-slim AS base |
11 |
| - |
12 |
| -# Prevents Python from writing pyc files. |
13 |
| -ENV PYTHONDONTWRITEBYTECODE=1 |
14 |
| - |
15 |
| -# Keeps Python from buffering stdout and stderr to avoid situations where |
16 |
| -# the application crashes without emitting any logs due to buffering. |
17 |
| -ENV PYTHONUNBUFFERED=1 |
18 |
| - |
19 |
| -WORKDIR /app |
20 |
| - |
21 |
| -# Create a non-privileged user that the app will run under. |
22 |
| -# See https://docs.docker.com/go/dockerfile-user-best-practices/ |
23 |
| -ARG UID=10001 |
24 |
| -RUN adduser \ |
25 |
| - --disabled-password \ |
26 |
| - --gecos "" \ |
27 |
| - --home "/nonexistent" \ |
28 |
| - --shell "/sbin/nologin" \ |
29 |
| - --no-create-home \ |
30 |
| - --uid "${UID}" \ |
31 |
| - appuser |
32 |
| - |
33 |
| -# Download dependencies as a separate step to take advantage of Docker's caching. |
34 |
| -# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. |
35 |
| -# Leverage a bind mount to requirements.txt to avoid having to copy them into |
36 |
| -# into this layer. |
| 15 | +# Build arguments |
| 16 | +ARG APP_ROOT |
| 17 | +ENV PYTHONDONTWRITEBYTECODE=1 \ |
| 18 | + PYTHONUNBUFFERED=1 \ |
| 19 | + PIP_NO_CACHE_DIR=1 \ |
| 20 | + PIP_DISABLE_PIP_VERSION_CHECK=1 \ |
| 21 | + PIP_DEFAULT_TIMEOUT=100 |
| 22 | + |
| 23 | +WORKDIR ${APP_ROOT} |
| 24 | + |
| 25 | +# Install build dependencies |
| 26 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 27 | + build-essential \ |
| 28 | + curl \ |
| 29 | + git \ |
| 30 | + && rm -rf /var/lib/apt/lists/* |
| 31 | + |
| 32 | +# Install Python dependencies |
| 33 | +COPY requirements*.txt ./ |
37 | 34 | RUN --mount=type=cache,target=/root/.cache/pip \
|
38 |
| - --mount=type=bind,source=requirements.txt,target=requirements.txt \ |
39 |
| - python -m pip install -r requirements.txt |
| 35 | + pip wheel --no-deps --wheel-dir wheels -r requirements.txt && \ |
| 36 | + pip install -r requirements.txt |
40 | 37 |
|
41 |
| -# Switch to the non-privileged user to run the application. |
42 |
| -USER appuser |
| 38 | +# Copy only necessary files for collecting static |
| 39 | +COPY manage.py . |
| 40 | +COPY .env .env |
| 41 | +COPY test_project/ test_project/ |
| 42 | +COPY apidemo/ apidemo/ |
| 43 | +COPY static/ static/ |
43 | 44 |
|
44 |
| -# Copy the source code into the container. |
45 |
| -COPY . . |
| 45 | +# Collect static files |
| 46 | +RUN python manage.py collectstatic --noinput |
46 | 47 |
|
47 |
| -# Expose the port that the application listens on. |
48 |
| -EXPOSE 8000 |
| 48 | +# Final stage |
| 49 | +FROM python:${PYTHON_VERSION}-${PYTHON_BASE_IMAGE} AS runtime |
| 50 | + |
| 51 | +# Runtime arguments and environment variables |
| 52 | +ARG APP_USER |
| 53 | +ARG APP_GROUP |
| 54 | +ARG APP_UID |
| 55 | +ARG APP_GID |
| 56 | +ARG APP_ROOT |
| 57 | + |
| 58 | +ENV PYTHONDONTWRITEBYTECODE=1 \ |
| 59 | + PYTHONUNBUFFERED=1 \ |
| 60 | + PIP_NO_CACHE_DIR=1 \ |
| 61 | + PIP_DISABLE_PIP_VERSION_CHECK=1 \ |
| 62 | + APP_ROOT=${APP_ROOT} |
| 63 | + |
| 64 | +WORKDIR ${APP_ROOT} |
| 65 | + |
| 66 | +# Install runtime dependencies |
| 67 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 68 | + curl \ |
| 69 | + && rm -rf /var/lib/apt/lists/* |
49 | 70 |
|
| 71 | +# Create app user and group |
| 72 | +RUN groupadd --gid ${APP_GID} ${APP_GROUP} && \ |
| 73 | + useradd --uid ${APP_UID} --gid ${APP_GID} --no-create-home --home-dir /nonexistent \ |
| 74 | + --shell /sbin/nologin --system ${APP_USER} |
| 75 | + |
| 76 | +# Create necessary directories with proper permissions |
| 77 | +RUN mkdir -p ${APP_ROOT}/static ${APP_ROOT}/media /var/run/django && \ |
| 78 | + chown -R ${APP_USER}:${APP_GROUP} ${APP_ROOT} /var/run/django |
| 79 | + |
| 80 | +# Copy wheels and install dependencies |
| 81 | +COPY --from=builder ${APP_ROOT}/wheels /wheels |
| 82 | +RUN --mount=type=cache,target=/root/.cache/pip \ |
| 83 | + pip install --no-cache-dir /wheels/* |
| 84 | + |
| 85 | +# Copy application code and static files |
| 86 | +COPY --chown=${APP_USER}:${APP_GROUP} . . |
| 87 | +COPY --from=builder --chown=${APP_USER}:${APP_GROUP} ${APP_ROOT}/static ${APP_ROOT}/static |
| 88 | + |
| 89 | +# Copy and set up entrypoint |
| 90 | +COPY --chown=${APP_USER}:${APP_GROUP} docker-entrypoint.sh /usr/local/bin/ |
| 91 | +RUN chmod +x /usr/local/bin/docker-entrypoint.sh |
| 92 | + |
| 93 | +# Set up health check |
| 94 | +HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=30s \ |
| 95 | + CMD curl -f http://localhost:8000/health/ || exit 1 |
| 96 | + |
| 97 | +# Set default environment variables for Gunicorn |
| 98 | +ENV GUNICORN_WORKERS=4 \ |
| 99 | + GUNICORN_THREADS=2 \ |
| 100 | + GUNICORN_WORKER_CLASS=gthread \ |
| 101 | + GUNICORN_MAX_REQUESTS=1000 \ |
| 102 | + GUNICORN_MAX_REQUESTS_JITTER=50 |
| 103 | + |
| 104 | +# Switch to non-privileged user |
| 105 | +USER ${APP_USER}:${APP_GROUP} |
| 106 | + |
| 107 | +EXPOSE 8000 |
50 | 108 |
|
51 |
| -# Run the application. |
52 |
| -# Test application |
53 |
| -# RUN python test_project/manage.py makemigrations && python test_project/manage.py migrate |
54 |
| -CMD ["gunicorn", "--workers=2", "test_project.wsgi", "--bind", "0.0.0.0:8000"] |
| 109 | +ENTRYPOINT ["docker-entrypoint.sh"] |
| 110 | +CMD gunicorn \ |
| 111 | + --workers=${GUNICORN_WORKERS} \ |
| 112 | + --threads=${GUNICORN_THREADS} \ |
| 113 | + --worker-class=${GUNICORN_WORKER_CLASS} \ |
| 114 | + --bind=0.0.0.0:8000 \ |
| 115 | + --max-requests=${GUNICORN_MAX_REQUESTS} \ |
| 116 | + --max-requests-jitter=${GUNICORN_MAX_REQUESTS_JITTER} \ |
| 117 | + test_project.wsgi:application |
0 commit comments