|
1 |
| -# Use an official Python runtime based on Debian 10 "buster" as a parent image. |
2 |
| -FROM python:3.10-slim-buster |
| 1 | +ARG PYTHON_IMG_TAG=3.11 |
3 | 2 |
|
4 |
| -# Add user that will be used in the container. |
5 |
| -RUN useradd wagtail |
6 | 3 |
|
7 |
| -# Port used by this container to serve HTTP. |
8 |
| -EXPOSE 8000 |
| 4 | +# Define the base stage |
| 5 | +FROM docker.io/python:${PYTHON_IMG_TAG}-slim-bookworm as base |
| 6 | +ARG APP_VERSION |
| 7 | +ARG COMMIT_REF |
| 8 | +ARG PYTHON_IMG_TAG |
| 9 | +LABEL org.hotosm.fmtm.app-name="backend" \ |
| 10 | + org.hotosm.fmtm.app-version="${APP_VERSION}" \ |
| 11 | + org.hotosm.fmtm.git-commit-ref="${COMMIT_REF:-none}" \ |
| 12 | + org.hotosm.fmtm.python-img-tag="${PYTHON_IMG_TAG}" \ |
| 13 | + org.hotosm.fmtm.maintainer= "[email protected]" \ |
| 14 | + org.hotosm.fmtm.api-port="8000" |
| 15 | +RUN set -ex \ |
| 16 | + && apt-get update \ |
| 17 | + && DEBIAN_FRONTEND=noninteractive apt-get install \ |
| 18 | + -y --no-install-recommends "locales" "ca-certificates" \ |
| 19 | + && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ |
| 20 | + && rm -rf /var/lib/apt/lists/* \ |
| 21 | + && update-ca-certificates |
| 22 | +# Set locale |
| 23 | +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen |
| 24 | +ENV LANG en_US.UTF-8 |
| 25 | +ENV LANGUAGE en_US:en |
| 26 | +ENV LC_ALL en_US.UTF-8 |
9 | 27 |
|
10 |
| -# Set environment variables. |
11 |
| -# 1. Force Python stdout and stderr streams to be unbuffered. |
12 |
| -# 2. Set PORT variable that is used by Gunicorn. This should match "EXPOSE" |
13 |
| -# command. |
14 |
| -ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1\ |
15 |
| - PORT=8000 |
16 | 28 |
|
17 |
| -# Install system packages required by Wagtail and Django. |
18 |
| -RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \ |
19 |
| - build-essential \ |
20 |
| - libpq-dev \ |
21 |
| - libmariadbclient-dev \ |
22 |
| - libjpeg62-turbo-dev \ |
23 |
| - zlib1g-dev \ |
24 |
| - libwebp-dev \ |
25 |
| - && rm -rf /var/lib/apt/lists/* |
| 29 | +# Extract dependencies using poetry (to requirements.txt) |
| 30 | +FROM base as extract-deps |
| 31 | +WORKDIR /opt/python |
| 32 | +COPY pyproject.toml poetry.lock* /opt/python/ |
| 33 | +RUN pip install --no-cache-dir --upgrade pip \ |
| 34 | + && pip install --no-cache-dir \ |
| 35 | + poetry==1.7.1 poetry-plugin-export==1.6.0 \ |
| 36 | + && poetry config warnings.export false |
| 37 | +RUN poetry export --without dev --output requirements.txt |
| 38 | +RUN poetry export --only dev --output requirements-dev.txt |
26 | 39 |
|
27 |
| -# Install pipenv |
28 |
| -RUN pip install pipenv |
29 | 40 |
|
30 |
| -# Install the application server. |
31 |
| -RUN pip install "gunicorn==20.0.4" |
| 41 | +# Define build stage (install deps) |
| 42 | +FROM base as build |
| 43 | +RUN set -ex \ |
| 44 | + && apt-get update \ |
| 45 | + && DEBIAN_FRONTEND=noninteractive apt-get install \ |
| 46 | + -y --no-install-recommends \ |
| 47 | + "build-essential" \ |
| 48 | + "gcc" \ |
| 49 | + "libpq-dev" \ |
| 50 | + "libmariadb-dev" \ |
| 51 | + "libjpeg62-turbo-dev" \ |
| 52 | + "zlib1g-dev" \ |
| 53 | + "libwebp-dev" \ |
| 54 | + && rm -rf /var/lib/apt/lists/* |
| 55 | +COPY --from=extract-deps \ |
| 56 | + /opt/python/requirements.txt /opt/python/ |
| 57 | +RUN pip install --user --no-warn-script-location \ |
| 58 | + --no-cache-dir -r /opt/python/requirements.txt |
32 | 59 |
|
33 |
| -# Copy Pipfile and Pipfile.lock |
34 |
| -COPY Pipfile Pipfile.lock ./ |
35 |
| - |
36 |
| -# Install the project requirements. |
37 |
| -RUN pipenv install --system --deploy |
38 | 60 |
|
| 61 | +# Define run stage |
| 62 | +FROM base as runtime |
| 63 | +ARG PYTHON_IMG_TAG |
| 64 | +ENV PORT=8000 \ |
| 65 | + PYTHONDONTWRITEBYTECODE=1 \ |
| 66 | + PYTHONUNBUFFERED=1 \ |
| 67 | + PYTHONFAULTHANDLER=1 \ |
| 68 | + PATH="/home/wagtail/.local/bin:$PATH" \ |
| 69 | + PYTHONPATH="/app" \ |
| 70 | + PYTHON_LIB="/home/wagtail/.local/lib/python$PYTHON_IMG_TAG/site-packages" \ |
| 71 | + SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ |
| 72 | + REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \ |
| 73 | + CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt |
| 74 | +# Install non-dev versions of packages (smaller) |
| 75 | +RUN set -ex \ |
| 76 | + && apt-get update \ |
| 77 | + && DEBIAN_FRONTEND=noninteractive apt-get install \ |
| 78 | + -y --no-install-recommends \ |
| 79 | + "postgresql-client" \ |
| 80 | + "libmariadb3" \ |
| 81 | + "libjpeg62-turbo" \ |
| 82 | + "zlib1g" \ |
| 83 | + "libwebp-dev" \ |
| 84 | + && rm -rf /var/lib/apt/lists/* |
| 85 | +# Copy the entrypoint script into the Docker image |
| 86 | +COPY --chown=wagtail:wagtail entrypoint.dev.sh /app/entrypoint.dev.sh |
| 87 | +# Copy pip dependencies from build stage |
| 88 | +COPY --from=build \ |
| 89 | + /root/.local \ |
| 90 | + /home/wagtail/.local |
39 | 91 | # Use /app folder as a directory where the source code is stored.
|
40 | 92 | WORKDIR /app
|
| 93 | +# Copy project |
| 94 | +COPY . /app/ |
| 95 | +# Add non-root user, permissions |
| 96 | +RUN useradd -u 1001 -m -c "hotosm account" -d /home/wagtail -s /bin/false wagtail \ |
| 97 | + && chown -R wagtail:wagtail /app /home/wagtail \ |
| 98 | + && chmod +x /app/entrypoint.dev.sh |
| 99 | +# Change to non-root user |
| 100 | +USER wagtail |
41 | 101 |
|
42 |
| -# Set this directory to be owned by the "wagtail" user. This Wagtail project |
43 |
| -# uses SQLite, the folder needs to be owned by the user that |
44 |
| -# will be writing to the database file. |
45 |
| -RUN chown wagtail:wagtail /app |
46 | 102 |
|
47 |
| -# Copy the source code of the project into the container. |
48 |
| -COPY --chown=wagtail:wagtail . . |
| 103 | +# Define test (ci) stage |
| 104 | +FROM runtime as test |
| 105 | +USER root |
| 106 | +ARG PYTHON_IMG_TAG |
| 107 | +COPY --from=extract-deps \ |
| 108 | + /opt/python/requirements-dev.txt /opt/python/ |
| 109 | +# Copy packages from user to root dirs (run ci as root) |
| 110 | +# && install dev dependencies (pytest) |
| 111 | +RUN mv /home/wagtail/.local/bin/* /usr/local/bin/ \ |
| 112 | + && mv /home/wagtail/.local/lib/python${PYTHON_IMG_TAG}/site-packages/* \ |
| 113 | + /usr/local/lib/python${PYTHON_IMG_TAG}/site-packages/ \ |
| 114 | + && pip install --upgrade --no-warn-script-location \ |
| 115 | + --no-cache-dir -r \ |
| 116 | + /opt/python/requirements-dev.txt \ |
| 117 | + && rm -r /opt/python \ |
| 118 | + # Pre-compile packages to .pyc (init speed gains) |
| 119 | + && python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)" |
| 120 | +CMD [ "pytest" ] |
49 | 121 |
|
50 |
| -# Use user "wagtail" to run the build commands below and the server itself. |
51 |
| -USER wagtail |
52 | 122 |
|
53 |
| -# Collect static files. |
54 |
| -RUN python manage.py collectstatic --noinput --clear |
| 123 | +# Define debug (development) stage |
| 124 | +FROM runtime as debug |
| 125 | +# Add Healthcheck |
| 126 | +HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \ |
| 127 | + CMD curl --fail http://localhost:8000 || exit 1 |
| 128 | +# Use the entrypoint script as the Docker entrypoint |
| 129 | +ENTRYPOINT ["/app/entrypoint.dev.sh"] |
| 130 | +CMD ["./wait-for-it.sh", "db:5432", "--", \ |
| 131 | + "python", "manage.py", "runserver", "0.0.0.0:8000"] |
| 132 | + |
55 | 133 |
|
| 134 | +# Define prod stage |
| 135 | +FROM runtime as prod |
| 136 | +# Add Healthcheck |
| 137 | +HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \ |
| 138 | + CMD curl --fail http://localhost:8000 || exit 1 |
| 139 | +# Pre-compile packages to .pyc (init speed gains) |
| 140 | +RUN python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)" \ |
| 141 | + # Collect static files |
| 142 | + && python manage.py collectstatic --noinput --clear |
56 | 143 | # Runtime command that executes when "docker run" is called, it does the
|
57 | 144 | # following:
|
58 | 145 | # 1. Migrate the database.
|
|
0 commit comments