-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
63 lines (54 loc) · 3.18 KB
/
Copy pathDockerfile
File metadata and controls
63 lines (54 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
FROM python:3.14-slim
WORKDIR /app
# Copy requirements FIRST so the RUN command can use it
COPY requirements.txt .
# Install system dependencies, install python dependencies, then remove bloat.
# git is needed only because requirements.txt pins spotapi to a commit of
# TzurSoffer's fork (see the note there) - it used to be uninstalled and
# reinstalled from the fork's unpinned HEAD right here, which made every build
# non-reproducible and meant CI never tested what this image actually runs.
#
# `apt-get upgrade` is a deliberate exception to that reproducibility goal, not
# an oversight: the base image lags its own security updates, and two builds of
# the same tag differing by a patched libssl is the trade that is worth making.
# Everything the app's behaviour depends on - Python packages, spotapi's commit -
# is pinned, so this can only move OS-level packages.
#
# pip goes the same way as gcc and git: it is a build tool, and nothing at
# runtime invokes it (CMD is `python wsgi.py`). Leaving it behind put a
# fetch-and-install tool inside a container that deliberately runs as root -
# see the note below - and it was also the sole source of every "fixable"
# finding in the image scan. Both packages the scanner names, msgpack and
# setuptools, come from pip/_vendor/vendor.txt rather than requirements.txt;
# setuptools isn't even present as code, and msgpack only ever runs as
# pip._vendor.msgpack while pip itself is running. No requirements pin can
# reach either, because the vendored version is pip's to choose.
# `python -m ensurepip` restores pip from the bundled wheel if a container
# ever needs it for debugging.
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends gcc git \
&& pip install --no-cache-dir -r requirements.txt \
&& apt-get purge -y git gcc \
&& apt-get autoremove --purge -y \
&& rm -rf /var/lib/apt/lists/* \
&& python -m pip uninstall -y pip
# The application itself (everything .dockerignore lets through)
COPY . .
# Deliberately still runs as root, and that is a considered choice rather than an
# oversight. docker-compose bind-mounts ./Database/Data and ./autoImport, so a
# non-root uid cannot write to them until the operator chowns those directories -
# which silently breaks every existing deployment on upgrade. Weighed against a
# threat model where the container is the only process on a single-tenant host and
# already holds the database it would be attacked for, the upgrade break costs
# more than the isolation buys. Revisit if this ever runs somewhere multi-tenant.
# The port waitress serves on inside the container
EXPOSE 5000
# Baseline environment; compose overrides/extends these
ENV FLASK_APP=wsgi.py
ENV PYTHONUNBUFFERED=1
# Backed by GET /health (checks DB connectivity, not just process liveness) -
# uses stdlib urllib so no extra package (curl/wget) is needed in this slim
# image just for the check itself.
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:5000/health', timeout=3).getcode() == 200 else 1)"
# Serve via wsgi.py (waitress), not the Flask dev server
CMD ["python", "wsgi.py"]