-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathDockerfile
More file actions
46 lines (40 loc) · 2.2 KB
/
Copy pathDockerfile
File metadata and controls
46 lines (40 loc) · 2.2 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
# syntax=docker/dockerfile:1
# ---------------------------------------------------------------------------
# dash-flows documentation site (run.py) — production image for Render.
#
# The React component bundle (dash_flows/dash_flows.min.js) is committed, so no
# Node/webpack build is needed: this is a pure-Python image that serves the
# pre-built Dash app with gunicorn. node_modules/ is excluded via .dockerignore.
# ---------------------------------------------------------------------------
FROM python:3.12-slim
# PYTHONUNBUFFERED -> stream logs straight to stdout (Render shows them live)
# PYTHONDONTWRITEBYTECODE -> no .pyc clutter in the image
# DASH_BACKEND=flask -> WSGI backend served by gunicorn (not fastapi/quart/ASGI)
# PORT -> local default; Render overrides this at runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
DASH_BACKEND=flask \
PORT=8560
WORKDIR /app
# Install Python deps first so this layer is cached across app-code changes.
#
# markdown2dash installs with --no-deps ON PURPOSE: 0.1.2 pins
# gunicorn>=21.2.0,<22.0.0, a range vulnerable to CVE-2024-6827 and
# CVE-2024-1135, and unresolvable against the gunicorn>=23 floor in
# requirements-docs.txt. Its real dependencies (mistune, docutils, jsonpath,
# pydantic, frontmatter) are listed there explicitly. CI asserts the gunicorn
# version inside this image so the pin cannot quietly come back.
COPY requirements-docs.txt ./
RUN pip install --no-cache-dir -r requirements-docs.txt \
&& pip install --no-cache-dir --no-deps markdown2dash==0.1.2
# Copy the application. run.py resolves templates/, dash_flows/, docs/,
# examples/, components/, lib/, pages/ relative to the working directory, so it
# must run from /app (the repo root) — which it does under this WORKDIR.
COPY . .
# Documentation only; the process actually binds to $PORT (below).
EXPOSE 8560
# run:server is the Flask WSGI callable (run.py: `server = app.server`).
# Shell form so ${PORT} / ${WEB_CONCURRENCY} expand when the container starts.
CMD gunicorn run:server --bind "0.0.0.0:${PORT}" --workers "${WEB_CONCURRENCY:-2}" --threads 4 --timeout 120 --access-logfile - --error-logfile -