Files
kbcrm/Dokumentationen/Deployment-Anleitung.md
gg efef212544 feat(deploy): Image-Promotion, deploy.env, Health-Check-Fix und Anleitungen
- Deploy-Scripts: Image-Promotion statt Rebuild (docker tag + up -d),
  deploy.env fuer Server-Konfiguration, -p fuer Projekt-Namen,
  Health-Check mit Host-Header und /xhr/health-check
- docker-compose.prod.yaml: name-Feld entfernt (Projekt-Name per -p)
- deploy.env.example als Vorlage hinzugefuegt
- .vibe/skills/client-build/SKILL.md aus Git-Index entfernt (gitignore)
- Deployment-Anleitung: Zugangsdaten, deploy.env, Image-Promotion,
  prod ohne eigenes Repo, git-clone-Hinweis, Log-Befehle mit -p
- Deploy-lokal-testen.md: neue Anleitung fuer lokales Testen

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 17:10:18 +02:00

16 KiB

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 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.

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).

Git-Zugangsdaten konfigurieren

Das git clone und git fetch in den Deploy-Scripts nutzen die Git-Credentials, die auf dem Deploy-Server konfiguriert sind. Die Zugangsdaten stehen nicht in den Scripts oder Konfigurationsdateien.

Drei Moeglichkeiten:

Methode Einrichtung Wo hinterlegt
SSH-Key git clone git@git.everdc.eu:.../kbcrm (SSH-URL verwenden) ~/.ssh/id_rsa auf dem Deploy-Server
Git-Credential-Store git config --global credential.helper store ~/.git-credentials auf dem Deploy-Server
Deploy-Token (Gitea) Token in Remote-URL: https://token:secret@git.everdc.eu/... In der origin-URL des geklonten Repos

Empfohlen: SSH-Key oder Deploy-Token. Der Credential-Store speichert Passwoerter im Klartext und sollte nur fuer Test-Umgebungen verwendet werden.


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 (einziges Repo auf dem Server)
│   └── .env              ← test-prod Umgebung (eigene Ports, eigene DB)
├── prod/
│   └── .env              ← prod Umgebung (kein eigenes Repo — Images per docker tag)
└── scripts/
    ├── deploy-kbcrm-test.sh   ← aus dem Repo (scripts/)
    ├── deploy-kbcrm-prod.sh   ← aus dem Repo (scripts/)
    └── deploy.env             ← Server-spezifische Konfiguration (nicht im Repo)

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

sudo mkdir -p /opt/kbcrm/test-prod /opt/kbcrm/prod

# Repository klonen (nur einmal — prod nutzt die Images aus test-prod)
# WICHTIG: git clone wird nur beim ersten Mal ausgefuehrt. Wenn das Verzeichnis
# bereits existiert (z.B. .env wurde bereits abgelegt), schlaegt clone fehl.
# Weitere Updates werden durch das deploy-kbcrm-test.sh Script via
# git fetch + git reset --hard durchgefuehrt — kein manueller clone mehr.
sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/test-prod/repo

# Falls das Verzeichnis bereits existiert und aktualisiert werden soll:
# cd /opt/kbcrm/test-prod/repo && git fetch origin main && git reset --hard origin/main
# Oder einfach: /opt/kbcrm/scripts/deploy-kbcrm-test.sh

4.2. .env-Dateien anlegen

Kopieren Sie die Vorlage aus dem Repo und passen Sie die Werte an:

# Test-Prod
cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/test-prod/.env

# Prod (Vorlage aus test-prod-Repo)
cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/prod/.env

4.3. .env fuer Test-Prod anpassen

vi /opt/kbcrm/test-prod/.env

Folgende Einstellungen muessen angepasst werden:

# 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).
# 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

# 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=<test-client-id>
ZITADEL_CLIENT_SECRET=<test-client-secret>
ZITADEL_REDIRECT_URI=https://test.kbp_crm.mylocal.de/auth/callback

4.4. .env fuer Prod anpassen

vi /opt/kbcrm/prod/.env
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
# 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

ZITADEL_REDIRECT_URI=https://kbp_crm.mylocal.de/auth/callback

4.5. Datenbank-Verzeichnisse erstellen

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:

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:

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/test-prod/repo/scripts/deploy-kbcrm-prod.sh /opt/kbcrm/scripts/
sudo chmod +x /opt/kbcrm/scripts/*.sh

4.8. deploy.env anlegen (Server-spezifische Konfiguration)

Die Deploy-Scripts laden ihre Server-spezifische Konfiguration (Pfade, Ports, Projekt-Namen) aus einer Datei deploy.env, die im gleichen Verzeichnis wie die Scripts liegen muss. Diese Datei ist nicht Teil des Repos und wird nicht per git pull ueberschrieben.

Kopieren Sie die Vorlage und passen Sie die Werte an:

cp /opt/kbcrm/test-prod/repo/scripts/deploy.env.example /opt/kbcrm/scripts/deploy.env
vi /opt/kbcrm/scripts/deploy.env

Inhalt der deploy.env:

# --- Test-Prod Umgebung (fuer deploy-kbcrm-test.sh) ---
DEPLOY_REPO_DIR=/opt/kbcrm/test-prod/repo
DEPLOY_ENV_FILE=/opt/kbcrm/test-prod/.env
DEPLOY_PROJECT_NAME=kbptest
DEPLOY_HEALTH_PORT=8443

# --- Prod Umgebung (fuer deploy-kbcrm-prod.sh) ---
DEPLOY_PROD_COMPOSE_FILE=/opt/kbcrm/test-prod/repo/docker-compose.prod.yaml
DEPLOY_PROD_ENV_FILE=/opt/kbcrm/prod/.env
DEPLOY_PROD_PROJECT_NAME=kbpprod
DEPLOY_PROD_TEST_PROJECT_NAME=kbptest
DEPLOY_PROD_HEALTH_PORT=443

Wenn deploy.env fehlt, verwenden die Scripts ihre eingebauten Defaults (die den obigen Werten entsprechen). Die deploy.env ist nur noetig, wenn Ihre Verzeichnisstruktur oder Ports abweichen.


5. Git-Hook einrichten (automatisches Test-Deploy)

Variante A: post-receive Hook (direkt auf dem Git-Server)

Wenn Shell-Zugang zum Git-Server besteht:

# Auf dem Git-Server, im bare Repository:
vi /srv/git/kbcrm.git/hooks/post-receive
#!/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
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):

# /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:

# /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
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:

/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 (Image-Promotion)

Nach Verifikation auf dem Test-Server:

/opt/kbcrm/scripts/deploy-kbcrm-prod.sh

Das Script fragt eine Bestaetigung ("yes/no") und uebernimmt dann die getesteten Images aus test-prod per docker tag — kein Rebuild, kein git pull. Prod startet mit exakt dem Stand, der in test-prod verifiziert wurde. Nur die .env unterscheidet sich (Ports, Zertifikate, DB-Pfad, OIDC).

Ablauf:

  1. Sicherheitsabfrage ("yes/no")
  2. docker tag — Images von kbptest-* nach kbpprod-* kopieren
  3. docker compose up -d — Container starten (ohne --build)
  4. Health-Check

Wichtig: Das Prod-Script fuehrt kein git pull aus. Eine Aenderung an der docker-compose.prod.yaml zwischen test-prod und Promotion wuerde bedeuten, dass prod mit einer anderen Konfiguration startet als verifiziert. Bei Aenderungen an der Compose-Datei muss erst ein neues test-prod-Deploy durchgefuehrt werden.

6.3. Alternative: Git-Tag-basierte Promotion

Statt das Prod-Script manuell aufzurufen, kann ein Git-Tag das Prod-Deploy ausloesen:

# 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:

#!/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

# Test-Prod
docker compose -p kbptest --env-file /opt/kbcrm/test-prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs

# Prod
docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs

# Nur Python-Container
docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs pythonapp

# Nur NGINX-Container
docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs nginx-proxy

Container-Status pruefen

docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml ps

Manuell in den Container wechseln

docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
    -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml exec pythonapp bash

Rollback

Bei Problemen kann auf einen frueheren Stand zurueckgekehrt werden:

# Auf dem Deploy-Server:
cd /opt/kbcrm/test-prod/repo
git log --oneline -10          # frueheren Commit finden
git checkout <commit-hash>
docker compose -p kbpprod --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:

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:

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). 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)
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 (s. BUILD_ENVIRONMENT)

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
  • deploy.env angelegt und angepasst (Pfade, Ports, Projekt-Namen)
  • 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