379 lines
18 KiB
Markdown
379 lines
18 KiB
Markdown
### **Anforderungsdokument (Version 3): Docker-Migration nach Ubuntu VM (`docker1`)**
|
||
|
||
**Zweck:** Verbindliche Netzwerk-Anforderungen und schrittweiser Migrationsplan für den Umzug des GTM-Engine Stacks (ca. 20 Container). Basierend auf den Stabilisierungs-Erkenntnissen vom 07.03.2026.
|
||
|
||
#### **Teil 1: Externe Port-Freigaben (Firewall)**
|
||
|
||
Diese Ports müssen auf der Firewall für den eingehenden Verkehr zur VM `10.10.81.2` geöffnet werden.
|
||
|
||
| Host-Port | Ziel-Dienst | Zugriff | Zweck |
|
||
| :--- | :--- | :--- | :--- |
|
||
| **8090** | `gateway_proxy` | Intranet | **Zentraler Zugang.** Alle Web-Apps (Dashboard, CE, Lead). |
|
||
| **3000** | `gitea` | Intranet | Gitea Web UI. |
|
||
| **2222** | `gitea` | Intranet | Gitea Git via SSH. |
|
||
| **8003** | `connector-so` | **Public** | SuperOffice Webhook-Empfänger (SSL erforderlich!). |
|
||
| **5678** | `n8n` | **Public** | Automation Workhooks. |
|
||
| **8094** | `gtm-architect`| Intranet | GTM Architect Direct. |
|
||
| **8092** | `b2b-marketing`| Intranet | B2B Marketing Assistant Direct. |
|
||
| **8001** | `transcription`| Intranet | Transcription Tool Direct (via 8090). |
|
||
|
||
---
|
||
|
||
### **Port-Mapping (docker-compose.yml)**
|
||
Die folgende Tabelle listet alle Host-Ports auf, die durch die `docker-compose.yml` gebunden werden. Diese sind die Ports, die auf `docker1` lauschen werden.
|
||
|
||
| Host-Port | Ziel-Dienst | Zweck / Notiz |
|
||
| :--- | :--- | :--- |
|
||
| **8090** | `nginx` (Gateway) | Zentraler Zugang (Intranet) |
|
||
| **8000** | `company-explorer` | API (intern) |
|
||
| **8003** | `connector-superoffice`| Webhook (Public) |
|
||
| **8501** | `lead-engine` | UI (intern) |
|
||
| **8004** | `lead-engine` | API (intern) |
|
||
| **8092** | `b2b-marketing-assistant`| UI/API (Intranet) |
|
||
| **8093** | `content-engine` | UI/API (Intranet) |
|
||
| **8094** | `gtm-architect` | UI/API (Intranet) |
|
||
| **8096** | `heatmap-frontend` | UI (Intranet) |
|
||
| **8002** | `heatmap-backend` | API (intern) |
|
||
| **8097** | `competitor-analysis` | UI/API (Intranet) |
|
||
| **8098** | `market-intelligence` | UI/API (Intranet) |
|
||
| **8001** | `transcription-tool` | UI/API (Intranet) |
|
||
|
||
---
|
||
|
||
#### **Teil 2: Netzwerk-Architektur (Intern)**
|
||
|
||
* **DNS Resolver:** In Nginx konfiguriert (`resolver 127.0.0.11`).
|
||
* **WebSockets:** Das Gateway unterstützt `Upgrade`-Header (kritisch für Streamlit/Lead-Engine).
|
||
* **Echo-Prävention:** Der Connector (`worker.py`) identifiziert sich dynamisch. Keine manuellen ID-Einträge in `.env` nötig, solange `SO_CLIENT_ID` passt.
|
||
* **Routing:**
|
||
* `/ce/` -> `company-explorer:8000`
|
||
* `/lead/` -> `lead-engine:8501` (UI)
|
||
* `/feedback/` -> `lead-engine:8004` (API)
|
||
* `/gtm/` -> `gtm-architect:3005` (API/Frontend)
|
||
* `/b2b/` -> `b2b-marketing-assistant:3002` (API/Frontend)
|
||
* `/content/` -> `content-engine:3000` (API/Frontend)
|
||
* `/market/` -> `market-intelligence:3001` (API/Frontend)
|
||
* `/competitor/` -> `competitor-analysis:3000` (API/Frontend)
|
||
* `/heatmap/` -> `heatmap-frontend:80` (Static/Proxy)
|
||
* `/tr/` -> `transcription-tool:8001` (API/Frontend) -> **Achtung:** Benötigt expliziten `rewrite` in Nginx!
|
||
|
||
---
|
||
|
||
# ⚠️ Kritische Lehren (Update 08.03.2026)
|
||
|
||
Der Umzug muss zwingend diese Punkte beachten, um den "Totalausfall" zu vermeiden:
|
||
|
||
### 1. Datenbank-Schema & Volumes
|
||
**Regel:** Datenbanken werden **NIEMALS** mehr direkt auf einen Host-Pfad gemountet.
|
||
**Grund:** Permission-Errors und SQLite-Locks auf Netzwerk-Dateisystemen.
|
||
**Vorgehen:** Nutzung von benannten Volumes (`explorer_db_data`, `connector_db_data`, `lead_engine_data`, `gtm_architect_data`, `b2b_marketing_data`, `content_engine_data`, `market_intel_data`, `competitor_analysis_data`, `transcription_uploads`).
|
||
|
||
### 2. Lead Engine: Kalender-Logik (v1.4)
|
||
* **Raster:** Das System bietet Termine nur im **15-Minuten-Takt** (:00, :15, :30, :45) an.
|
||
* **Abstand:** Zwischen zwei Terminvorschlägen liegen mind. **3 Stunden** Pause.
|
||
* **AppOnly Workaround:** Termin wird im Kalender von `info@robo-planet.de` erstellt und der Mitarbeiter (`e.melcer@`) als Teilnehmer hinzugefügt.
|
||
|
||
### 3. GTM Architect & B2B Assistant: Standalone-Betrieb
|
||
* **Architektur:** Beide Apps nutzen das "Self-Contained Image" Muster. Code, Frontend-Builds (`dist/`) und `node_modules` sind fest im Image verbaut.
|
||
* **GTM Port:** 3005 intern.
|
||
* **B2B Port:** 3002 intern.
|
||
* **DB-Abhängigkeit:** Der B2B Assistant benötigt zwingend die Datei `market_db_manager.py` (wird beim Build aus dem Root kopiert).
|
||
|
||
### 4. Transcription Tool: FFmpeg & Routing
|
||
* **FFmpeg:** Muss im Image vorhanden sein (Build dauert ca. 15 Min auf Synology).
|
||
* **Pfade:** Das Tool benötigt eine `tsconfig.json` im `frontend/` Ordner für den TypeScript-Build.
|
||
* **Nginx:** Der Pfad `/tr/` muss explizit umgeschrieben werden: `rewrite ^/tr/(.*) /$1 break;`.
|
||
|
||
---
|
||
|
||
### 📂 Docker Volume Migration (Der "Plug & Play" Weg)
|
||
|
||
Um die Daten (Companies, Leads, Projekte, Audio-Files) ohne Verluste umzuziehen, müssen die benannten Volumes gesichert werden.
|
||
|
||
**Auf der Synology (Quelle):**
|
||
```bash
|
||
# Backup aller kritischen Volumes in ein Archiv
|
||
docker run --rm -v explorer_db_data:/data -v $(pwd):/backup alpine tar czf /backup/explorer_data.tar.gz -C /data .
|
||
docker run --rm -v lead_engine_data:/data -v $(pwd):/backup alpine tar czf /backup/lead_data.tar.gz -C /data .
|
||
docker run --rm -v gtm_architect_data:/data -v $(pwd):/backup alpine tar czf /backup/gtm_data.tar.gz -C /data .
|
||
docker run --rm -v b2b_marketing_data:/data -v $(pwd):/backup alpine tar czf /backup/b2b_data.tar.gz -C /data .
|
||
docker run --rm -v content_engine_data:/data -v $(pwd):/backup alpine tar czf /backup/content_data.tar.gz -C /data .
|
||
docker run --rm -v market_intel_data:/data -v $(pwd):/backup alpine tar czf /backup/market_data.tar.gz -C /data .
|
||
docker run --rm -v competitor_analysis_data:/data -v $(pwd):/backup alpine tar czf /backup/competitor_data.tar.gz -C /data .
|
||
docker run --rm -v transcription_uploads:/data -v $(pwd):/backup alpine tar czf /backup/tr_uploads.tar.gz -C /data .
|
||
```
|
||
|
||
|
||
|
||
**Auf der Ubuntu VM (Ziel):**
|
||
1. Volumes anlegen: `docker volume create explorer_db_data` (etc.)
|
||
2. Daten wiederherstellen:
|
||
```bash
|
||
docker run --rm -v explorer_db_data:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/explorer_data.tar.gz"
|
||
```
|
||
|
||
---
|
||
|
||
### **Verbindlicher Migrationsplan**
|
||
|
||
**Phase 1: Vorbereitung**
|
||
1. [ ] **`git push`** auf Synology (aktuellster Stand inkl. GTM-Integration).
|
||
2. [ ] **`.env` Datei sichern** (Vollständigkeit prüfen!).
|
||
3. [ ] **Volumes sichern** (siehe oben: `tar.gz` Erstellung).
|
||
|
||
**Phase 2: Deployment auf `docker1`**
|
||
1. [ ] Repo klonen: `git clone ... /opt/gtm-engine`
|
||
2. [ ] `.env` kopieren.
|
||
3. [ ] **Volumes restoren** (BEVOR `docker compose up` ausgeführt wird).
|
||
4. [ ] Starten: `docker compose up -d --build`
|
||
5. [ ] **Schema-Check:** `docker exec -it company-explorer python /app/fix_missing_columns.py`
|
||
|
||
**Phase 3: Verifizierung**
|
||
1. [ ] Check Kalender-Lesen: `docker exec lead-engine python /app/trading_twins/test_calendar_logic.py`
|
||
2. [ ] Check GTM Architect: `https://10.10.81.2:8090/gtm/`
|
||
|
||
---
|
||
|
||
### **Anhang A: Post-Deployment Port Check**
|
||
Führen Sie dieses Skript auf einem Client-Rechner aus (nicht auf `docker1` selbst), um die Erreichbarkeit der freigegebenen Ports zu testen. Ersetzen Sie `TARGET_IP` durch `10.10.81.2`.
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
TARGET_IP="10.10.81.2"
|
||
PORTS_INTRANET=(8090 3000 2222 8092 8093 8094 8096 8097 8098 8001)
|
||
PORTS_PUBLIC=(8003 5678)
|
||
|
||
echo "--- Testing Intranet Ports on $TARGET_IP ---"
|
||
for port in "${PORTS_INTRANET[@]}"; do
|
||
if nc -z -w 2 $TARGET_IP $port; then
|
||
echo "✅ Port $port is OPEN"
|
||
else
|
||
echo "❌ Port $port is CLOSED"
|
||
fi
|
||
done
|
||
|
||
|
||
|
||
echo -e "\n--- Testing Public Ports on $TARGET_IP (von extern prüfen!) ---"
|
||
|
||
for port in "${PORTS_PUBLIC[@]}"; do
|
||
|
||
echo " - Bitte prüfen Sie Port $port manuell von außerhalb des Netzwerks."
|
||
|
||
done
|
||
|
||
```
|
||
|
||
|
||
|
||
---
|
||
|
||
|
||
|
||
### **Teil 4: Entwicklungs- vs. Produktions-Workflow**
|
||
|
||
|
||
|
||
Dieses Kapitel definiert die verbindlichen Regeln für die Weiterentwicklung der GTM-Engine nach der Migration, um maximale Stabilität und Sicherheit des Produktivsystems zu gewährleisten.
|
||
|
||
|
||
|
||
#### **1. Grundprinzipien: Getrennte Welten**
|
||
|
||
|
||
|
||
* **Regel 1: Keine Entwicklung in der Produktion.** Auf der Wackler-VM (`docker1`) wird niemals Code direkt bearbeitet, experimentiert oder getestet. Sie ist ausschließlich für den Betrieb der stabilen Softwareversion vorgesehen.
|
||
|
||
* **Regel 2: Getrennte Konfiguration.** Jede Umgebung hat ihre eigene Konfigurationsdatei.
|
||
|
||
* **Produktion (Wackler):** `.env` – Enthält alle echten API-Schlüssel und Endpunkte.
|
||
|
||
* **Entwicklung (Synology):** `.env.dev` (oder eine Kopie der `.env`) – Verwendet Test-Schlüssel, Sandbox-Endpunkte und Dummy-Werte.
|
||
|
||
* **Regel 3: Die Produktionsdatenbank ist heilig.** Die produktiven Docker-Volumes (`explorer_db_data` etc.) werden niemals für Entwicklungszwecke angetastet. Lokale Entwicklung findet gegen eine separate, lokale Test-Datenbank statt.
|
||
|
||
|
||
|
||
#### **2. Umgang mit Live-Daten & Aktionen**
|
||
|
||
|
||
|
||
**a) SuperOffice Webhook**
|
||
|
||
|
||
|
||
* **Problem:** Es darf nur einen aktiven Webhook geben, um Dateninkonsistenzen zu vermeiden.
|
||
|
||
* **Lösung:**
|
||
|
||
1. **Produktion:** Der Webhook (`https://<neue-wackler-domain>/connector/webhook`) wird bei SuperOffice registriert. In der `.env` ist das `WEBHOOK_SECRET_TOKEN` gesetzt.
|
||
|
||
2. **Entwicklung:** In der `.env.dev` wird das `WEBHOOK_SECRET_TOKEN` auskommentiert (`#WEBHOOK_SECRET_TOKEN=...`). Der Connector ist somit "taub" für externe Anrufe.
|
||
|
||
3. **Testen in der Entwicklung:** Um realistische Tests durchzuführen, wird eine Umgebungsvariable `LOG_WEBHOOKS=true` im **produktiv-Connector** gesetzt. Dieser schreibt dann den Body jedes eingehenden Webhooks in die Docker-Logs. Diese JSON-Payloads können kopiert werden, um mit einem Skript (`curl`) gezielte, manuelle Tests gegen die lokale Entwicklungsumgebung zu fahren.
|
||
|
||
|
||
|
||
**b) E-Mail-Versand (Lead Engine & Co.)**
|
||
|
||
|
||
|
||
* **Problem:** Das Entwicklungssystem darf unter keinen Umständen E-Mails an echte Kunden oder Kontakte senden.
|
||
|
||
* **Lösung: Der "Safety Override"**
|
||
|
||
1. **Produktion:** Die echten MS Graph API Credentials (`INFO_...`) sind in der `.env` gesetzt.
|
||
|
||
2. **Entwicklung:** Die MS Graph Credentials in der `.env.dev` sind auskommentiert oder mit Dummy-Werten belegt.
|
||
|
||
3. **Sicherer Test-Modus:** Eine neue Umgebungsvariable `DEV_MODE_EMAIL_RECIPIENT` wird implementiert.
|
||
|
||
* Wenn diese Variable in der `.env.dev` gesetzt ist (z.B. `DEV_MODE_EMAIL_RECIPIENT=ihre.test@adresse.de`), leitet der Code **alle** ausgehenden E-Mails an diese eine Adresse um.
|
||
|
||
* **Test-Prozess:** Für einen E-Mail-Test werden in der `.env.dev` temporär die echten MS Graph Credentials eingetragen, `DEV_MODE_EMAIL_RECIPIENT` gesetzt, der Test durchgeführt und danach werden die Credentials **sofort wieder entfernt/auskommentiert**.
|
||
|
||
|
||
|
||
#### **3. Deployment-Prozess: Von der Entwicklung zur Produktion**
|
||
|
||
|
||
|
||
Der Prozess, um neuen, getesteten Code sicher auf das Produktivsystem zu bringen, ist wie folgt:
|
||
|
||
|
||
|
||
1. **Entwicklung (Synology):**
|
||
|
||
* Ein neues Feature wird entwickelt und lokal gegen die Test-Datenbank getestet.
|
||
|
||
* Änderungen werden committet und in das **lokale Gitea auf der Synology** gepusht (`git push`).
|
||
|
||
|
||
|
||
2. **Deployment (Wackler VM):**
|
||
|
||
* Verbindung zur Wackler-VM via SSH.
|
||
|
||
* Ins Projektverzeichnis navigieren (`/opt/gtm-engine`).
|
||
|
||
* Den neuesten stabilen Code vom Entwicklungs-Gitea holen: `git pull synology main`.
|
||
|
||
* Die Docker-Container mit dem neuen Code neu bauen und starten: `docker compose up -d --build`.
|
||
|
||
|
||
|
||
3. **Netzwerkanforderung für Deployment:**
|
||
|
||
* Für den `git pull`-Befehl muss die Wackler-VM (`10.10.81.2`) auf den **Gitea-Port (TCP 3000) der Synology-Diskstation** zugreifen können. Dies muss einmalig im Netzwerk konfiguriert werden.
|
||
|
||
---
|
||
|
||
### **Teil 5: Alternative - Single-Host-Setup (Dev & Prod auf einer VM)**
|
||
|
||
Dieses Szenario beschreibt den wahrscheinlicheren Fall, dass die gesamte Arbeit (Entwicklung und Produktion) auf der von der IT bereitgestellten VM (`docker1`) stattfinden muss. Der Ansatz wandelt sich von "physischer Trennung" zu **"logischer Trennung mit starker Isolation"**.
|
||
|
||
#### **1. Grundkonzept: Schalldichte Räume**
|
||
|
||
Auf der VM werden zwei komplett isolierte Umgebungen parallel betrieben. Docker ist für dieses Szenario optimiert und gewährleistet die Trennung von Code, Konfiguration, Ports und vor allem Daten.
|
||
|
||
* **Produktionsumgebung (`prod`):** Läuft stabil mit dem geprüften Code und greift auf die produktiven Daten zu.
|
||
* **Entwicklungsumgebung (`dev`):** Dient zum Entwickeln und Testen. Hat eine eigene Konfiguration und eine komplett separate Test-Datenbank.
|
||
|
||
#### **2. Implementierung**
|
||
|
||
**a) Verzeichnisstruktur**
|
||
|
||
Eine saubere Ordnerstruktur ist die Basis für die Trennung.
|
||
|
||
```
|
||
/opt/gtm-engine/
|
||
├── dev/
|
||
│ ├── docker-compose.yml
|
||
│ ├── .env
|
||
│ └── (kompletter Code des Projekts...)
|
||
│
|
||
└── prod/
|
||
├── docker-compose.yml
|
||
├── .env
|
||
└── (kompletter Code des Projekts...)
|
||
```
|
||
|
||
**b) Auflösung von Port-Konflikten**
|
||
|
||
Zwei Stacks können nicht dieselben Ports nutzen. Die `dev`-Umgebung nutzt daher verschobene Ports.
|
||
|
||
| Dienst | Produktions-Port (in `prod/docker-compose.yml`) | Entwicklungs-Port (in `dev/docker-compose.yml`) |
|
||
| :--- | :---: | :---: |
|
||
| Gateway | `8090:80` | `9090:80` |
|
||
| Webhook | `8003:8003` | `9003:8003` |
|
||
| GTM | `8094:3005` | `9094:3005` |
|
||
|
||
So ist das **Produktivsystem** unter `http://10.10.81.2:8090` und das **Entwicklungssystem** unter `http://10.10.81.2:9090` erreichbar.
|
||
|
||
**c) Garantierte Datenbank-Isolation**
|
||
|
||
**Dies ist der wichtigste Punkt:** Docker stellt sicher, dass die Datenbanken niemals vermischt werden können. Docker Compose erstellt benannte Volumes mit dem Verzeichnisnamen als Präfix.
|
||
|
||
* `docker compose up` im Ordner `prod/` erzeugt das Volume `prod_explorer_db_data`.
|
||
* `docker compose up` im Ordner `dev/` erzeugt das Volume `dev_explorer_db_data`.
|
||
|
||
Diese beiden Volumes sind **vollständig voneinander isolierte Container** auf der Festplatte. Ein Zugriff von `dev` auf `prod`-Daten ist technisch unmöglich. **Ihre Anforderung, dass die Produktionsdatenbank sicher ist, wird hiermit zu 100% erfüllt.**
|
||
|
||
#### **3. Vereinfachter Workflow auf einer Maschine**
|
||
|
||
Das neue Gitea (auf Port 3000) wird zur zentralen "Source of Truth".
|
||
|
||
1. **Entwicklung:**
|
||
* Sie arbeiten im Verzeichnis `/opt/gtm-engine/dev/`.
|
||
* Sie ändern den Code und testen ihn, indem Sie den `dev`-Stack starten:
|
||
`cd /opt/gtm-engine/dev/ && docker compose up -d --build`
|
||
* Sie verifizieren die Änderungen im Browser unter `http://10.10.81.2:9090`.
|
||
* Wenn alles passt, committen Sie und pushen zum Gitea auf derselben Maschine: `git push`.
|
||
|
||
2. **Deployment (Release in die Produktion):**
|
||
* Wechseln Sie in das Produktionsverzeichnis: `cd /opt/gtm-engine/prod/`.
|
||
* Holen Sie den neuen, im `dev`-Zweig getesteten Code aus Gitea: `git pull`.
|
||
* Aktualisieren Sie den Produktions-Stack: `docker compose up -d --build`.
|
||
|
||
Dieser Prozess ist sicher, schnell und wiederholbar, da er nur aus Standard-Git- und Docker-Befehlen besteht.
|
||
|
||
---
|
||
|
||
### **UMZUG Live - Lessons Learned (März 2026)**
|
||
|
||
Der reale Umzug von der Synology auf die Wackler-VM (`docker1`) hat gezeigt, dass die Theorie gut war, die Praxis aber spezifische Hürden bereithielt. Diese Lektionen sind kritisch für alle zukünftigen Deployments:
|
||
|
||
#### **1. Die Git-Historien-Falle (Packfile / Timeout Error)**
|
||
* **Problem:** Versuche, das Repository per `git clone` oder über die Gitea-Migrations-Funktion umzuziehen, brachen reproduzierbar mit `RPC failed; HTTP 504` oder `fatal: expected 'packfile'` ab.
|
||
* **Ursache:** Das lokale `.git` Verzeichnis war auf **1,4 Gigabyte** angewachsen, da in der Vergangenheit versehentlich große MP3-Dateien committet wurden. Auch nach dem Löschen verbleiben diese in der Historie und blockieren den Transfer über Proxies.
|
||
* **Lösung:** Radikale Bereinigung der Historie via `git filter-branch` (oder `git filter-repo`) für den Ordner `uploads_audio/`, gefolgt von einer aggressiven Garbage Collection (`git gc --prune=now --aggressive`).
|
||
* **Ergebnis:** Schrumpfung des Repos von 1,4 GB auf **130 MB**.
|
||
* **Lesson:** Bei Git-Transfer-Fehlern zuerst die Größe des `.git` Ordners prüfen.
|
||
|
||
#### **2. Der "Filter-Branch" Nebeneffekt (.env Schutz)**
|
||
* **Problem:** Nach der Git-Bereinigung schienen ungetrackte Dateien (wie `.env` und `volume_backups/`) plötzlich verschwunden.
|
||
* **Ursache:** `git filter-branch` erzwingt einen harten Reset auf den bereinigten Stand. Dateien, die in der `.gitignore` stehen, werden dabei oft aus dem Arbeitsverzeichnis entfernt.
|
||
* **Lösung:** Wiederherstellung aus einem zuvor erstellten `.tar.gz`-Backup des gesamten Projektverzeichnisses.
|
||
* **Lesson:** Führe niemals tiefe Git-Eingriffe ohne ein physisches Dateibackup der ungetrackten Dateien durch.
|
||
|
||
#### **3. Das "Transit-Repo" Muster für Secrets**
|
||
* **Problem:** Sicherer Transfer der `.env` und Datenbanken ohne Einchecken in das Haupt-Repo und ohne Zugriff auf externe Transfer-Dienste (Firewall-Blockade).
|
||
* **Lösung:** Erstellung eines temporären, privaten Gitea-Repositorys namens `transit` auf dem Ziel-System. Hochladen des verschlüsselten Backups über die Gitea-Weboberfläche und anschließendes Klonen/Entpacken auf der VM. Danach sofortige Löschung des `transit` Repos.
|
||
* **Lesson:** Gitea ist das beste Transit-Medium für große Files hinter restriktiven Firewalls.
|
||
|
||
#### **4. Docker Build Context & Berechtigungen**
|
||
* **Problem:** `docker compose up --build` brach ab mit: `failed to solve: error from sender: open /volume_backups: permission denied`.
|
||
* **Ursache:** Docker versucht beim Bauen das gesamte Verzeichnis einzulesen. Ordner, die dem `root` User gehören (z.B. durch Gemini-Aktionen erstellt), blockieren den Prozess.
|
||
* **Lösung:**
|
||
1. Berechtigungen korrigieren: `sudo chown -R $USER:$USER volume_backups/`
|
||
2. Ordner in der `.dockerignore` ausschließen, damit er gar nicht erst eingelesen wird.
|
||
* **Lesson:** Alles, was nicht in ein Container-Image gehört, **muss** in die `.dockerignore`.
|
||
|
||
#### **5. Vite & TypeScript Build-Tücken**
|
||
* **Problem:** Der Frontend-Build schlug fehl, weil die `tsconfig.json` im Container nicht gefunden wurde.
|
||
* **Ursache:** `COPY . .` verhält sich in komplexen Verzeichnisstrukturen manchmal unvorhersehbar.
|
||
* **Lösung:** Explizites Kopieren aller Konfigurationsdateien im Dockerfile: `COPY tsconfig.json tsconfig.node.json tsconfig.app.json vite.config.ts ./` direkt vor dem Build-Schritt.
|
||
* **Lesson:** Verlasse dich beim Build nicht auf Wildcards; kopiere essenzielle Config-Files explizit.
|
||
|
||
|