412 lines
6.5 KiB
Markdown
412 lines
6.5 KiB
Markdown
# Best Practices - Dos and Don'ts
|
|
|
|
Empfehlungen fuer den sicheren und effizienten Betrieb des Container Spawners.
|
|
|
|
## Inhaltsverzeichnis
|
|
|
|
- [Produktions-Checkliste](#produktions-checkliste)
|
|
- [Dos - Empfohlene Praktiken](#dos---empfohlene-praktiken)
|
|
- [Don'ts - Zu vermeiden](#donts---zu-vermeiden)
|
|
- [Haeufige Fehler](#haeufige-fehler)
|
|
|
|
---
|
|
|
|
## Produktions-Checkliste
|
|
|
|
### Vor dem Go-Live
|
|
|
|
| Kategorie | Aufgabe | Status |
|
|
|-----------|---------|--------|
|
|
| **Sicherheit** | SECRET_KEY generiert (min. 32 Bytes) | [ ] |
|
|
| | `.env` nicht im Repository | [ ] |
|
|
| | HTTPS aktiviert | [ ] |
|
|
| | Docker Socket Proxy konfiguriert | [ ] |
|
|
| **Konfiguration** | BASE_DOMAIN korrekt | [ ] |
|
|
| | TRAEFIK_NETWORK existiert | [ ] |
|
|
| | Resource-Limits angemessen | [ ] |
|
|
| **Infrastruktur** | DNS-Eintraege (Wildcard) | [ ] |
|
|
| | Traefik laeuft stabil | [ ] |
|
|
| | Firewall konfiguriert | [ ] |
|
|
| **Monitoring** | Health-Check funktioniert | [ ] |
|
|
| | Logs werden geschrieben | [ ] |
|
|
| | Backup-Strategie | [ ] |
|
|
| **Testing** | Login/Signup funktioniert | [ ] |
|
|
| | Container wird erstellt | [ ] |
|
|
| | Subdomain erreichbar | [ ] |
|
|
|
|
---
|
|
|
|
## Dos - Empfohlene Praktiken
|
|
|
|
### Konfiguration
|
|
|
|
**DO: SECRET_KEY sicher generieren**
|
|
|
|
```bash
|
|
# Guter Key (32 Bytes = 64 Hex-Zeichen)
|
|
python3 -c "import secrets; print(secrets.token_hex(32))"
|
|
```
|
|
|
|
**DO: Umgebungsvariablen fuer sensible Daten**
|
|
|
|
```bash
|
|
# In .env (nie committen!)
|
|
SECRET_KEY=abc123...
|
|
DATABASE_URL=postgresql://...
|
|
```
|
|
|
|
**DO: Resource-Limits setzen**
|
|
|
|
```bash
|
|
# In .env
|
|
DEFAULT_MEMORY_LIMIT=512m
|
|
DEFAULT_CPU_QUOTA=50000
|
|
```
|
|
|
|
---
|
|
|
|
### Deployment
|
|
|
|
**DO: Docker Compose fuer Orchestrierung**
|
|
|
|
```bash
|
|
# Nicht einzelne docker run Befehle
|
|
docker-compose up -d
|
|
docker-compose logs -f
|
|
docker-compose down
|
|
```
|
|
|
|
**DO: Images taggen**
|
|
|
|
```bash
|
|
# Versionierte Tags statt :latest in Produktion
|
|
docker build -t spawner:0.1.0 .
|
|
docker build -t spawner:latest .
|
|
```
|
|
|
|
**DO: Health-Checks nutzen**
|
|
|
|
```yaml
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
```
|
|
|
|
---
|
|
|
|
### Monitoring
|
|
|
|
**DO: Logs zentralisieren**
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
```
|
|
|
|
**DO: Regelmaessige Backups**
|
|
|
|
```bash
|
|
# Cronjob fuer taegliches Backup
|
|
0 2 * * * /pfad/zu/spawner/backup.sh
|
|
```
|
|
|
|
**DO: Disk-Space ueberwachen**
|
|
|
|
```bash
|
|
# Docker-Ressourcen pruefen
|
|
docker system df
|
|
docker system prune -f # Vorsicht in Produktion!
|
|
```
|
|
|
|
---
|
|
|
|
### Sicherheit
|
|
|
|
**DO: HTTPS erzwingen**
|
|
|
|
```yaml
|
|
# Traefik-Labels
|
|
- "traefik.http.routers.spawner.entrypoints=websecure"
|
|
- "traefik.http.routers.spawner.tls=true"
|
|
```
|
|
|
|
**DO: Minimale Berechtigungen**
|
|
|
|
```yaml
|
|
# Docker Socket Proxy statt direktem Zugriff
|
|
DOCKER_HOST: tcp://docker-proxy:2375
|
|
```
|
|
|
|
**DO: Container als Non-Root**
|
|
|
|
```dockerfile
|
|
# Im User-Template
|
|
USER nginx
|
|
# oder
|
|
RUN useradd -m appuser && chown -R appuser /app
|
|
USER appuser
|
|
```
|
|
|
|
---
|
|
|
|
## Don'ts - Zu vermeiden
|
|
|
|
### Konfiguration
|
|
|
|
**DON'T: Hardcoded Secrets**
|
|
|
|
```python
|
|
# NIEMALS!
|
|
SECRET_KEY = "supersecret123"
|
|
```
|
|
|
|
**DON'T: Debug-Mode in Produktion**
|
|
|
|
```python
|
|
# NIEMALS in Produktion!
|
|
app.run(debug=True)
|
|
FLASK_DEBUG=1
|
|
```
|
|
|
|
**DON'T: Schwache Keys**
|
|
|
|
```bash
|
|
# ZU KURZ / ZU EINFACH
|
|
SECRET_KEY=test
|
|
SECRET_KEY=12345
|
|
```
|
|
|
|
---
|
|
|
|
### Deployment
|
|
|
|
**DON'T: Manuelles Container-Management**
|
|
|
|
```bash
|
|
# Vermeiden - besser docker-compose
|
|
docker run -d --name spawner ...
|
|
docker stop spawner
|
|
docker rm spawner
|
|
```
|
|
|
|
**DON'T: Unversionierte Images in Produktion**
|
|
|
|
```bash
|
|
# Vermeiden
|
|
USER_TEMPLATE_IMAGE=user-service-template:latest
|
|
|
|
# Besser
|
|
USER_TEMPLATE_IMAGE=user-service-template:0.1.0
|
|
```
|
|
|
|
**DON'T: Ohne Health-Checks deployen**
|
|
|
|
---
|
|
|
|
### Sicherheit
|
|
|
|
**DON'T: Docker Socket direkt exponieren**
|
|
|
|
```yaml
|
|
# NIEMALS!
|
|
ports:
|
|
- "2375:2375" # Docker API oeffentlich
|
|
```
|
|
|
|
**DON'T: .env committen**
|
|
|
|
```bash
|
|
# .gitignore MUSS enthalten:
|
|
.env
|
|
*.db
|
|
```
|
|
|
|
**DON'T: Container ohne Resource-Limits**
|
|
|
|
```python
|
|
# Vermeiden - DoS-Risiko
|
|
container = client.containers.run(image)
|
|
|
|
# Besser
|
|
container = client.containers.run(
|
|
image,
|
|
mem_limit='512m',
|
|
cpu_quota=50000
|
|
)
|
|
```
|
|
|
|
**DON'T: Root-Container in Produktion**
|
|
|
|
---
|
|
|
|
### Wartung
|
|
|
|
**DON'T: Logs ignorieren**
|
|
|
|
```bash
|
|
# Regelmaessig pruefen!
|
|
docker-compose logs --tail=100 spawner
|
|
```
|
|
|
|
**DON'T: Backups vergessen**
|
|
|
|
**DON'T: Updates aufschieben**
|
|
|
|
```bash
|
|
# Regelmaessig aktualisieren
|
|
git pull origin main
|
|
docker-compose build
|
|
docker-compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Haeufige Fehler
|
|
|
|
### 1. "Connection refused" beim Health-Check
|
|
|
|
**Ursache**: Container noch nicht bereit
|
|
|
|
**Loesung**: Warten oder start_period erhoehen:
|
|
|
|
```yaml
|
|
healthcheck:
|
|
start_period: 30s
|
|
```
|
|
|
|
---
|
|
|
|
### 2. "Network not found"
|
|
|
|
**Ursache**: Traefik-Netzwerk existiert nicht
|
|
|
|
**Loesung**:
|
|
|
|
```bash
|
|
docker network create web
|
|
# Oder TRAEFIK_NETWORK in .env anpassen
|
|
```
|
|
|
|
---
|
|
|
|
### 3. "Permission denied" bei Docker Socket
|
|
|
|
**Ursache**: Spawner-Container hat keinen Zugriff
|
|
|
|
**Loesung**:
|
|
|
|
```yaml
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:rw
|
|
```
|
|
|
|
Oder Docker-Gruppe:
|
|
|
|
```bash
|
|
sudo usermod -aG docker $USER
|
|
```
|
|
|
|
---
|
|
|
|
### 4. "Image not found"
|
|
|
|
**Ursache**: User-Template nicht gebaut
|
|
|
|
**Loesung**:
|
|
|
|
```bash
|
|
docker build -t user-service-template:latest ./user-template/
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Subdomain nicht erreichbar
|
|
|
|
**Ursache**: DNS oder Traefik-Konfiguration
|
|
|
|
**Diagnose**:
|
|
|
|
```bash
|
|
# DNS pruefen
|
|
nslookup username.example.com
|
|
|
|
# Traefik-Routes pruefen (Dashboard)
|
|
# Container-Labels pruefen
|
|
docker inspect user-xxx | jq '.[0].Config.Labels'
|
|
```
|
|
|
|
---
|
|
|
|
### 6. "Database locked"
|
|
|
|
**Ursache**: SQLite-Konkurrenzzugriff
|
|
|
|
**Loesung fuer Produktion**: PostgreSQL verwenden:
|
|
|
|
```bash
|
|
DATABASE_URL=postgresql://spawner:pass@postgres:5432/spawner
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Container startet, aber Service nicht erreichbar
|
|
|
|
**Ursache**: Falscher Port in Labels
|
|
|
|
**Loesung**: Port in `container_manager.py` pruefen:
|
|
|
|
```python
|
|
# Muss mit EXPOSE im Dockerfile uebereinstimmen
|
|
f'traefik.http.services.user{user_id}.loadbalancer.server.port': '8080'
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
### Nuetzliche Befehle
|
|
|
|
```bash
|
|
# Status
|
|
docker-compose ps
|
|
docker ps --filter 'label=spawner.managed=true'
|
|
|
|
# Logs
|
|
docker-compose logs -f spawner
|
|
docker logs user-xxx-1
|
|
|
|
# Neustart
|
|
docker-compose restart spawner
|
|
|
|
# Komplett neu
|
|
docker-compose down
|
|
docker-compose up -d --build
|
|
|
|
# Cleanup
|
|
docker system prune -f
|
|
docker volume prune -f
|
|
```
|
|
|
|
### Debugging
|
|
|
|
```bash
|
|
# In Container einsteigen
|
|
docker exec -it spawner bash
|
|
|
|
# Python-Shell
|
|
docker exec -it spawner python
|
|
|
|
# Datenbank
|
|
docker exec spawner sqlite3 /app/data/users.db ".tables"
|
|
```
|
|
|
|
---
|
|
|
|
Zurueck zur [Dokumentations-Uebersicht](../README.md)
|