Merge feature/auto-deploy: automatisches Deployment und Start-Meldungen

This commit is contained in:
gg committed 2026-09-13 20:08:24 +02:00
commit bde45871f6
16 files changed
+637 -109

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
.git/
Client/node_modules/
Client/tsout/
App_Server/data/
App_Server/env/
oidc-test/
*.pyc
__pycache__/
+22 -23
View File
@@ -1,7 +1,8 @@
ARG PYTHON_VERSION # ── Stage: prod ──────────────────────────────────────────────────
ARG NODE_VERSION # Build-Context ist das Repo-Root (.), daher muessen alle COPY-Pfade
ARG NPM_VERSION # 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 ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
RUN apt-get clean && apt-get update && apt-get install -y locales RUN apt-get clean && apt-get update && apt-get install -y locales
RUN echo "de_DE.UTF-8 UTF-8" >> /etc/locale.gen RUN echo "de_DE.UTF-8 UTF-8" >> /etc/locale.gen
@@ -12,18 +13,21 @@ RUN locale-gen
RUN mkdir -p /usr/opt/pythonapp/App_Server/root /usr/opt/pythonapp/env /usr/var/pythonappdata RUN mkdir -p /usr/opt/pythonapp/App_Server/root /usr/opt/pythonapp/env /usr/var/pythonappdata
WORKDIR /usr/opt/pythonapp WORKDIR /usr/opt/pythonapp
RUN python -m venv --upgrade-deps /usr/opt/pythonapp/env 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 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" ] # Python-Anwendungscode ins Image kopieren (inkl. templates/index.html)
# ENTRYPOINT [ "sh", "/init.sh" ] COPY App_Server/root/ /usr/opt/pythonapp/App_Server/root/
# CMD []
# 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 FROM prod AS dev
ARG NODE_VERSION # Node.js-Version fest im Dockerfile verankert.
ARG NPM_VERSION
# --- 2. Systemabhängigkeiten (für Python + Node.js) --- # --- 2. Systemabhängigkeiten (für Python + Node.js) ---
RUN apt-get update && \ RUN apt-get update && \
@@ -41,19 +45,14 @@ RUN apt-get update && \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# --- 3. Node.js + npm installieren --- # --- 3. Node.js + npm installieren ---
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \
apt-get install -y nodejs && \ apt-get install -y nodejs && \
npm install -g npm@${NPM_VERSION} npm install -g npm@latest
# --- 8. Arbeitsverzeichnis für TypeScript --- # --- 4. Dev-Start-Script kopieren (fuer Konsistenz, wird sonst per bind-mount bereitgestellt) ---
# In dem Entwicklungsverzeichnis (PYTHON_DEV_DIR) existiert dies schon als Unterverzeichnis "Client", daher wird es hier nicht erstellt 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"] 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"]
+3 -3
View File
@@ -4,7 +4,7 @@ from settings import Configuration
from db import dbauth from db import dbauth
from db import dbapp from db import dbapp
from auth.model.checkdb import checkupdates as checkupdatesauth from auth.model.checkdb import checkupdates as checkupdatesauth
from os import path import os
login_manager = LoginManager() login_manager = LoginManager()
@@ -12,7 +12,7 @@ login_manager = LoginManager()
def init_app(): def init_app():
app = Flask(__name__) app = Flask(__name__)
config: Configuration = Configuration("root/config.ini") config: Configuration = Configuration("/usr/opt/pythonapp/App_Server/root/config.ini")
_projektname = config.app_projectname _projektname = config.app_projectname
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////usr/var/pythonappdb/{}.db".format(_projektname) 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", "auth": "sqlite:////usr/var/pythonappdb/auth.db",
"appdb": "sqlite:////usr/var/pythonappdb/{}.db".format(_projektname) "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["PROJEKTNAME"] = config.app_projectname
app.config["APPID"] = config.app_appid app.config["APPID"] = config.app_appid
app.config["APPVERSION"] = config.clientapp_version app.config["APPVERSION"] = config.clientapp_version
+3
View File
@@ -8,12 +8,15 @@ def run_server():
if app.config["DEBUG"] is False: if app.config["DEBUG"] is False:
_server = "waitress" _server = "waitress"
# serve(app, listen='nginx-proxy:8080') # Funktioniert aus unbekannten Gründen nicht. Der Hostname wird im Docker Container nicht korrekt aufgelöst # 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') serve(app, listen='*:8080')
else: else:
_server = "werkzeug" _server = "werkzeug"
print(f"Server startet mit Server type: {_server}", flush=True)
serving.run_simple("0.0.0.0", 8080, app, use_reloader=True) serving.run_simple("0.0.0.0", 8080, app, use_reloader=True)
except Exception as ex: except Exception as ex:
print(str(ex)) print(str(ex))
print(f"Server gestartet. Server type: {_server}", flush=True)
if __name__ == '__main__': if __name__ == '__main__':
app = init_app() app = init_app()
+6 -15
View File
@@ -1,16 +1,7 @@
# Python virtual Environment anlegen #!/bin/bash
cd /usr/opt/pythonapp # Startet den Python-Server im Prod-Modus.
# Wenn es die Datei pyvenv.cfg im Unterverzeichnis env nocht nicht gibt, muss das # pip install ist nicht mehr noetig — Abhaengigkeiten sind im Image.
# virtuelle Environment angelegt werden # Kein cd noetig — alle Pfade sind absolut.
# if [ ! -s env/pyvenv.cfg ] # Python-Imports funktionieren ueber sys.path[0] (Verzeichnis des Scripts).
# 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."
echo "Starting Python-Server..." echo "Starting Python-Server..."
env/bin/python root/runserver.py exec /usr/opt/pythonapp/env/bin/python /usr/opt/pythonapp/App_Server/root/runserver.py
+1 -1
View File
@@ -38,5 +38,5 @@ export function getIniValue(value){
return result["APP"][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) result = parseINIString(inifile)
+3
View File
@@ -1,4 +1,7 @@
{ {
"scripts": {
"build": "tsc && rollup -c rollup.config.mjs"
},
"dependencies": { "dependencies": {
"mithril": "^2.2.2" "mithril": "^2.2.2"
}, },
+432
View File
@@ -0,0 +1,432 @@
# 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`).
---
## 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).
# 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
```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/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 <commit-hash>
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). 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
- [ ] 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`
+25 -3
View File
@@ -1,6 +1,28 @@
ARG NGINX_VERSION # ── Stage: frontend — baut das TypeScript-JS-Bundle ──────────────
FROM nginx:${NGINX_VERSION} # Node.js, npm und node_modules landen NICHT im Prod-Image.
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
# 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:mainline-alpine
RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
RUN mkdir -p /etc/certs /etc/nginx/templates RUN mkdir -p /etc/certs /etc/nginx/templates
COPY 10-select-ssl.sh /docker-entrypoint.d/10-select-ssl.sh COPY NGINX_Server/10-select-ssl.sh /docker-entrypoint.d/10-select-ssl.sh
RUN chmod +x /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/
+9 -16
View File
@@ -2,15 +2,14 @@ name: kbpbase
services: services:
nginx-proxy: nginx-proxy:
build: build:
context: NGINX_Server context: . # Repo-Root (fuer frontend-Stage im NGINX-Dockerfile)
args: # Übergibt die ARGS an das dockerfile dockerfile: NGINX_Server/Dockerfile # explizit, da Context nicht mehr NGINX_Server/
NGINX_VERSION: ${NGINX_VERSION}
ports: ports:
- ${NGINX_HOST_PORT}:4430 - ${NGINX_HOST_PORT}:4430
volumes: volumes:
- ${NGINX_STATIC_DIR}:/usr/share/nginx/html:ro # NGINX_STATIC_DIR entfaellt in Prod — statische Dateien sind im NGINX-Image
- ${NGINX_CONFIG_TEMPLATE_DIR}:/etc/nginx/configtemplates:ro # 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 - ${NGINX_CERTS}:/etc/certs
environment: environment:
- NGINX_USE_SSL=${NGINX_USE_SSL:-true} - NGINX_USE_SSL=${NGINX_USE_SSL:-true}
@@ -19,15 +18,9 @@ services:
- NGINX_SERVER_NAME=${NGINX_SERVER_NAME} - NGINX_SERVER_NAME=${NGINX_SERVER_NAME}
pythonapp: pythonapp:
build: build:
context: App_Server context: . # Repo-Root (fuer COPY-Pfade im Dockerfile)
# target: kommt aus dem prod oder dev dockerfile dockerfile: App_Server/Dockerfile # explizit, da Context nicht mehr App_Server/
args: # Übergibt die ARGS an das dockerfile
PYTHON_VERSION: ${PYTHON_VERSION}
volumes: volumes:
#- ${PYTHON_APP_DIR}:/usr/opt/pythonapp/root
- ${PYTHON_DB_DIR}:/usr/var/pythonappdb - ${PYTHON_DB_DIR}:/usr/var/pythonappdb
- python-venv:/usr/opt/pythonapp/env # python-venv Volume entfaellt — venv ist im Image
# - ${PYTHON_ENV_DIR}:/usr/opt/pythonapp/env # (dev-Stage definiert eigenen Volume in docker-compose.dev.yaml)
volumes:
python-venv:
+15 -5
View File
@@ -6,17 +6,27 @@ services:
service: pythonapp service: pythonapp
build: build:
target: dev target: dev
args: # Übergibt die ARGS an das dockerfile
# PYTHON_VERSION: ${PYTHON_VERSION}
NODE_VERSION: ${NODE_VERSION:-18}
NPM_VERSION: ${NPM_VERSION:-8}
environment: environment:
# Globales Vibe-Home innerhalb des Workspace-Bind-Mounts, damit es auf dem Host persistiert # Globales Vibe-Home innerhalb des Workspace-Bind-Mounts, damit es auf dem Host persistiert
- VIBE_HOME=/usr/opt/pythonapp/.vibe-global - VIBE_HOME=/usr/opt/pythonapp/.vibe-global
# Debug-Modus aktivieren — Werkzeug-Reloader statt Waitress
- APP_DEBUG=true
volumes: volumes:
- ${PYTHON_DEV_DIR}:/usr/opt/pythonapp - ${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 - node-modules:/usr/opt/typescript/node_modules
nginx-proxy:
extends:
file: docker-compose.base.yaml
service: nginx-proxy
volumes: 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: node-modules:
+6 -2
View File
@@ -6,5 +6,9 @@ services:
service: pythonapp service: pythonapp
build: build:
target: prod target: prod
volumes: # Kein Code-bind-mount mehr — Code ist im Image
- ${PYTHON_APP_DIR}:/usr/opt/pythonapp nginx-proxy:
extends:
file: docker-compose.base.yaml
service: nginx-proxy
# Kein STATIC_DIR bind-mount mehr — statische Dateien sind im NGINX-Image
-27
View File
@@ -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
+24 -14
View File
@@ -1,34 +1,44 @@
# =============================================================================
# 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 # Parameter für NGINX-Server
NGINX_VERSION=mainline-alpine
# SSL für NGINX aktivieren (true) oder deaktivieren (false). # SSL für NGINX aktivieren (true) oder deaktivieren (false).
# Bei false muss NGINX_HOST_PORT angepasst werden (z.B. 8080 statt 443). # 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_USE_SSL=true
NGINX_HOST_PORT=443 NGINX_HOST_PORT=443
NGINX_SERVER_NAME=kbp_crm.mylocal.de 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_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_CONFIG_TEMPLATE_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/configtemplates
NGINX_CERTS=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/certs NGINX_CERTS=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/NGINX_Server/certs
NGINX_CERT=wildcard.mylocal.crt NGINX_CERT=wildcard.mylocal.crt
NGINX_CERT_KEY=wildcard.mylocal.key NGINX_CERT_KEY=wildcard.mylocal.key
# Parameter for Python App-Server # Parameter for Python App-Server
PYTHON_VERSION=slim-bookworm # PYTHON_APP_DIR entfaellt — Code wird in Prod ins Image gebaut (Option B).
PYTHON_APP_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/root # 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_DEV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm
PYTHON_DB_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/data PYTHON_DB_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/data
# PYTHON_ENV_DIR=/home/schoschi/Entwicklung/Projekte_Runden/kbcrm/App_Server/env # 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
# 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 # ZITADEL OIDC SETTINGS
+44
View File
@@ -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 ==="
+36
View File
@@ -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 ==="