From 71a364a1fd5918bf8aab6c7919ce14d8d0608b57 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Fri, 24 Apr 2026 15:55:16 +0200 Subject: [PATCH] deploy: Dockerfile + docker-compose Traefik per VPS pm.tielogic.xyz Dockerfile (multi-arch, python 3.13-slim): - uv copiato da ghcr.io/astral-sh/uv per install deps - System deps: libgl1 libglib2.0-0 (cv2) + libgomp1 (numba) - uv sync --frozen --no-dev da uv.lock - ENV: IMAGES_DIR=/data/images, HOST=0.0.0.0, PORT=8080 - HEALTHCHECK su GET /images ogni 30s docker-compose.yml: - Service pm2d con image ${REGISTRY}/pm2d:${TAG} - Volume ./images:/data/images (persistenza upload/UI) - Network esterna 'traefik' (adattare se diverso) - Labels Traefik: - Router HTTPS Host(pm.tielogic.xyz) entrypoint websecure TLS letsencrypt - Middleware bodysize 50MB (upload multipart) - Redirect HTTP->HTTPS automatico main.py: HOST/PORT da env (default 127.0.0.1:8080 per dev locale). README: sezione Deploy con build/push/run su VPS. .dockerignore: esclude .venv, Test/, benchmarks/, md files. Build + smoke test container: OK su port 18080. Co-Authored-By: Claude Opus 4.7 (1M context) --- .dockerignore | 22 +++++++++++++++++++++ Dockerfile | 38 +++++++++++++++++++++++++++++++++++ README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 46 +++++++++++++++++++++++++++++++++++++++++++ main.py | 10 +++++++--- 5 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e1147e4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +.venv +.git +.gitignore +.github +__pycache__ +*.pyc +*.pyo +*.pyd +.DS_Store +.idea +.vscode +*.log +# Test images non necessarie nel container (caricate via volume/UI) +Test +benchmarks +ROADMAP.md +shape_model_2d_technical_doc.md +*.md +!README.md +Dockerfile +docker-compose*.yml +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e7ab468 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM python:3.13-slim AS base + +# uv package manager (copia binario ufficiale) +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +# System deps per opencv (libgl/glib), numba (libgomp) +RUN apt-get update && apt-get install -y --no-install-recommends \ + libgl1 \ + libglib2.0-0 \ + libgomp1 \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Install deps da lockfile (layer cachato finché pyproject/uv.lock non cambiano) +COPY pyproject.toml uv.lock ./ +COPY .python-version* ./ +RUN uv sync --frozen --no-dev + +# Copia sorgenti applicazione +COPY pm2d ./pm2d +COPY main.py ./ + +# Defaults (override via docker-compose env) +ENV IMAGES_DIR=/data/images \ + HOST=0.0.0.0 \ + PORT=8080 \ + PYTHONUNBUFFERED=1 + +# Cartella dati (montata come volume in compose) +RUN mkdir -p /data/images + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/images').read()" || exit 1 + +CMD ["uv", "run", "python", "main.py"] diff --git a/README.md b/README.md index 207c08b..2cd4ed8 100644 --- a/README.md +++ b/README.md @@ -140,3 +140,52 @@ Implementato con **shift+add vettorizzato NumPy** (O(N_features · H · W) invec - ICP locale per raffinamento pose - Vincoli di orientamento: clustering delle pose per eliminare duplicati cross-variante - Numba JIT per il ciclo shift+add (eventuale 3-5× su scene grandi) + +## Deploy VPS con Docker + Traefik + +Assume che sulla VPS siano già attivi: +- **Traefik** come reverse proxy su network Docker esterna `traefik` +- Entrypoints `web` (:80) e `websecure` (:443) +- Cert resolver `letsencrypt` configurato + +### Build e push al registry + +```bash +# Build locale +docker build -t vps-ip:5000/pm2d:latest . +docker push vps-ip:5000/pm2d:latest +``` + +### Sulla VPS + +```bash +# Cartella deploy (immagini persistenti qui) +mkdir -p /opt/docker/pm2d/images +cd /opt/docker/pm2d + +# Copia docker-compose.yml +# Imposta REGISTRY / TAG se necessario via .env +echo "REGISTRY=vps-ip:5000" > .env +echo "TAG=latest" >> .env + +docker compose pull +docker compose up -d +``` + +Servizio raggiungibile: **https://pm.tielogic.xyz** + +### Note operative + +- **Volume `./images`**: persistenza delle immagini caricate tramite UI + (`IMAGES_DIR=/data/images` nel container). Sopravvive a restart. +- **Upload max 50MB**: middleware Traefik `pm2d-bodysize`. Adattare se serve. +- **Cache matcher in-memory**: si svuota a restart container (no problema, + viene ri-popolata al primo match). +- **Healthcheck**: HTTP `GET /images` ogni 30s. +- Se nome network Traefik diverso da `traefik`, modifica + `docker-compose.yml` sezione `networks`. + +### Adattamenti config Traefik non-standard + +Se la VPS ha convenzioni diverse (es. cert resolver chiamato `le`, +entrypoint `https`), modifica i labels nel `docker-compose.yml`. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2f0bb67 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +# docker-compose per deploy VPS con Traefik. +# Assume che Traefik sia già attivo sulla VPS con: +# - network esterna "traefik" (adatta nome se diverso) +# - entrypoint "websecure" su :443 +# - certresolver "letsencrypt" configurato +# +# Adattare eventualmente: nome network, entrypoint, certresolver. + +services: + pm2d: + image: ${REGISTRY:-localhost:5000}/pm2d:${TAG:-latest} + container_name: pm2d + restart: unless-stopped + environment: + IMAGES_DIR: /data/images + HOST: 0.0.0.0 + PORT: 8080 + volumes: + # Persistenza immagini tra restart (upload/selezione) + - ./images:/data/images + networks: + - traefik + labels: + - "traefik.enable=true" + + # Router HTTPS principale + - "traefik.http.routers.pm2d.rule=Host(`pm.tielogic.xyz`)" + - "traefik.http.routers.pm2d.entrypoints=websecure" + - "traefik.http.routers.pm2d.tls=true" + - "traefik.http.routers.pm2d.tls.certresolver=letsencrypt" + - "traefik.http.services.pm2d.loadbalancer.server.port=8080" + + # Middleware: upload fino a 50MB (default Traefik bufferizza a 4MB) + - "traefik.http.middlewares.pm2d-bodysize.buffering.maxRequestBodyBytes=52428800" + - "traefik.http.routers.pm2d.middlewares=pm2d-bodysize" + + # Redirect HTTP → HTTPS + - "traefik.http.routers.pm2d-http.rule=Host(`pm.tielogic.xyz`)" + - "traefik.http.routers.pm2d-http.entrypoints=web" + - "traefik.http.routers.pm2d-http.middlewares=pm2d-redirect-https" + - "traefik.http.middlewares.pm2d-redirect-https.redirectscheme.scheme=https" + - "traefik.http.middlewares.pm2d-redirect-https.redirectscheme.permanent=true" + +networks: + traefik: + external: true diff --git a/main.py b/main.py index 7a3f92c..72660d2 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,14 @@ """Entry-point PM2D — webapp HTML. -Esegui: uv run python main.py -Apri: http://127.0.0.1:8080/ +Esegui locale: uv run python main.py (default 127.0.0.1:8080) +Container: HOST=0.0.0.0 PORT=8080 python main.py """ +import os + from pm2d.web.server import serve if __name__ == "__main__": - serve(host="127.0.0.1", port=8080) + host = os.environ.get("HOST", "127.0.0.1") + port = int(os.environ.get("PORT", "8080")) + serve(host=host, port=port)