# 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 ```bash 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: ```bash # 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 ```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). # 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= 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 # 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 ```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/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: ```bash 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`: ```bash # --- 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: ```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 (Image-Promotion) Nach Verifikation auf dem Test-Server: ```bash /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: ```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 -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 ```bash 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 ```bash 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: ```bash # Auf dem Deploy-Server: cd /opt/kbcrm/test-prod/repo git log --oneline -10 # frueheren Commit finden git checkout 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: ```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). 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`