From 3b3e2893e54efff273c2ebe21d2985cc0786f400 Mon Sep 17 00:00:00 2001 From: Gerd Date: Sun, 13 Sep 2026 18:40:55 +0200 Subject: [PATCH 1/5] feat(deploy): Multi-Stage-Docker-Build fuer automatisches Deployment - App_Server/Dockerfile: Build-Context auf Repo-Root, COPY-Pfade angepasst, ENTRYPOINT aktiviert, dev-Stage mit inittypescriptdev.sh - NGINX_Server/Dockerfile: Multi-Stage mit frontend-Stage (Node.js baut TypeScript), statische Dateien und Config-Templates ins Image kopiert - docker-compose.base.yaml: Context auf '.', python-venv Volume entfernt, NGINX_STATIC_DIR/CONFIG_TEMPLATE_DIR bind-mounts in Dev verschoben - docker-compose.dev.yaml: python-venv-dev Volume, APP_DEBUG=true, nginx-proxy mit bind-mounts fuer statische Dateien und Templates - docker-compose.prod.yaml: Code-bind-mount entfernt, nginx-proxy extends - approot.py: absolute Pfade, Debug-Modus via APP_DEBUG Umgebungsvariable - init.sh: vereinfacht (kein cd/pip, absolute Pfade) - inireader.mjs: absoluter Pfad fuer config.ini - package.json: scripts.build (tsc + rollup) ergaenzt - .dockerignore, deploy-kbcrm-test.sh, deploy-kbcrm-prod.sh erstellt - docker-compose.save.yaml geloescht - env-sample: BUILD_ENVIRONMENT nach oben, Dev-only Variablen markiert Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- .dockerignore | 8 +++++++ App_Server/Dockerfile | 33 +++++++++++++++------------ App_Server/root/approot.py | 6 ++--- App_Server/scripts/init.sh | 21 +++++------------ Client/inireader.mjs | 2 +- Client/package.json | 3 +++ NGINX_Server/Dockerfile | 30 ++++++++++++++++++++++-- docker-compose.base.yaml | 30 ++++++++++++------------ docker-compose.dev.yaml | 19 +++++++++++++--- docker-compose.prod.yaml | 8 +++++-- docker-compose.save.yaml | 27 ---------------------- env-sample | 24 ++++++++++++-------- scripts/deploy-kbcrm-prod.sh | 44 ++++++++++++++++++++++++++++++++++++ scripts/deploy-kbcrm-test.sh | 36 +++++++++++++++++++++++++++++ 14 files changed, 200 insertions(+), 91 deletions(-) create mode 100644 .dockerignore delete mode 100644 docker-compose.save.yaml create mode 100644 scripts/deploy-kbcrm-prod.sh create mode 100644 scripts/deploy-kbcrm-test.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..296bb1a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git/ +Client/node_modules/ +Client/tsout/ +App_Server/data/ +App_Server/env/ +oidc-test/ +*.pyc +__pycache__/ diff --git a/App_Server/Dockerfile b/App_Server/Dockerfile index e1d0023..bbd86c8 100644 --- a/App_Server/Dockerfile +++ b/App_Server/Dockerfile @@ -1,6 +1,10 @@ ARG PYTHON_VERSION ARG NODE_VERSION ARG NPM_VERSION + +# ── Stage: prod ────────────────────────────────────────────────── +# Build-Context ist das Repo-Root (.), daher muessen alle COPY-Pfade +# mit App_Server/ praefigiert werden. FROM python:${PYTHON_VERSION} AS prod RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN apt-get clean && apt-get update && apt-get install -y locales @@ -12,15 +16,19 @@ RUN locale-gen RUN mkdir -p /usr/opt/pythonapp/App_Server/root /usr/opt/pythonapp/env /usr/var/pythonappdata WORKDIR /usr/opt/pythonapp RUN python -m venv --upgrade-deps /usr/opt/pythonapp/env -COPY root/requirements.txt /usr/opt/pythonapp/App_Server/root/requirements.txt +COPY App_Server/root/requirements.txt /usr/opt/pythonapp/App_Server/root/requirements.txt RUN /usr/opt/pythonapp/env/bin/pip install --no-cache-dir --upgrade pip -r /usr/opt/pythonapp/App_Server/root/requirements.txt -# COPY scripts/init.sh /init.sh -# RUN chmod 552 /init.sh -# # ENTRYPOINT [ "/init.sh" ] -# ENTRYPOINT [ "sh", "/init.sh" ] -# CMD [] +# Python-Anwendungscode ins Image kopieren (inkl. templates/index.html) +COPY App_Server/root/ /usr/opt/pythonapp/App_Server/root/ +# Prod-Start-Script +COPY App_Server/scripts/init.sh /usr/opt/pythonapp/App_Server/scripts/init.sh +RUN chmod +x /usr/opt/pythonapp/App_Server/scripts/init.sh + +ENTRYPOINT ["sh", "/usr/opt/pythonapp/App_Server/scripts/init.sh"] + +# ── Stage: dev ─────────────────────────────────────────────────── FROM prod AS dev ARG NODE_VERSION ARG NPM_VERSION @@ -41,19 +49,14 @@ RUN apt-get update && \ && rm -rf /var/lib/apt/lists/* # --- 3. Node.js + npm installieren --- - RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \ apt-get install -y nodejs && \ npm install -g npm@${NPM_VERSION} -# --- 8. Arbeitsverzeichnis für TypeScript --- -# In dem Entwicklungsverzeichnis (PYTHON_DEV_DIR) existiert dies schon als Unterverzeichnis "Client", daher wird es hier nicht erstellt +# --- 4. Dev-Start-Script kopieren (fuer Konsistenz, wird sonst per bind-mount bereitgestellt) --- +COPY App_Server/scripts/inittypescriptdev.sh /usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh +RUN chmod +x /usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh -# --- 9. TypeScript + Rollup installieren --- -# Das Script inittypescriptdev.sh wird in der Entwicklungsumgebung ausgeführt, um die TypeScript-Umgebung zu initialisieren. -# Daher ist es nicht notwendig, es hier zu kopieren und auszuführen. -# COPY ./scripts/inittypescriptdev.sh /usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh -# RUN chmod +x /usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh ENTRYPOINT ["sh", "/usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh"] -CMD ["/usr/opt/pythonapp/env/bin/python", "root/runserver.py", "-D"] +CMD ["/usr/opt/pythonapp/env/bin/python", "/usr/opt/pythonapp/App_Server/root/runserver.py", "-D"] diff --git a/App_Server/root/approot.py b/App_Server/root/approot.py index ea2ede9..da9961e 100644 --- a/App_Server/root/approot.py +++ b/App_Server/root/approot.py @@ -4,7 +4,7 @@ from settings import Configuration from db import dbauth from db import dbapp from auth.model.checkdb import checkupdates as checkupdatesauth -from os import path +import os login_manager = LoginManager() @@ -12,7 +12,7 @@ login_manager = LoginManager() def init_app(): app = Flask(__name__) - config: Configuration = Configuration("root/config.ini") + config: Configuration = Configuration("/usr/opt/pythonapp/App_Server/root/config.ini") _projektname = config.app_projectname app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////usr/var/pythonappdb/{}.db".format(_projektname) @@ -20,7 +20,7 @@ def init_app(): "auth": "sqlite:////usr/var/pythonappdb/auth.db", "appdb": "sqlite:////usr/var/pythonappdb/{}.db".format(_projektname) } - app.config["DEBUG"] = path.exists("root/debug") + app.config["DEBUG"] = os.environ.get("APP_DEBUG", "false").lower() in ("true", "1", "yes") app.config["PROJEKTNAME"] = config.app_projectname app.config["APPID"] = config.app_appid app.config["APPVERSION"] = config.clientapp_version diff --git a/App_Server/scripts/init.sh b/App_Server/scripts/init.sh index c7583ad..c869720 100644 --- a/App_Server/scripts/init.sh +++ b/App_Server/scripts/init.sh @@ -1,16 +1,7 @@ -# Python virtual Environment anlegen -cd /usr/opt/pythonapp -# Wenn es die Datei pyvenv.cfg im Unterverzeichnis env nocht nicht gibt, muss das -# virtuelle Environment angelegt werden -# if [ ! -s env/pyvenv.cfg ] -# then -# echo "Creating Python Virtual environment..." -# python -m venv --upgrade-deps /usr/opt/pythonapp/env -# echo "Python Virtual environment created." -# fi -echo "Installing Python requirements..." -env/bin/pip install -r root/requirements.txt -echo "Python requirements installed." - +#!/bin/bash +# Startet den Python-Server im Prod-Modus. +# pip install ist nicht mehr noetig — Abhaengigkeiten sind im Image. +# Kein cd noetig — alle Pfade sind absolut. +# Python-Imports funktionieren ueber sys.path[0] (Verzeichnis des Scripts). echo "Starting Python-Server..." -env/bin/python root/runserver.py \ No newline at end of file +exec /usr/opt/pythonapp/env/bin/python /usr/opt/pythonapp/App_Server/root/runserver.py \ No newline at end of file diff --git a/Client/inireader.mjs b/Client/inireader.mjs index c8ef556..93bc92e 100644 --- a/Client/inireader.mjs +++ b/Client/inireader.mjs @@ -38,5 +38,5 @@ export function getIniValue(value){ return result["APP"][value] } -var inifile = readFileSync("../App_Server/root/config.ini").toString() +var inifile = readFileSync("/usr/opt/pythonapp/App_Server/root/config.ini").toString() result = parseINIString(inifile) diff --git a/Client/package.json b/Client/package.json index ab198a1..eb07143 100644 --- a/Client/package.json +++ b/Client/package.json @@ -1,4 +1,7 @@ { + "scripts": { + "build": "tsc && rollup -c rollup.config.mjs" + }, "dependencies": { "mithril": "^2.2.2" }, diff --git a/NGINX_Server/Dockerfile b/NGINX_Server/Dockerfile index b3add02..14763a7 100644 --- a/NGINX_Server/Dockerfile +++ b/NGINX_Server/Dockerfile @@ -1,6 +1,32 @@ ARG NGINX_VERSION +ARG NODE_VERSION +ARG NPM_VERSION + +# ── Stage: frontend — baut das TypeScript-JS-Bundle ────────────── +# Node.js, npm und node_modules landen NICHT im Prod-Image. +FROM node:${NODE_VERSION}-bookworm-slim AS frontend +# Zielverzeichnis fuer rollup-Output erstellen (absoluter Pfad wie in rollup.config.mjs) +RUN mkdir -p /usr/opt/pythonapp/NGINX_Server/html/static +WORKDIR /usr/opt/pythonapp/Client +# Erst package.json + lockfile kopieren (besseres Layer-Caching) +COPY Client/package.json Client/package-lock.json* ./ +RUN npm ci || npm install +# Rest des Client-Quellcodes kopieren +COPY Client/ ./ +# config.ini wird fuer rollup benoetigt (inireader.mjs liest Version/Name) +COPY App_Server/root/config.ini /usr/opt/pythonapp/App_Server/root/config.ini +# tsc + rollup ausfuehren — Output geht nach /usr/opt/pythonapp/NGINX_Server/html/static/ +RUN npm run build + +# ── Stage: nginx ──────────────────────────────────────────────── FROM nginx:${NGINX_VERSION} RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN mkdir -p /etc/certs /etc/nginx/templates -COPY 10-select-ssl.sh /docker-entrypoint.d/10-select-ssl.sh -RUN chmod +x /docker-entrypoint.d/10-select-ssl.sh \ No newline at end of file +COPY NGINX_Server/10-select-ssl.sh /docker-entrypoint.d/10-select-ssl.sh +RUN chmod +x /docker-entrypoint.d/10-select-ssl.sh +# NGINX Config-Templates ins Image kopieren (in Prod; Dev ueberschreibt per bind-mount) +COPY NGINX_Server/configtemplates/ /etc/nginx/configtemplates/ +# Alle statischen Dateien aus dem Repo kopieren (CSS, favicon, fonts, logo) +COPY NGINX_Server/html/static/ /usr/share/nginx/html/static/ +# JS-Bundle aus der frontend-Stage ueberschreibt ggf. vorhandene alte JS-Dateien +COPY --from=frontend /usr/opt/pythonapp/NGINX_Server/html/static/ /usr/share/nginx/html/static/ \ No newline at end of file diff --git a/docker-compose.base.yaml b/docker-compose.base.yaml index 394dd0a..7f97777 100644 --- a/docker-compose.base.yaml +++ b/docker-compose.base.yaml @@ -2,15 +2,19 @@ name: kbpbase services: nginx-proxy: build: - context: NGINX_Server - args: # Übergibt die ARGS an das dockerfile - NGINX_VERSION: ${NGINX_VERSION} + context: . # Repo-Root (fuer frontend-Stage im NGINX-Dockerfile) + dockerfile: NGINX_Server/Dockerfile # explizit, da Context nicht mehr NGINX_Server/ + args: + NGINX_VERSION: ${NGINX_VERSION} + NODE_VERSION: ${NODE_VERSION} + NPM_VERSION: ${NPM_VERSION} ports: - ${NGINX_HOST_PORT}:4430 volumes: - - ${NGINX_STATIC_DIR}:/usr/share/nginx/html:ro - - ${NGINX_CONFIG_TEMPLATE_DIR}:/etc/nginx/configtemplates:ro + # NGINX_STATIC_DIR entfaellt in Prod — statische Dateien sind im NGINX-Image + # NGINX_CONFIG_TEMPLATE_DIR entfaellt in Prod — Templates sind im Image + # Beide werden in Dev per bind-mount ueberschrieben (siehe docker-compose.dev.yaml) - ${NGINX_CERTS}:/etc/certs environment: - NGINX_USE_SSL=${NGINX_USE_SSL:-true} @@ -19,15 +23,13 @@ services: - NGINX_SERVER_NAME=${NGINX_SERVER_NAME} pythonapp: build: - context: App_Server - # target: kommt aus dem prod oder dev dockerfile - args: # Übergibt die ARGS an das dockerfile + context: . # Repo-Root (fuer COPY-Pfade im Dockerfile) + dockerfile: App_Server/Dockerfile # explizit, da Context nicht mehr App_Server/ + args: PYTHON_VERSION: ${PYTHON_VERSION} + NODE_VERSION: ${NODE_VERSION} # fuer dev-Stage + NPM_VERSION: ${NPM_VERSION} # fuer dev-Stage volumes: - #- ${PYTHON_APP_DIR}:/usr/opt/pythonapp/root - ${PYTHON_DB_DIR}:/usr/var/pythonappdb - - python-venv:/usr/opt/pythonapp/env - # - ${PYTHON_ENV_DIR}:/usr/opt/pythonapp/env - -volumes: - python-venv: \ No newline at end of file + # python-venv Volume entfaellt — venv ist im Image + # (dev-Stage definiert eigenen Volume in docker-compose.dev.yaml) \ No newline at end of file diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 900e866..859f58f 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -6,17 +6,30 @@ services: service: pythonapp build: target: dev - args: # Übergibt die ARGS an das dockerfile - # PYTHON_VERSION: ${PYTHON_VERSION} + args: NODE_VERSION: ${NODE_VERSION:-18} NPM_VERSION: ${NPM_VERSION:-8} environment: # Globales Vibe-Home innerhalb des Workspace-Bind-Mounts, damit es auf dem Host persistiert - VIBE_HOME=/usr/opt/pythonapp/.vibe-global + # Debug-Modus aktivieren — Werkzeug-Reloader statt Waitress + - APP_DEBUG=true volumes: - ${PYTHON_DEV_DIR}:/usr/opt/pythonapp - # - ${TYPESCRIPT_SRC_DIR}:/usr/opt/typescript + # python-venv-dev: venv als Volume, da der bind-mount das Image-venv ueberlagert + - python-venv-dev:/usr/opt/pythonapp/env - node-modules:/usr/opt/typescript/node_modules + nginx-proxy: + extends: + file: docker-compose.base.yaml + service: nginx-proxy + volumes: + # Statische Dateien per bind-mount — Aenderungen (tsc/rollup, CSS) sofort sichtbar + - ${NGINX_STATIC_DIR}:/usr/share/nginx/html:ro + # Config-Templates per bind-mount — Aenderungen an NGINX-Config sofort sichtbar + - ${NGINX_CONFIG_TEMPLATE_DIR}:/etc/nginx/configtemplates:ro + volumes: + python-venv-dev: node-modules: \ No newline at end of file diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index 381dc04..1d1d80a 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -6,5 +6,9 @@ services: service: pythonapp build: target: prod - volumes: - - ${PYTHON_APP_DIR}:/usr/opt/pythonapp \ No newline at end of file + # Kein Code-bind-mount mehr — Code ist im Image + nginx-proxy: + extends: + file: docker-compose.base.yaml + service: nginx-proxy + # Kein STATIC_DIR bind-mount mehr — statische Dateien sind im NGINX-Image \ No newline at end of file diff --git a/docker-compose.save.yaml b/docker-compose.save.yaml deleted file mode 100644 index 0cda9af..0000000 --- a/docker-compose.save.yaml +++ /dev/null @@ -1,27 +0,0 @@ -services: - nginx-proxy: - build: - context: NGINX_Server - args: # Übergibt die ARGS an das dockerfile - NGINX_VERSION: ${NGINX_VERSION} - - ports: - - ${NGINX_HOST_PORT}:4430 - volumes: - - ${NGINX_STATIC_DIR}:/usr/share/nginx/html:ro - - ${NGINX_CONFIG_TEMPLATE_DIR}:/etc/nginx/configtemplates:ro - - ${NGINX_CERTS}:/etc/certs - environment: - - NGINX_USE_SSL=${NGINX_USE_SSL:-true} - - NGINX_CERT=${NGINX_CERT} - - NGINX_CERT_KEY=${NGINX_CERT_KEY} - - NGINX_SERVER_NAME=${NGINX_SERVER_NAME} - pythonapp: - build: - context: App_Server - args: # Übergibt die ARGS an das dockerfile - PYTHON_VERSION: ${PYTHON_VERSION} - volumes: - - ${PYTHON_APP_DIR}:/usr/opt/pythonapp/root - - ${PYTHON_DB_DIR}:/usr/var/pythonappdb - # - ${PYTHON_ENV_DIR}:/usr/opt/pythonapp/env \ No newline at end of file diff --git a/env-sample b/env-sample index 7481c5d..06318a8 100644 --- a/env-sample +++ b/env-sample @@ -1,3 +1,12 @@ +# ============================================================================= +# BUILD-UMGEBUNG (zentrale Einstellung — hier dev/prod umschalten) +# ============================================================================= +# Bestimmt, welche docker-compose-*.yaml neben der base-Datei geladen wird. +# COMPOSE_FILE wird von Docker Compose automatisch ausgewertet (auch via .env); +# mehrere Dateien werden durch ':' getrennt und nacheinander gemerged. +BUILD_ENVIRONMENT=dev +COMPOSE_FILE=docker-compose.base.yaml:docker-compose.${BUILD_ENVIRONMENT}.yaml + # Parameter für NGINX-Server NGINX_VERSION=mainline-alpine # SSL für NGINX aktivieren (true) oder deaktivieren (false). @@ -5,7 +14,11 @@ NGINX_VERSION=mainline-alpine NGINX_USE_SSL=true NGINX_HOST_PORT=443 NGINX_SERVER_NAME=kbp_crm.mylocal.de +# NGINX_STATIC_DIR: nur in Dev benoetigt (bind-mount fuer statische Dateien). +# In Prod liegen die statischen Dateien im NGINX-Image. NGINX_STATIC_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/html +# NGINX_CONFIG_TEMPLATE_DIR: nur in Dev benoetigt (bind-mount fuer NGINX-Templates). +# In Prod liegen die Templates im NGINX-Image. NGINX_CONFIG_TEMPLATE_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/configtemplates NGINX_CERTS=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/certs NGINX_CERT=wildcard.mylocal.crt @@ -13,7 +26,8 @@ NGINX_CERT_KEY=wildcard.mylocal.key # Parameter for Python App-Server PYTHON_VERSION=slim-bookworm -PYTHON_APP_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/root +# PYTHON_APP_DIR entfaellt — Code wird in Prod ins Image gebaut (Option B). +# In Dev wird PYTHON_DEV_DIR als bind-mount verwendet (siehe docker-compose.dev.yaml). PYTHON_DEV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm PYTHON_DB_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/data # PYTHON_ENV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/env @@ -22,14 +36,6 @@ PYTHON_DB_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/data NODE_VERSION=lts NPM_VERSION=latest -# Umgebung für Docker-Compose-Stack (prod oder dev). -# Bestimmt, welche docker-compose-*.yaml neben der base-Datei geladen wird. -# COMPOSE_FILE wird von Docker Compose automatisch ausgewertet (auch via .env); -# mehrere Dateien werden durch ':' getrennt und nacheinander gemerged. -# Dadurch entfällt der mehrfache Aufruf des -f Parameters beim docker compose Aufruf. -BUILD_ENVIRONMENT=dev -COMPOSE_FILE=docker-compose.base.yaml:docker-compose.${BUILD_ENVIRONMENT}.yaml - # ============================================================================= # ZITADEL OIDC SETTINGS # ============================================================================= diff --git a/scripts/deploy-kbcrm-prod.sh b/scripts/deploy-kbcrm-prod.sh new file mode 100644 index 0000000..07b236b --- /dev/null +++ b/scripts/deploy-kbcrm-prod.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Deploy-Script: Promotion nach PROD (manuelles OK erforderlich) +# Wird manuell aufgerufen, nachdem test-prod verifiziert wurde. +# Voraussetzungen auf dem Server: git, docker, docker compose. +set -euo pipefail + +# Konfiguration — an die jeweilige Server-Umgebung anpassen +REPO_DIR=/opt/kbcrm-prod/repo +COMPOSE_FILE=docker-compose.prod.yaml +ENV_FILE=/opt/kbcrm-prod/.env +PROD_PORT=443 + +echo "=== Deploy KB-CRM PROD ===" + +# Sicherheitsabfrage — nur bei manueller Eingabe von "yes" wird fortgesetzt +echo "This will deploy to PRODUCTION. Continue? (yes/no)" +read -r confirmation +if [ "$confirmation" != "yes" ]; then + echo "Aborted." + exit 1 +fi + +# 1. Code aktualisieren +cd "$REPO_DIR" +echo "=== Pulling latest code ===" +git fetch origin main +git checkout main +git reset --hard origin/main + +# 2. Images neu bauen und Container starten +echo "=== Building and starting containers ===" +docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d --build + +# 3. Health-Check +echo "=== Waiting for container startup ===" +sleep 10 +if curl -sf "http://localhost:${PROD_PORT}/health-check" >/dev/null 2>&1; then + echo "Health check: OK" +else + echo "WARNING: Health check failed — check container logs with:" + echo " docker compose --env-file $ENV_FILE -f $COMPOSE_FILE logs" +fi + +echo "=== Deploy PROD complete ===" diff --git a/scripts/deploy-kbcrm-test.sh b/scripts/deploy-kbcrm-test.sh new file mode 100644 index 0000000..9a5e148 --- /dev/null +++ b/scripts/deploy-kbcrm-test.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Deploy-Script: Automatisches Deployment nach TEST (test-prod) +# Wird durch Git-Hook/Webhook oder manuell aufgerufen. +# Voraussetzungen auf dem Server: git, docker, docker compose. +set -euo pipefail + +# Konfiguration — an die jeweilige Server-Umgebung anpassen +REPO_DIR=/opt/kbcrm-test/repo +COMPOSE_FILE=docker-compose.prod.yaml +ENV_FILE=/opt/kbcrm-test/.env +TEST_PORT=8443 + +echo "=== Deploy KB-CRM TEST ===" + +# 1. Code aktualisieren +cd "$REPO_DIR" +echo "=== Pulling latest code ===" +git fetch origin main +git checkout main +git reset --hard origin/main + +# 2. Images neu bauen und Container starten +echo "=== Building and starting containers ===" +docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d --build + +# 3. Health-Check +echo "=== Waiting for container startup ===" +sleep 10 +if curl -sf "http://localhost:${TEST_PORT}/health-check" >/dev/null 2>&1; then + echo "Health check: OK" +else + echo "WARNING: Health check failed — check container logs with:" + echo " docker compose --env-file $ENV_FILE -f $COMPOSE_FILE logs" +fi + +echo "=== Deploy TEST complete ===" From 40ad0e1ba9ec729834651332cc199e923f68f1b2 Mon Sep 17 00:00:00 2001 From: Gerd Date: Sun, 13 Sep 2026 19:12:14 +0200 Subject: [PATCH 2/5] refactor(deploy): Image-Versionen fest in Dockerfiles verankern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARG-Variablen durch feste Werte in FROM ersetzt — nicht per .env ueberschreibbar. PYTHON_VERSION, NODE_VERSION, NPM_VERSION, NGINX_VERSION aus .env, env-sample und Compose-Dateien entfernt. Deployment-Anleitung erstellt und entsprechend angepasst. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- App_Server/Dockerfile | 14 +- Dokumentationen/Deployment-Anleitung.md | 422 ++++++++++++++++++++++++ NGINX_Server/Dockerfile | 8 +- docker-compose.base.yaml | 9 - docker-compose.dev.yaml | 3 - env-sample | 5 - 6 files changed, 429 insertions(+), 32 deletions(-) create mode 100644 Dokumentationen/Deployment-Anleitung.md diff --git a/App_Server/Dockerfile b/App_Server/Dockerfile index bbd86c8..5332131 100644 --- a/App_Server/Dockerfile +++ b/App_Server/Dockerfile @@ -1,11 +1,8 @@ -ARG PYTHON_VERSION -ARG NODE_VERSION -ARG NPM_VERSION - # ── Stage: prod ────────────────────────────────────────────────── # Build-Context ist das Repo-Root (.), daher muessen alle COPY-Pfade # mit App_Server/ praefigiert werden. -FROM python:${PYTHON_VERSION} AS prod +# Python-Version fest im Dockerfile verankert — nicht per .env ueberschreibbar. +FROM python:slim-bookworm AS prod RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN apt-get clean && apt-get update && apt-get install -y locales RUN echo "de_DE.UTF-8 UTF-8" >> /etc/locale.gen @@ -30,8 +27,7 @@ ENTRYPOINT ["sh", "/usr/opt/pythonapp/App_Server/scripts/init.sh"] # ── Stage: dev ─────────────────────────────────────────────────── FROM prod AS dev -ARG NODE_VERSION -ARG NPM_VERSION +# Node.js-Version fest im Dockerfile verankert. # --- 2. Systemabhängigkeiten (für Python + Node.js) --- RUN apt-get update && \ @@ -49,9 +45,9 @@ RUN apt-get update && \ && rm -rf /var/lib/apt/lists/* # --- 3. Node.js + npm installieren --- -RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \ +RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \ apt-get install -y nodejs && \ - npm install -g npm@${NPM_VERSION} + npm install -g npm@latest # --- 4. Dev-Start-Script kopieren (fuer Konsistenz, wird sonst per bind-mount bereitgestellt) --- COPY App_Server/scripts/inittypescriptdev.sh /usr/opt/pythonapp/App_Server/scripts/inittypescriptdev.sh diff --git a/Dokumentationen/Deployment-Anleitung.md b/Dokumentationen/Deployment-Anleitung.md new file mode 100644 index 0000000..54d4b10 --- /dev/null +++ b/Dokumentationen/Deployment-Anleitung.md @@ -0,0 +1,422 @@ +# Anleitung: Automatisches Deployment einrichten + +Diese Anleitung richtet sich an den Administrator des Deploy-Servers. +Sie beschreibt, wie der automatische Deploy-Mechanismus fuer KB-CRM +eingerichtet und betrieben wird. + +--- + +## 1. Architektur ueberblick + +``` + Entwickler + │ git push origin main + ▼ + ┌─────────────────┐ + │ Git-Remote │ (z.B. Gitea auf git.everdc.eu) + └────────┬────────┘ + │ Webhook oder post-receive Hook + ▼ + ┌─────────────────┐ + │ Deploy-Server │ + │ (Deploy-Script) │ + └────────┬────────┘ + │ + ┌──────────┴──────────┐ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ Stufe 1: TEST │ │ Stufe 2: PROD │ + │ (automatisch) │ │ (manuell) │ + └─────────────────┘ └─────────────────┘ +``` + +**Prinzip:** +- Jeder `git push` auf `main` loest automatisch ein Deployment auf dem + Test-Server aus (Stufe 1). +- Nach erfolgreicher Verifikation wird manuell auf den Prod-Server + deployt (Stufe 2). +- In Prod wird der gesamte Code (Python + TypeScript) in das Docker-Image + gebaut. Es wird kein Node.js, npm oder Python-venv auf dem Server benoetigt. +- Der Deploy-Server benoetigt nur: `git`, `docker` und `docker compose`. + +--- + +## 2. Voraussetzungen auf dem Deploy-Server + +Folgende Software muss installiert sein: + +| Software | Version | Zweck | +|---|---|---| +| `git` | aktuell | Code vom Remote-Repository holen | +| `docker` | aktuell | Container bauen und starten | +| `docker compose` | v2+ | Multi-Container-Orchestrierung | +| `curl` | aktuell | Health-Check nach Deploy | + +Docker Compose muss als Plugin installiert sein (Befehl: `docker compose`, +nicht `docker-compose`). + +--- + +## 3. Verzeichnisstruktur auf dem Deploy-Server + +Es werden zwei Umgebungen (test-prod und prod) parallel betrieben: + +``` +/opt/kbcrm/ +├── test-prod/ +│ ├── repo/ ← git clone des Repos +│ └── .env ← test-prod Umgebung (eigene Ports, eigene DB) +├── prod/ +│ ├── repo/ +│ └── .env ← prod Umgebung +└── scripts/ + ├── deploy-kbcrm-test.sh ← aus dem Repo (scripts/) + └── deploy-kbcrm-prod.sh ← aus dem Repo (scripts/) +``` + +Die SQLite-Datenbanken liegen ausserhalb des Repos in einem bind-mount +(`PYTHON_DB_DIR`), der beim Deploy nicht beruehrt wird. + +--- + +## 4. Einrichtung: Schritt fuer Schritt + +### 4.1. Repositories klonen + +```bash +sudo mkdir -p /opt/kbcrm/test-prod /opt/kbcrm/prod + +# Test-Prod Repository +sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/test-prod/repo + +# Prod Repository +sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/prod/repo +``` + +### 4.2. `.env`-Dateien anlegen + +Kopieren Sie die Vorlage aus dem Repo und passen Sie die Werte an: + +```bash +# Test-Prod +cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/test-prod/.env + +# Prod +cp /opt/kbcrm/prod/repo/env-sample /opt/kbcrm/prod/.env +``` + +### 4.3. `.env` fuer Test-Prod anpassen + +```bash +vi /opt/kbcrm/test-prod/.env +``` + +Folgende Einstellungen muessen angepasst werden: + +```bash +# WICHTIG: BUILD_ENVIRONMENT muss in den .env-Dateien auf dem Deploy-Server +# NICHT gesetzt werden. Die Deploy-Scripts rufen docker compose explizit +# mit -f docker-compose.prod.yaml auf und ignorieren COMPOSE_FILE. +# Daher kann BUILD_ENVIRONMENT und COMPOSE_FILE aus der .env geloescht +# oder ignoriert werden. + +# NGINX-Server +NGINX_USE_SSL=true +NGINX_HOST_PORT=8443 # eigener Port fuer Test-Prod +NGINX_SERVER_NAME=test.kbp_crm.mylocal.de +# NGINX_STATIC_DIR und NGINX_CONFIG_TEMPLATE_DIR werden in Prod nicht +# benoetigt (statische Dateien und Templates sind im Image). +NGINX_CERTS=/opt/kbcrm/test-prod/certs # Pfad zu SSL-Zertifikaten +NGINX_CERT=test-wildcard.mylocal.crt +NGINX_CERT_KEY=test-wildcard.mylocal.key + +# Python App-Server +PYTHON_DB_DIR=/opt/kbcrm/test-prod/data # eigene DB fuer Test-Prod + + +# OIDC-Settings anpassen (ggf. separater OIDC-Client fuer Test) +ZITADEL_ISSUER=https://id.vmlx.de +ZITADEL_CLIENT_ID= +ZITADEL_CLIENT_SECRET= +ZITADEL_REDIRECT_URI=https://test.kbp_crm.mylocal.de/auth/callback +``` + +### 4.4. `.env` fuer Prod anpassen + +```bash +vi /opt/kbcrm/prod/.env +``` + +```bash +NGINX_HOST_PORT=443 # Produktions-Port +NGINX_SERVER_NAME=kbp_crm.mylocal.de +NGINX_CERTS=/opt/kbcrm/prod/certs +NGINX_CERT=wildcard.mylocal.crt +NGINX_CERT_KEY=wildcard.mylocal.key + +PYTHON_DB_DIR=/opt/kbcrm/prod/data # eigene DB fuer Prod + +ZITADEL_REDIRECT_URI=https://kbp_crm.mylocal.de/auth/callback +``` + +### 4.5. Datenbank-Verzeichnisse erstellen + +```bash +sudo mkdir -p /opt/kbcrm/test-prod/data +sudo mkdir -p /opt/kbcrm/prod/data +``` + +### 4.6. SSL-Zertifikate ablegen + +Kopieren Sie die Zertifikate in die in `.env` angegebenen Verzeichnisse: + +```bash +sudo mkdir -p /opt/kbcrm/test-prod/certs /opt/kbcrm/prod/certs +sudo cp wildcard.mylocal.crt wildcard.mylocal.key /opt/kbcrm/test-prod/certs/ +sudo cp wildcard.mylocal.crt wildcard.mylocal.key /opt/kbcrm/prod/certs/ +``` + +### 4.7. Deploy-Scripts kopieren + +Die Scripts liegen im Repo unter `scripts/`. Kopieren Sie sie an einen +zentralen Ort: + +```bash +sudo mkdir -p /opt/kbcrm/scripts +sudo cp /opt/kbcrm/test-prod/repo/scripts/deploy-kbcrm-test.sh /opt/kbcrm/scripts/ +sudo cp /opt/kbcrm/prod/repo/scripts/deploy-kbcrm-prod.sh /opt/kbcrm/scripts/ +sudo chmod +x /opt/kbcrm/scripts/*.sh +``` + +Passen Sie die Pfad-Variablen am Anfang der Scripts an, falls Ihre +Verzeichnisstruktur abweicht. + +--- + +## 5. Git-Hook einrichten (automatisches Test-Deploy) + +### Variante A: post-receive Hook (direkt auf dem Git-Server) + +Wenn Shell-Zugang zum Git-Server besteht: + +```bash +# Auf dem Git-Server, im bare Repository: +vi /srv/git/kbcrm.git/hooks/post-receive +``` + +```bash +#!/bin/bash +while read oldrev newrev ref; do + if [ "$ref" = "refs/heads/main" ]; then + ssh deploy-user@deploy-server "/opt/kbcrm/scripts/deploy-kbcrm-test.sh" + fi +done +``` + +```bash +chmod +x /srv/git/kbcrm.git/hooks/post-receive +``` + +### Variante B: Webhook (empfohlen fuer Gitea) + +1. In Gitea: Repository-Einstellungen -> Webhooks -> Webhook hinzufuegen +2. URL: `http://deploy-server:9000/webhook` (Beispiel) +3. Content-Type: `application/json` +4. Trigger: "Push Events" (nur auf `main`) +5. Auf dem Deploy-Server: kleiner HTTP-Listener, der den Webhook entgegennimmt + und das Deploy-Script startet. + +Beispiel fuer einen einfachen Webhook-Listener (systemd-Service mit Python): + +```python +# /opt/kbcrm/scripts/webhook-listener.py +from http.server import HTTPServer, BaseHTTPRequestHandler +import subprocess + +class Handler(BaseHTTPRequestHandler): + def do_POST(self): + subprocess.run(["/opt/kbcrm/scripts/deploy-kbcrm-test.sh"]) + self.send_response(200) + self.end_headers() + +HTTPServer(("", 9000), Handler).serve_forever() +``` + +Systemd-Service: + +```ini +# /etc/systemd/system/kbcrm-webhook.service +[Unit] +Description=KB-CRM Deploy Webhook Listener +After=network.target + +[Service] +ExecStart=/usr/bin/python3 /opt/kbcrm/scripts/webhook-listener.py +Restart=always +User=deploy-user + +[Install] +WantedBy=multi-user.target +``` + +```bash +sudo systemctl enable --now kbcrm-webhook +``` + +--- + +## 6. Deployment durchfuehren + +### 6.1. Automatisches Deploy nach Test-Prod + +Wird durch `git push` ausgeloest (sofern Hook eingerichtet). Manuell: + +```bash +/opt/kbcrm/scripts/deploy-kbcrm-test.sh +``` + +Das Script fuehrt folgende Schritte aus: +1. `git pull` (latest main) +2. `docker compose up -d --build` (baut beide Images: NGINX mit TypeScript- + Build und Python mit Anwendungscode) +3. Health-Check + +### 6.2. Manuelle Promotion nach Prod + +Nach Verifikation auf dem Test-Server: + +```bash +/opt/kbcrm/scripts/deploy-kbcrm-prod.sh +``` + +Das Script fragt eine Bestaetigung ("yes/no") und fuehrt dann den gleichen +Ablauf aus wie das Test-Script, jedoch mit der Prod-`.env`. + +### 6.3. Alternative: Git-Tag-basierte Promotion + +Statt das Prod-Script manuell aufzurufen, kann ein Git-Tag das Prod-Deploy +ausloesen: + +```bash +# Auf dem Entwickler-Rechner: +git tag -a v00.00.02 -m "Release: deploy to prod" +git push origin v00.00.02 +``` + +Der `post-receive`-Hook prueft auf Tags: + +```bash +#!/bin/bash +while read oldrev newrev ref; do + if [ "$ref" = "refs/heads/main" ]; then + ssh deploy-user@deploy-server "/opt/kbcrm/scripts/deploy-kbcrm-test.sh" + elif [[ "$ref" = refs/tags/v* ]]; then + ssh deploy-user@deploy-server "/opt/kbcrm/scripts/deploy-kbcrm-prod.sh" + fi +done +``` + +--- + +## 7. Wartung und Fehlersuche + +### Logs anzeigen + +```bash +# Test-Prod +docker compose --env-file /opt/kbcrm/test-prod/.env \ + -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs + +# Prod +docker compose --env-file /opt/kbcrm/prod/.env \ + -f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs + +# Nur Python-Container +docker compose --env-file /opt/kbcrm/prod/.env \ + -f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs pythonapp + +# Nur NGINX-Container +docker compose --env-file /opt/kbcrm/prod/.env \ + -f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs nginx-proxy +``` + +### Container-Status pruefen + +```bash +docker compose --env-file /opt/kbcrm/prod/.env \ + -f /opt/kbcrm/prod/repo/docker-compose.prod.yaml ps +``` + +### Manuell in den Container wechseln + +```bash +docker compose --env-file /opt/kbcrm/prod/.env \ + -f /opt/kbcrm/prod/repo/docker-compose.prod.yaml exec pythonapp bash +``` + +### Rollback + +Bei Problemen kann auf einen frueheren Stand zurueckgekehrt werden: + +```bash +# Auf dem Deploy-Server: +cd /opt/kbcrm/prod/repo +git log --oneline -10 # frueheren Commit finden +git checkout +docker compose --env-file /opt/kbcrm/prod/.env \ + -f docker-compose.prod.yaml up -d --build +``` + +### Datenbank sichern + +Die SQLite-Datenbanken liegen im bind-mount (`PYTHON_DB_DIR`). +Sicherung: + +```bash +cp /opt/kbcrm/prod/data/*.db /backup/kbcrm-prod-$(date +%Y%m%d).db +``` + +### Docker-Images aufraeumen + +Nach mehreren Deploys koennen alte Images Speicher belegen: + +```bash +docker image prune -f +``` + +--- + +## 8. Uebersicht: `.env`-Variablen fuer Prod + +| Variable | Erforderlich | Beschreibung | +|---|---|---| +| `NGINX_USE_SSL` | ja | SSL aktivieren (true/false) | +| `NGINX_HOST_PORT` | ja | Externer Port (443 fuer SSL, 8080 fuer no-SSL) | +| `NGINX_SERVER_NAME` | ja | Server-Name fuer NGINX-Konfiguration | +| `NGINX_CERTS` | ja | Pfad zu SSL-Zertifikaten (bind-mount) | +| `NGINX_CERT` | ja | Zertifikatsdatei | +| `NGINX_CERT_KEY` | ja | Privater Schluessel | +| `NGINX_STATIC_DIR` | nein | Nur in Dev; in Prod im Image | +| `NGINX_CONFIG_TEMPLATE_DIR` | nein | Nur in Dev; in Prod im Image | +| `PYTHON_DB_DIR` | ja | Pfad zu SQLite-Datenbanken (bind-mount) | +| `PYTHON_DEV_DIR` | nein | Nur in Dev | +| `APP_DEBUG` | nein | Nur in Dev; in Prod nicht setzen | +| `BUILD_ENVIRONMENT` | nein | Nicht relevant — Deploy-Scripts geben Compose-Datei explizit an. In .env auf Deploy-Server nicht erforderlich | +| `ZITADEL_*` | ja | OIDC-Konfiguration | +| `COMPOSE_FILE` | nein | Wird von Deploy-Scripts ignoriert | + +--- + +## 9. Checkliste fuer den Admin + +- [ ] Docker und Docker Compose installiert +- [ ] Git installiert, Repository geklont (test-prod + prod) +- [ ] `.env`-Dateien erstellt und angepasst (Ports, DB, Zertifikate, OIDC) +- [ ] Datenbank-Verzeichnisse erstellt (`PYTHON_DB_DIR`) +- [ ] SSL-Zertifikate abgelegt (`NGINX_CERTS`) +- [ ] Deploy-Scripts kopiert und ausfuehrbar gemacht +- [ ] Git-Hook oder Webhook eingerichtet (fuer automatisches Test-Deploy) +- [ ] Erster manueller Deploy-Test: `deploy-kbcrm-test.sh` +- [ ] Health-Check erfolgreich: `curl -k https://localhost:8443/health-check` +- [ ] Erster Prod-Deploy: `deploy-kbcrm-prod.sh` +- [ ] Health-Check erfolgreich: `curl -k https://localhost:443/health-check` diff --git a/NGINX_Server/Dockerfile b/NGINX_Server/Dockerfile index 14763a7..6662e47 100644 --- a/NGINX_Server/Dockerfile +++ b/NGINX_Server/Dockerfile @@ -1,10 +1,6 @@ -ARG NGINX_VERSION -ARG NODE_VERSION -ARG NPM_VERSION - # ── Stage: frontend — baut das TypeScript-JS-Bundle ────────────── # Node.js, npm und node_modules landen NICHT im Prod-Image. -FROM node:${NODE_VERSION}-bookworm-slim AS frontend +FROM node:lts-bookworm-slim AS frontend # Zielverzeichnis fuer rollup-Output erstellen (absoluter Pfad wie in rollup.config.mjs) RUN mkdir -p /usr/opt/pythonapp/NGINX_Server/html/static WORKDIR /usr/opt/pythonapp/Client @@ -19,7 +15,7 @@ COPY App_Server/root/config.ini /usr/opt/pythonapp/App_Server/root/config.ini RUN npm run build # ── Stage: nginx ──────────────────────────────────────────────── -FROM nginx:${NGINX_VERSION} +FROM nginx:mainline-alpine RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN mkdir -p /etc/certs /etc/nginx/templates COPY NGINX_Server/10-select-ssl.sh /docker-entrypoint.d/10-select-ssl.sh diff --git a/docker-compose.base.yaml b/docker-compose.base.yaml index 7f97777..258a375 100644 --- a/docker-compose.base.yaml +++ b/docker-compose.base.yaml @@ -4,11 +4,6 @@ services: build: context: . # Repo-Root (fuer frontend-Stage im NGINX-Dockerfile) dockerfile: NGINX_Server/Dockerfile # explizit, da Context nicht mehr NGINX_Server/ - args: - NGINX_VERSION: ${NGINX_VERSION} - NODE_VERSION: ${NODE_VERSION} - NPM_VERSION: ${NPM_VERSION} - ports: - ${NGINX_HOST_PORT}:4430 volumes: @@ -25,10 +20,6 @@ services: build: context: . # Repo-Root (fuer COPY-Pfade im Dockerfile) dockerfile: App_Server/Dockerfile # explizit, da Context nicht mehr App_Server/ - args: - PYTHON_VERSION: ${PYTHON_VERSION} - NODE_VERSION: ${NODE_VERSION} # fuer dev-Stage - NPM_VERSION: ${NPM_VERSION} # fuer dev-Stage volumes: - ${PYTHON_DB_DIR}:/usr/var/pythonappdb # python-venv Volume entfaellt — venv ist im Image diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 859f58f..0e2c27e 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -6,9 +6,6 @@ services: service: pythonapp build: target: dev - args: - NODE_VERSION: ${NODE_VERSION:-18} - NPM_VERSION: ${NPM_VERSION:-8} environment: # Globales Vibe-Home innerhalb des Workspace-Bind-Mounts, damit es auf dem Host persistiert - VIBE_HOME=/usr/opt/pythonapp/.vibe-global diff --git a/env-sample b/env-sample index 06318a8..318e9bc 100644 --- a/env-sample +++ b/env-sample @@ -8,7 +8,6 @@ BUILD_ENVIRONMENT=dev COMPOSE_FILE=docker-compose.base.yaml:docker-compose.${BUILD_ENVIRONMENT}.yaml # Parameter für NGINX-Server -NGINX_VERSION=mainline-alpine # SSL für NGINX aktivieren (true) oder deaktivieren (false). # Bei false muss NGINX_HOST_PORT angepasst werden (z.B. 8080 statt 443). NGINX_USE_SSL=true @@ -25,16 +24,12 @@ NGINX_CERT=wildcard.mylocal.crt NGINX_CERT_KEY=wildcard.mylocal.key # Parameter for Python App-Server -PYTHON_VERSION=slim-bookworm # PYTHON_APP_DIR entfaellt — Code wird in Prod ins Image gebaut (Option B). # In Dev wird PYTHON_DEV_DIR als bind-mount verwendet (siehe docker-compose.dev.yaml). PYTHON_DEV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm PYTHON_DB_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/data # PYTHON_ENV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/env -# Parameter für Dev-Mode (Development-Container für VS-Code) -NODE_VERSION=lts -NPM_VERSION=latest # ============================================================================= # ZITADEL OIDC SETTINGS From b268b301c17082944f4818587df4a9b44ea0e64a Mon Sep 17 00:00:00 2001 From: Gerd Date: Sun, 13 Sep 2026 19:30:09 +0200 Subject: [PATCH 3/5] docs(deploy): COMPOSE_FILE-Beschreibung um BUILD_ENVIRONMENT-Bezug ergaenzt Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- Dokumentationen/Deployment-Anleitung.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dokumentationen/Deployment-Anleitung.md b/Dokumentationen/Deployment-Anleitung.md index 54d4b10..6e5fffb 100644 --- a/Dokumentationen/Deployment-Anleitung.md +++ b/Dokumentationen/Deployment-Anleitung.md @@ -403,7 +403,7 @@ docker image prune -f | `APP_DEBUG` | nein | Nur in Dev; in Prod nicht setzen | | `BUILD_ENVIRONMENT` | nein | Nicht relevant — Deploy-Scripts geben Compose-Datei explizit an. In .env auf Deploy-Server nicht erforderlich | | `ZITADEL_*` | ja | OIDC-Konfiguration | -| `COMPOSE_FILE` | nein | Wird von Deploy-Scripts ignoriert | +| `COMPOSE_FILE` | nein | Wird von Deploy-Scripts ignoriert (s. `BUILD_ENVIRONMENT`)| --- From fffa9d4a2807759b315c70fcbf01ee76af3be7c0 Mon Sep 17 00:00:00 2001 From: Gerd Date: Sun, 13 Sep 2026 19:47:42 +0200 Subject: [PATCH 4/5] docs(deploy): Multi-Stage-Build korrekt beschrieben, SSL-Variablen-Hinweis ergaenzt Die Deploy-Anleitung sagte irrefuehrend "gesamter Code (Python + TypeScript)" im Image. Korrigiert auf den tatsaechlichen Multi-Stage-Build: nur das fertige JS-Bundle landet im NGINX-Image, nicht der TS-Quellcode. Zussaetzlich Hinweis in env-sample und Deployment-Anleitung, dass NGINX_CERTS/CERT/CERT_KEY auch bei SSL=false nicht geloescht werden duerfen, da docker-compose.base.yaml sie immer als Volume/Env bindet. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- Dokumentationen/Deployment-Anleitung.md | 20 +++++++++++++++----- env-sample | 9 +++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Dokumentationen/Deployment-Anleitung.md b/Dokumentationen/Deployment-Anleitung.md index 6e5fffb..736d052 100644 --- a/Dokumentationen/Deployment-Anleitung.md +++ b/Dokumentationen/Deployment-Anleitung.md @@ -35,8 +35,11 @@ eingerichtet und betrieben wird. Test-Server aus (Stufe 1). - Nach erfolgreicher Verifikation wird manuell auf den Prod-Server deployt (Stufe 2). -- In Prod wird der gesamte Code (Python + TypeScript) in das Docker-Image - gebaut. Es wird kein Node.js, npm oder Python-venv auf dem Server benoetigt. +- In Prod wird der Python-Code in das App-Image kopiert. Der TypeScript-Quellcode + wird waehrend des Docker-Builds in einer Multi-Stage-Build-Stage kompiliert + (tsc + rollup); in das finale NGINX-Image wird nur das fertige JS-Bundle + uebernommen, nicht der TypeScript-Quellcode. Es wird kein Node.js, npm oder + Python-venv auf dem Server benoetigt. - Der Deploy-Server benoetigt nur: `git`, `docker` und `docker compose`. --- @@ -126,6 +129,11 @@ NGINX_HOST_PORT=8443 # eigener Port fuer Test-Prod NGINX_SERVER_NAME=test.kbp_crm.mylocal.de # NGINX_STATIC_DIR und NGINX_CONFIG_TEMPLATE_DIR werden in Prod nicht # benoetigt (statische Dateien und Templates sind im Image). +# WICHTIG: Auch bei NGINX_USE_SSL=false duerfen NGINX_CERTS, NGINX_CERT und +# NGINX_CERT_KEY NICHT geloescht werden. docker-compose.base.yaml bindet +# ${NGINX_CERTS} immer als Volume (/etc/certs) ein — ein fehlender Wert +# fuehrt zu einem Compose-Fehler. Bei SSL=false reicht es, Platzhalterwerte +# zu setzen (z.B. NGINX_CERTS auf ein beliebiges Verzeichnis zeigen lassen). NGINX_CERTS=/opt/kbcrm/test-prod/certs # Pfad zu SSL-Zertifikaten NGINX_CERT=test-wildcard.mylocal.crt NGINX_CERT_KEY=test-wildcard.mylocal.key @@ -153,6 +161,8 @@ NGINX_SERVER_NAME=kbp_crm.mylocal.de NGINX_CERTS=/opt/kbcrm/prod/certs NGINX_CERT=wildcard.mylocal.crt NGINX_CERT_KEY=wildcard.mylocal.key +# Auch hier gilt: Bei NGINX_USE_SSL=false die Zertifikats-Variablen nicht +# loeschen, sondern Platzhalterwerte stehen lassen (s. Hinweis oben). PYTHON_DB_DIR=/opt/kbcrm/prod/data # eigene DB fuer Prod @@ -393,9 +403,9 @@ docker image prune -f | `NGINX_USE_SSL` | ja | SSL aktivieren (true/false) | | `NGINX_HOST_PORT` | ja | Externer Port (443 fuer SSL, 8080 fuer no-SSL) | | `NGINX_SERVER_NAME` | ja | Server-Name fuer NGINX-Konfiguration | -| `NGINX_CERTS` | ja | Pfad zu SSL-Zertifikaten (bind-mount) | -| `NGINX_CERT` | ja | Zertifikatsdatei | -| `NGINX_CERT_KEY` | ja | Privater Schluessel | +| `NGINX_CERTS` | ja | Pfad zu SSL-Zertifikaten (bind-mount). Auch bei SSL=false erforderlich — Compose bindet diesen Pfad immer als Volume ein | +| `NGINX_CERT` | ja | Zertifikatsdatei. Bei SSL=false nicht verwendet, darf aber nicht geloescht werden (Platzhalter reicht) | +| `NGINX_CERT_KEY` | ja | Privater Schluessel. Bei SSL=false nicht verwendet, darf aber nicht geloescht werden (Platzhalter reicht) | | `NGINX_STATIC_DIR` | nein | Nur in Dev; in Prod im Image | | `NGINX_CONFIG_TEMPLATE_DIR` | nein | Nur in Dev; in Prod im Image | | `PYTHON_DB_DIR` | ja | Pfad zu SQLite-Datenbanken (bind-mount) | diff --git a/env-sample b/env-sample index 318e9bc..e93dd8a 100644 --- a/env-sample +++ b/env-sample @@ -10,6 +10,15 @@ COMPOSE_FILE=docker-compose.base.yaml:docker-compose.${BUILD_ENVIRONMENT}.yaml # Parameter für NGINX-Server # SSL für NGINX aktivieren (true) oder deaktivieren (false). # Bei false muss NGINX_HOST_PORT angepasst werden (z.B. 8080 statt 443). +# +# WICHTIG: Auch wenn SSL deaktiviert wird (NGINX_USE_SSL=false), duerfen die +# Zertifikats-Variablen (NGINX_CERTS, NGINX_CERT, NGINX_CERT_KEY) NICHT geloescht +# werden. docker-compose.base.yaml bindet ${NGINX_CERTS} immer als Volume +# (/etc/certs) ein — ein fehlender Wert fuehrt zu einem Compose-Fehler. +# NGINX_CERT und NGINX_CERT_KEY werden ebenfalls immer als Umgebungsvariablen +# an den Container uebergeben; bei SSL=false werden sie nicht verwendet, muessen +# aber als Platzhalter erhalten bleiben. Es reicht, gueltige Platzhalterwerte +# einzutragen (z.B. NGINX_CERTS auf ein beliebiges Verzeichnis zeigen lassen). NGINX_USE_SSL=true NGINX_HOST_PORT=443 NGINX_SERVER_NAME=kbp_crm.mylocal.de From 4015f83ba04046edbf8f7538e3bf87601ee55318 Mon Sep 17 00:00:00 2001 From: Gerd Date: Sun, 13 Sep 2026 20:08:12 +0200 Subject: [PATCH 5/5] chore(runserver): Start-Meldungen fuer Server-Typ ergaenzt Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- App_Server/root/runserver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/App_Server/root/runserver.py b/App_Server/root/runserver.py index 3ea7615..5f072bf 100644 --- a/App_Server/root/runserver.py +++ b/App_Server/root/runserver.py @@ -8,12 +8,15 @@ def run_server(): if app.config["DEBUG"] is False: _server = "waitress" # serve(app, listen='nginx-proxy:8080') # Funktioniert aus unbekannten Gründen nicht. Der Hostname wird im Docker Container nicht korrekt aufgelöst + print(f"Server startet mit Server type: {_server}", flush=True) serve(app, listen='*:8080') else: _server = "werkzeug" + print(f"Server startet mit Server type: {_server}", flush=True) serving.run_simple("0.0.0.0", 8080, app, use_reloader=True) except Exception as ex: print(str(ex)) + print(f"Server gestartet. Server type: {_server}", flush=True) if __name__ == '__main__': app = init_app()