From 4fde7ed46ad64d187acfedf35618e203cf05c7c3 Mon Sep 17 00:00:00 2001 From: rxf Date: Tue, 10 Feb 2026 20:22:44 +0100 Subject: [PATCH] =?UTF-8?q?neues=20docker-compose=20f=C3=BCr=20Profuktion?= =?UTF-8?q?=20und=20Anpassungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEPLOY-PRODUCTION.md | 185 ++++++++++++++++++++++++++++++++++++++++ docker-compose.prod.yml | 98 +++++++++++++++++++++ frontend/nginx.conf | 2 +- frontend/src/App.jsx | 10 ++- 4 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 DEPLOY-PRODUCTION.md create mode 100644 docker-compose.prod.yml diff --git a/DEPLOY-PRODUCTION.md b/DEPLOY-PRODUCTION.md new file mode 100644 index 0000000..87f40ed --- /dev/null +++ b/DEPLOY-PRODUCTION.md @@ -0,0 +1,185 @@ +# Production Deployment Guide + +## Voraussetzungen + +- Docker und Docker Compose auf dem Server installiert +- Traefik läuft im `dockge_default` Netzwerk +- Domain `wetter.fuerst-stuttgart.de` zeigt auf den Server +- `.env` Datei mit Datenbank-Credentials + +## 1. Images bauen und pushen + +Lokal auf dem Entwicklungsrechner: + +```bash +# Alle Images bauen und zur Registry pushen +./push-images.sh +``` + +Dieser Befehl: +- Baut `wetterstation-collector` +- Baut `wetterstation-api` +- Baut `wetterstation-frontend` +- Pusht alle Images zu `docker.citysensor.de` + +## 2. Server vorbereiten + +Auf dem Production-Server: + +```bash +# Projekt-Verzeichnis erstellen +mkdir -p ~/wetterstation +cd ~/wetterstation + +# docker-compose.prod.yml hochladen +# .env Datei erstellen oder hochladen +``` + +### Beispiel .env für Production: + +```env +# Datenbank +DB_NAME=wetterstation +DB_USER=wetterstation_user +DB_PASSWORD= +DB_HOST=postgres +DB_PORT=5432 + +# Collector +COLLECTOR_PORT=8001 + +# Optional: PostgreSQL custom port +# DB_PORT=5432 +``` + +## 3. Deployment starten + +```bash +# Images von Registry pullen +docker-compose -f docker-compose.prod.yml pull + +# Services starten +docker-compose -f docker-compose.prod.yml up -d + +# Logs prüfen +docker-compose -f docker-compose.prod.yml logs -f +``` + +## 4. Erreichbarkeit + +Nach erfolgreichem Start ist die Wetterstation erreichbar unter: + +- **Frontend**: https://wetter.fuerst-stuttgart.de/ +- **API**: https://wetter.fuerst-stuttgart.de/api/health +- **Collector**: https://wetter.fuerst-stuttgart.de/collector/health + +Traefik übernimmt: +- Automatisches HTTPS (Let's Encrypt) +- Routing basierend auf Pfad +- StripPrefix für `/api` und `/collector` + +## 5. Updates deployen + +Bei Code-Änderungen: + +```bash +# Lokal: Images neu bauen und pushen +./push-images.sh + +# Server: Neue Images pullen und Container neu starten +ssh user@server +cd ~/wetterstation +docker-compose -f docker-compose.prod.yml pull +docker-compose -f docker-compose.prod.yml up -d +``` + +## 6. Nützliche Befehle + +```bash +# Status prüfen +docker-compose -f docker-compose.prod.yml ps + +# Logs einzelner Services +docker-compose -f docker-compose.prod.yml logs -f frontend +docker-compose -f docker-compose.prod.yml logs -f api +docker-compose -f docker-compose.prod.yml logs -f collector +docker-compose -f docker-compose.prod.yml logs -f postgres + +# Service neu starten +docker-compose -f docker-compose.prod.yml restart api + +# Alle Services stoppen +docker-compose -f docker-compose.prod.yml down + +# Services stoppen und Volumes löschen (⚠️ löscht Daten!) +docker-compose -f docker-compose.prod.yml down -v +``` + +## 7. Datenbank-Backup + +```bash +# Backup erstellen +docker exec wetterstation_db_prod pg_dump -U wetterstation_user wetterstation > backup_$(date +%Y%m%d_%H%M%S).sql + +# Backup wiederherstellen +docker exec -i wetterstation_db_prod psql -U wetterstation_user wetterstation < backup.sql +``` + +## Architektur + +``` +Internet + ↓ +Traefik (dockge_default) + ↓ + ├─→ Frontend (nginx) → API (intern) + ├─→ API (FastAPI) + └─→ Collector (FastAPI) + ↓ + PostgreSQL (intern) +``` + +**Netzwerke**: +- `dockge_default` (external): Traefik-Netzwerk +- `wetterstation_internal`: Interne Service-Kommunikation + +**Container**: +- `wetterstation_frontend_prod`: Nginx + React SPA +- `wetterstation_api_prod`: FastAPI (Weather Data API) +- `wetterstation_collector_prod`: FastAPI (Data Collection) +- `wetterstation_db_prod`: PostgreSQL 16 + +## Troubleshooting + +### SSL-Zertifikat wird nicht erstellt + +Prüfe: +- DNS zeigt auf Server: `dig wetter.fuerst-stuttgart.de` +- Traefik läuft: `docker ps | grep traefik` +- Port 80/443 offen: `netstat -tulpn | grep -E ':(80|443)'` + +### API nicht erreichbar + +```bash +# Prüfe ob Container läuft +docker ps | grep wetterstation_api_prod + +# Prüfe Logs +docker logs wetterstation_api_prod + +# Teste intern +docker exec wetterstation_api_prod curl localhost:8000/health +``` + +### Datenbank-Verbindungsfehler + +```bash +# Prüfe ob DB läuft +docker ps | grep wetterstation_db_prod + +# Prüfe DB-Logs +docker logs wetterstation_db_prod + +# Teste Verbindung +docker exec wetterstation_db_prod psql -U wetterstation_user -d wetterstation -c "SELECT 1" +``` diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..1926a5a --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,98 @@ +services: + postgres: + image: postgres:16-alpine + container_name: wetterstation_db_prod + restart: unless-stopped + environment: + POSTGRES_DB: ${DB_NAME} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + networks: + - internal + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] + interval: 10s + timeout: 5s + retries: 5 + + collector: + image: docker.citysensor.de/wetterstation-collector:latest + container_name: wetterstation_collector_prod + restart: unless-stopped + env_file: + - ./.env + environment: + DB_HOST: postgres + depends_on: + postgres: + condition: service_healthy + networks: + - internal + - proxy + labels: + - "traefik.enable=true" + - "traefik.docker.network=dockge_default" + - "traefik.http.routers.wetterstation-collector.rule=Host(`wetter.fuerst-stuttgart.de`) && PathPrefix(`/collector`)" + - "traefik.http.routers.wetterstation-collector.entrypoints=websecure" + - "traefik.http.routers.wetterstation-collector.tls=true" + - "traefik.http.routers.wetterstation-collector.tls.certresolver=letsencrypt" + - "traefik.http.middlewares.wetterstation-collector-stripprefix.stripprefix.prefixes=/collector" + - "traefik.http.routers.wetterstation-collector.middlewares=wetterstation-collector-stripprefix" + - "traefik.http.services.wetterstation-collector.loadbalancer.server.port=8001" + + api: + image: docker.citysensor.de/wetterstation-api:latest + container_name: wetterstation_api_prod + restart: unless-stopped + env_file: + - ./.env + environment: + DB_HOST: postgres + depends_on: + postgres: + condition: service_healthy + networks: + - internal + - proxy + labels: + - "traefik.enable=true" + - "traefik.docker.network=dockge_default" + - "traefik.http.routers.wetterstation-api.rule=Host(`wetter.fuerst-stuttgart.de`) && PathPrefix(`/api`)" + - "traefik.http.routers.wetterstation-api.entrypoints=websecure" + - "traefik.http.routers.wetterstation-api.tls=true" + - "traefik.http.routers.wetterstation-api.tls.certresolver=letsencrypt" + - "traefik.http.middlewares.wetterstation-api-stripprefix.stripprefix.prefixes=/api" + - "traefik.http.routers.wetterstation-api.middlewares=wetterstation-api-stripprefix" + - "traefik.http.services.wetterstation-api.loadbalancer.server.port=8000" + + frontend: + image: docker.citysensor.de/wetterstation-frontend:latest + container_name: wetterstation_frontend_prod + restart: unless-stopped + depends_on: + - api + networks: + - internal + - proxy + labels: + - "traefik.enable=true" + - "traefik.docker.network=dockge_default" + - "traefik.http.routers.wetterstation.rule=Host(`wetter.fuerst-stuttgart.de`)" + - "traefik.http.routers.wetterstation.entrypoints=websecure" + - "traefik.http.routers.wetterstation.tls=true" + - "traefik.http.routers.wetterstation.tls.certresolver=letsencrypt" + - "traefik.http.services.wetterstation.loadbalancer.server.port=80" + +volumes: + postgres_data: + name: wetterstation_postgres_data_prod + +networks: + internal: + name: wetterstation_internal + driver: bridge + proxy: + name: dockge_default + external: true diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 2615e7c..c41aaec 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -10,7 +10,7 @@ server { gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; - # API proxy + # API proxy (wird im Docker-Compose-Netzwerk aufgelöst) location /api/ { proxy_pass http://api:8000/; proxy_http_version 1.1; diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 849cb8f..3e05056 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -17,8 +17,14 @@ function App() { setLastUpdate(new Date()) setLoading(false) } else { - // Entwicklungsmodus: Daten von API holen - const response = await fetch('http://localhost:8000/weather/history?hours=24') + // Development oder Production: Daten von API holen + // Im Development: localhost:8000 + // Im Production: /api/ (nginx proxy) + const apiUrl = import.meta.env.DEV + ? 'http://localhost:8000/weather/history?hours=24' + : '/api/weather/history?hours=24' + + const response = await fetch(apiUrl) if (!response.ok) { throw new Error('API-Fehler: ' + response.status) }