neues docker-compose für Profuktion und Anpassungen
This commit is contained in:
185
DEPLOY-PRODUCTION.md
Normal file
185
DEPLOY-PRODUCTION.md
Normal 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
98
docker-compose.prod.yml
Normal 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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user