neues docker-compose für Profuktion und Anpassungen

This commit is contained in:
rxf
2026-02-10 20:22:44 +01:00
parent f32e472ea3
commit 4fde7ed46a
4 changed files with 292 additions and 3 deletions

185
DEPLOY-PRODUCTION.md Normal file
View File

@@ -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=<sicheres-passwort>
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"
```

98
docker-compose.prod.yml Normal file
View File

@@ -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

View File

@@ -10,7 +10,7 @@ server {
gzip_min_length 1024; gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; 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/ { location /api/ {
proxy_pass http://api:8000/; proxy_pass http://api:8000/;
proxy_http_version 1.1; proxy_http_version 1.1;

View File

@@ -17,8 +17,14 @@ function App() {
setLastUpdate(new Date()) setLastUpdate(new Date())
setLoading(false) setLoading(false)
} else { } else {
// Entwicklungsmodus: Daten von API holen // Development oder Production: Daten von API holen
const response = await fetch('http://localhost:8000/weather/history?hours=24') // 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) { if (!response.ok) {
throw new Error('API-Fehler: ' + response.status) throw new Error('API-Fehler: ' + response.status)
} }