build: optimize Docker setup for Caddy SPA and improve runtime stability
Updated Dockerfile to streamline the hybrid build process and optimize artifact integration. Modified Caddyfile for better routing logic, enhanced security headers, Prometheus metrics, and improved API proxy handling. Adjusted `dc-gui.yaml` for container stability and simplified runtime configuration for `apiBaseUrl`. Expanded documentation with troubleshooting and session logs.
This commit is contained in:
@@ -1,41 +1,69 @@
|
|||||||
:4000 {
|
{
|
||||||
# Root directory for static files
|
# Global Options
|
||||||
root * /usr/share/caddy
|
# Enable Prometheus metrics
|
||||||
|
servers {
|
||||||
# Enable Gzip/Zstd compression
|
metrics
|
||||||
encode gzip zstd
|
}
|
||||||
|
}
|
||||||
# Serve static files
|
|
||||||
file_server
|
:4000 {
|
||||||
|
# Root directory for static files
|
||||||
# Templates for runtime configuration (config.json)
|
root * /usr/share/caddy
|
||||||
templates {
|
|
||||||
mime application/json
|
# Access Logs (JSON format for Docker)
|
||||||
}
|
log {
|
||||||
|
output stdout
|
||||||
# SPA Routing: Fallback to index.html for non-existent files
|
format json
|
||||||
try_files {path} /index.html
|
}
|
||||||
|
|
||||||
# Cache Control for static assets (immutable)
|
# Enable Gzip/Zstd compression
|
||||||
@static {
|
encode gzip zstd
|
||||||
file
|
|
||||||
path *.js *.css *.png *.jpg *.svg *.wasm
|
# Templates for runtime configuration (config.json)
|
||||||
}
|
templates {
|
||||||
header @static Cache-Control "public, max-age=31536000, immutable"
|
mime application/json
|
||||||
|
}
|
||||||
# Security Headers (Future Proofing for Wasm)
|
|
||||||
header {
|
# Cache Control for static assets (immutable)
|
||||||
# Cross-Origin Isolation for SharedArrayBuffer (required for some Wasm features)
|
@static {
|
||||||
Cross-Origin-Opener-Policy "same-origin"
|
file
|
||||||
Cross-Origin-Embedder-Policy "require-corp"
|
path *.js *.css *.png *.jpg *.svg *.wasm
|
||||||
|
}
|
||||||
# Standard Security Headers
|
header @static Cache-Control "public, max-age=31536000, immutable"
|
||||||
X-Content-Type-Options "nosniff"
|
|
||||||
X-Frame-Options "DENY"
|
# Security Headers (Future Proofing for Wasm)
|
||||||
}
|
header {
|
||||||
|
# Cross-Origin Isolation for SharedArrayBuffer (required for some Wasm features)
|
||||||
# Health Check
|
Cross-Origin-Opener-Policy "same-origin"
|
||||||
handle /health {
|
Cross-Origin-Embedder-Policy "require-corp"
|
||||||
respond "healthy" 200
|
|
||||||
}
|
# Standard Security Headers
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "DENY"
|
||||||
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
|
Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- ROUTING LOGIC ---
|
||||||
|
|
||||||
|
# 1. API Proxy (Priority 1)
|
||||||
|
handle /api/* {
|
||||||
|
reverse_proxy api-gateway:8081 {
|
||||||
|
header_up Host {upstream_hostport}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Health Check
|
||||||
|
handle /health {
|
||||||
|
respond "healthy" 200
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Static Files & SPA Fallback (Priority 2)
|
||||||
|
handle {
|
||||||
|
# Serve static files if they exist
|
||||||
|
file_server
|
||||||
|
|
||||||
|
# SPA Routing: Fallback to index.html for non-existent files
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1.8
|
# syntax=docker/dockerfile:1.8
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# Dockerfile for Meldestelle Web-App (Pre-built Artifacts)
|
# Dockerfile for Meldestelle Web-App (Hybrid Build)
|
||||||
# Version: 3.1.0 - Local Build Injection
|
# Version: 3.2.0 - Optimized & Cleaned
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
||||||
# === GLOBAL ARGS ===
|
# === GLOBAL ARGS ===
|
||||||
@@ -31,6 +31,7 @@ COPY config/docker/caddy/web-app/config.json /usr/share/caddy/config.json
|
|||||||
COPY frontend/shells/meldestelle-portal/build/dist/js/productionExecutable/ /usr/share/caddy/
|
COPY frontend/shells/meldestelle-portal/build/dist/js/productionExecutable/ /usr/share/caddy/
|
||||||
|
|
||||||
# Ensure favicon exists (fallback)
|
# Ensure favicon exists (fallback)
|
||||||
|
# Using the shared asset from existing config structure
|
||||||
COPY config/docker/nginx/web-app/favicon.svg /usr/share/caddy/favicon.svg
|
COPY config/docker/nginx/web-app/favicon.svg /usr/share/caddy/favicon.svg
|
||||||
|
|
||||||
EXPOSE 4000
|
EXPOSE 4000
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"apiBaseUrl": "{{env "API_BASE_URL" | default "http://localhost:8081"}}"
|
"apiBaseUrl": "{{env "API_BASE_URL" | default ""}}"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -11,8 +11,6 @@ services:
|
|||||||
context: . # Wichtig: Root Context für Monorepo Zugriff
|
context: . # Wichtig: Root Context für Monorepo Zugriff
|
||||||
dockerfile: config/docker/caddy/web-app/Dockerfile
|
dockerfile: config/docker/caddy/web-app/Dockerfile
|
||||||
args:
|
args:
|
||||||
GRADLE_VERSION: "${DOCKER_GRADLE_VERSION:-9.3.1}"
|
|
||||||
JAVA_VERSION: "${DOCKER_JAVA_VERSION:-25}"
|
|
||||||
# Frontend spezifisch:
|
# Frontend spezifisch:
|
||||||
CADDY_VERSION: "${DOCKER_CADDY_VERSION:-2.9-alpine}"
|
CADDY_VERSION: "${DOCKER_CADDY_VERSION:-2.9-alpine}"
|
||||||
# Metadaten:
|
# Metadaten:
|
||||||
@@ -21,14 +19,12 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "org.opencontainers.image.created=${DOCKER_BUILD_DATE}"
|
- "org.opencontainers.image.created=${DOCKER_BUILD_DATE}"
|
||||||
container_name: "${PROJECT_NAME:-meldestelle}-web-app"
|
container_name: "${PROJECT_NAME:-meldestelle}-web-app"
|
||||||
restart: no
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "${WEB_APP_PORT:-4000:4000}"
|
- "${WEB_APP_PORT:-4000:4000}"
|
||||||
environment:
|
environment:
|
||||||
# Runtime Configuration for Caddy Templates
|
# Runtime Configuration for Caddy Templates
|
||||||
# Browser can access API via localhost:8081 (Gateway)
|
# Browser can access API via localhost:8081 (Gateway)
|
||||||
# In Docker network, it might be http://api-gateway:8081, but browser runs on host!
|
|
||||||
# Usually, for local dev, we want the browser to hit localhost:8081.
|
|
||||||
API_BASE_URL: "${WEB_APP_API_URL:-http://localhost:8081}"
|
API_BASE_URL: "${WEB_APP_API_URL:-http://localhost:8081}"
|
||||||
depends_on:
|
depends_on:
|
||||||
api-gateway:
|
api-gateway:
|
||||||
|
|||||||
@@ -41,19 +41,30 @@ Den Docker-Build für den `web-app` Service reparieren, der aufgrund von Gradle/
|
|||||||
* **Lösung:** Template auf einfache Syntax zurückgesetzt: `{{env "API_BASE_URL" ...}}`.
|
* **Lösung:** Template auf einfache Syntax zurückgesetzt: `{{env "API_BASE_URL" ...}}`.
|
||||||
* **Playbook Update:** DevOps Engineer übernimmt explizit die Verantwortung für Caddy/Webserver-Konfiguration.
|
* **Playbook Update:** DevOps Engineer übernimmt explizit die Verantwortung für Caddy/Webserver-Konfiguration.
|
||||||
|
|
||||||
|
### 5. Problem: Ping-Service Erreichbarkeit (CORS/Routing)
|
||||||
|
* **Symptom:** Frontend erhielt HTML (SPA Fallback) statt JSON vom Backend.
|
||||||
|
* **Ursache:** Caddy leitete `/api/*` nicht an den `api-gateway` weiter, sondern lieferte die `index.html` aus.
|
||||||
|
* **Lösung:**
|
||||||
|
1. **Caddyfile:** `reverse_proxy /api/* api-gateway:8081` hinzugefügt.
|
||||||
|
2. **config.json:** `apiBaseUrl` auf leer (`""`) gesetzt, damit Frontend relative Pfade nutzt.
|
||||||
|
3. **Security:** Caddyfile um Security Headers (`Permissions-Policy`, `Referrer-Policy`) und Logging erweitert.
|
||||||
|
|
||||||
## ✅ Ergebnisse
|
## ✅ Ergebnisse
|
||||||
1. **Web-App läuft:** Der Container `meldestelle-web-app` startet erfolgreich und ist unter `http://localhost:4000` erreichbar.
|
1. **Web-App läuft:** Der Container `meldestelle-web-app` startet erfolgreich und ist unter `http://localhost:4000` erreichbar.
|
||||||
2. **Build-Prozess:** Stabilisiert durch Hybrid-Ansatz (Lokal bauen -> Docker kopieren).
|
2. **API-Zugriff funktioniert:** Ping-Service liefert JSON (`200 OK`) an das Frontend.
|
||||||
3. **Infrastruktur:** `build.gradle.kts` (Root) ist sauberer und performanter.
|
3. **Build-Prozess:** Stabilisiert durch Hybrid-Ansatz (Lokal bauen -> Docker kopieren).
|
||||||
|
4. **Infrastruktur:** `build.gradle.kts` (Root) ist sauberer und performanter.
|
||||||
|
|
||||||
## ⏭️ Nächste Schritte (Open Points)
|
## ⏭️ Nächste Schritte (Open Points)
|
||||||
* **Ping-Service Erreichbarkeit:** Das Frontend kann den Ping-Service (`http://localhost:8081`) noch nicht erreichen (CORS oder Netzwerk-Thema). -> Übergabe an Backend/Frontend.
|
|
||||||
* **CI/CD:** Für die CI-Pipeline muss der Hybrid-Build berücksichtigt werden (Build-Step vor Docker-Build).
|
* **CI/CD:** Für die CI-Pipeline muss der Hybrid-Build berücksichtigt werden (Build-Step vor Docker-Build).
|
||||||
|
* **WebGL Warnungen:** Im Browser-Log tauchen WebGL-Warnungen auf (vermutlich Compose/Skiko related), die aber die Funktion nicht beeinträchtigen.
|
||||||
|
|
||||||
## 📂 Betroffene Dateien
|
## 📂 Betroffene Dateien
|
||||||
* `build.gradle.kts` (Root)
|
* `build.gradle.kts` (Root)
|
||||||
* `gradle.properties`
|
* `gradle.properties`
|
||||||
* `config/docker/caddy/web-app/Dockerfile`
|
* `config/docker/caddy/web-app/Dockerfile`
|
||||||
|
* `config/docker/caddy/web-app/Caddyfile`
|
||||||
* `.dockerignore`
|
* `.dockerignore`
|
||||||
* `config/docker/caddy/web-app/config.json`
|
* `config/docker/caddy/web-app/config.json`
|
||||||
|
* `dc-gui.yaml`
|
||||||
* `docs/04_Agents/Playbooks/DevOpsEngineer.md`
|
* `docs/04_Agents/Playbooks/DevOpsEngineer.md`
|
||||||
|
|||||||
Reference in New Issue
Block a user