chore: dockerfile (testing)

This commit is contained in:
2026-09-18 23:00:38 +02:00
parent 979be91417
commit 59b568ff41
6 changed files with 253 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
.git
.gitignore
.venv
venv
**/__pycache__
**/*.pyc
*.egg-info
.ruff_cache
.pytest_cache
ctagsfile
images
main.py
gifs_opus_magnum
polylan_submitter/node_modules
polylan_submitter/static
polylan_submitter/static_source/vite
polylan_submitter/media
polylan_submitter/db.sqlite3
polylan_submitter/.vscode
polylan_submitter/settingsLocal.py
deploy/supervisor/polylan-submitter.env
+75
View File
@@ -0,0 +1,75 @@
# Single OCI image bundling the Vue/Vite frontend, the Django/Channels web
# process (daphne) and the RQ worker, managed together by supervisord - see
# deploy/docker/README.md for why (single-container Proxmox deploy) and how
# to run it. Postgres, Redis and S3 stay external, configured the same way
# as the bare-metal deploy in deploy/ (see deploy/supervisor/*.env.dist for
# the full env var list) - this image carries no infra, just the app.
#
# Build (from repo root, requires BuildKit/buildx - RUN --mount=type=cache
# below is rejected by the legacy builder):
# docker buildx build --load -t polylan-submitter .
# ---- frontend build -------------------------------------------------------
FROM node:22-alpine AS frontend-builder
WORKDIR /app
COPY polylan_submitter/package.json polylan_submitter/pnpm-lock.yaml ./
RUN corepack enable && corepack prepare pnpm@9.0.0 --activate \
&& pnpm install --frozen-lockfile
COPY polylan_submitter/vite.config.ts polylan_submitter/tsconfig*.json ./
COPY polylan_submitter/src ./src
COPY polylan_submitter/public ./public
RUN pnpm build
# ---- python dependencies ---------------------------------------------------
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS python-builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
WORKDIR /app
COPY pyproject.toml uv.lock ./
# --no-install-project: this pyproject has no [build-system]/package to
# install, it only pins the app's dependencies - so just build the venv.
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-install-project
# ---- runtime ----------------------------------------------------------------
FROM python:3.13-slim AS runtime
# tesseract-ocr: shelled out to by pytesseract (submissions/tasks.py,
# opus_magnum/utils.py) for OCR'ing Opus Magnum submission GIFs.
# libgl1/libglib2.0-0: runtime shared libs opencv-python needs even though
# it ships as a wheel.
# libpq5: psycopg (plain, not psycopg[binary]/[c]) loads libpq via ctypes.
# supervisor/tini: process supervision - see deploy/docker/supervisord.conf.
RUN apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr \
libgl1 \
libglib2.0-0 \
libpq5 \
supervisor \
tini \
&& rm -rf /var/lib/apt/lists/*
RUN groupadd --system app && useradd --system --gid app --create-home app
WORKDIR /app
COPY --from=python-builder --chown=app:app /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
DJANGO_SETTINGS_MODULE=polylan_submitter.settings
COPY --chown=app:app polylan_submitter/ /app/polylan_submitter/
COPY --from=frontend-builder --chown=app:app /app/static_source/vite /app/polylan_submitter/static_source/vite
COPY --chown=app:app deploy/docker/entrypoint.sh /app/entrypoint.sh
COPY --chown=app:app deploy/docker/supervisord.conf /app/supervisord.conf
RUN rm -rf /app/polylan_submitter/node_modules /app/polylan_submitter/settingsLocal.py \
&& mkdir -p /app/polylan_submitter/static /app/polylan_submitter/media \
&& chown app:app /app/polylan_submitter/static /app/polylan_submitter/media \
&& chmod +x /app/entrypoint.sh
USER app
WORKDIR /app/polylan_submitter
EXPOSE 8000
ENTRYPOINT ["tini", "--", "/app/entrypoint.sh"]
CMD ["supervisord", "-c", "/app/supervisord.conf", "-n"]
+71
View File
@@ -0,0 +1,71 @@
# Container image
`Dockerfile` (repo root) builds a single OCI image with the Vite frontend
already built in, and a `.venv` with all Python deps. At runtime it uses
`supervisord` (`deploy/docker/supervisord.conf`) to run two processes in the
one container:
- `web` - one `daphne` process serving the Django/Channels app on `:8000`
- `worker` - two `rqworker` processes for background jobs (submission OCR,
AAndT sync, etc.)
Postgres, Redis and S3-compatible storage are **not** bundled - the image
expects them the same way the bare-metal deploy does (see
`deploy/supervisor/polylan-submitter.env.dist` for the full list of env
vars: `POSTGRES_*`, `REDIS_HOST`/`REDIS_PORT`, `AWS_*`, `DJANGO_SECRET_KEY`,
etc.). Point those at wherever those services already live (other
containers/LXCs, or existing infra).
## Build
Requires BuildKit/buildx (`RUN --mount=type=cache` in the Dockerfile is
rejected by the legacy builder):
```sh
docker buildx build --load -t polylan-submitter .
```
## Run
```sh
docker run -d \
--name polylan-submitter \
--env-file deploy/supervisor/polylan-submitter.env \
-p 8000:8000 \
polylan-submitter
```
Migrations run automatically on container start (set `MIGRATE_ON_START=false`
to skip). `collectstatic` does not run automatically - it pushes to the
shared S3 bucket, so run it once per deploy instead of racing multiple
replicas on every boot:
```sh
docker run --rm --env-file deploy/supervisor/polylan-submitter.env \
polylan-submitter python manage.py collectstatic --noinput
```
## Scaling
There's one `daphne` process per container (unlike the bare-metal deploy's
`NUM_WEB_WORKERS` behind HAProxy) - scale by running multiple container
replicas behind an external reverse proxy/load balancer instead.
## Deploying to Proxmox
Recent Proxmox VE versions can create an LXC container directly from an OCI
image (`pct create <vmid> <image-ref> ...` with an OCI-format image
reference, currently a tech-preview feature - check `pct create --help` and
the Proxmox VE docs for your installed version, since the exact flags have
been changing across releases). Practically that means:
1. Push the built image to a registry Proxmox can reach (a local registry,
GHCR, Docker Hub, etc.) - `docker tag`/`docker push`.
2. Create the container on the Proxmox host from that image reference,
passing the same env vars as above.
3. Map/forward port 8000 as needed, and put whatever TLS-terminating
reverse proxy you already run in front of it.
If OCI-to-LXC isn't available on your Proxmox version, the same image runs
fine in a plain VM (or an LXC) with any container runtime (Docker/Podman)
installed instead.
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Container entrypoint: runs migrations (unless disabled), then execs
# whatever CMD supplied (supervisord by default - see the Dockerfile).
#
# collectstatic is intentionally NOT run here: it pushes to the shared S3
# bucket (see STORAGES in settings.py), so running it on every container
# start would race across replicas for no benefit - run it once per deploy
# instead:
# docker run --rm --env-file <env file> polylan-submitter \
# python manage.py collectstatic --noinput
set -euo pipefail
if [ "${MIGRATE_ON_START:-true}" = "true" ]; then
echo "Running migrations..."
python manage.py migrate --noinput
fi
exec "$@"
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env sh
# One-off build+push to the Gitea container registry for testing.
# Usage: deploy/docker/push.sh [tag] (run from repo root, default tag: test)
set -eu
REGISTRY=git.legrems.mom
OWNER=polylan
IMAGE=polylan-submitter
TAG="${1:-test}"
cd "$(dirname "$0")/../.."
REF="$REGISTRY/$OWNER/$IMAGE:$TAG"
# buildx (BuildKit) is required: the Dockerfile uses RUN --mount=type=cache
# for the uv dep cache, which the legacy builder rejects.
docker buildx build --load -t "$REF" .
docker push "$REF"
echo "Pushed $REF"
+46
View File
@@ -0,0 +1,46 @@
; Runs inside the single app container as the non-root "app" user (see
; Dockerfile's USER app - no [supervisord] user= needed, we never start as
; root here). Unlike deploy/supervisor/polylan-submitter.conf (multiple
; daphne processes behind HAProxy on one bare-metal host), this container
; runs ONE daphne process on TCP: scale by running more container replicas
; behind an external reverse proxy/load balancer instead of more processes
; per container.
;
; Program logs go to stdout/stderr (logfile_maxbytes=0 disables supervisor's
; own log rotation for them) so `docker logs` / Proxmox's log viewer picks
; them up - no log files inside the container to manage.
[supervisord]
nodaemon=true
pidfile=/tmp/supervisord.pid
logfile=/dev/null
logfile_maxbytes=0
[program:web]
command=/app/.venv/bin/daphne -b 0.0.0.0 -p 8000 polylan_submitter.asgi:application
directory=/app/polylan_submitter
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopsignal=TERM
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; RQ workers - same command as deploy/supervisor/polylan-submitter.conf.
[program:worker]
command=/app/.venv/bin/python manage.py rqworker default --job-class django_tasks_rq.Job --with-scheduler
directory=/app/polylan_submitter
process_name=%(program_name)s-%(process_num)02d
numprocs=2
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopsignal=TERM
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0