feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready

This commit is contained in:
2025-10-31 01:39:24 +01:00
parent 55c04e4fd0
commit e26eb2aa12
601 changed files with 44184 additions and 32477 deletions

View File

@@ -0,0 +1,381 @@
# Docker Swarm + Traefik Deployment Guide
Production deployment guide for the Custom PHP Framework using Docker Swarm orchestration with Traefik load balancer.
## Architecture Overview
```
Internet → Traefik (SSL Termination, Load Balancing)
[Web Service - 3 Replicas]
↓ ↓
Database Redis Queue Workers
(PostgreSQL) (Cache/Sessions) (2 Replicas)
```
**Key Components**:
- **Traefik v2.10**: Reverse proxy, SSL termination, automatic service discovery
- **Web Service**: 3 replicas of PHP-FPM + Nginx (HTTP only, Traefik handles HTTPS)
- **PostgreSQL 16**: Single instance database (manager node)
- **Redis 7**: Sessions and cache (manager node)
- **Queue Workers**: 2 replicas for background job processing
- **Docker Swarm**: Native container orchestration with rolling updates and health checks
## Prerequisites
1. **Docker Engine 28.0+** with Swarm mode enabled
2. **Production Server** with SSH access
3. **SSL Certificates** in `./ssl/` directory (cert.pem, key.pem)
4. **Environment Variables** in `.env` file on production server
5. **Docker Image** built and available
## Initial Setup
### 1. Initialize Docker Swarm
On production server:
```bash
docker swarm init
```
Verify:
```bash
docker node ls
# Should show 1 node as Leader
```
### 2. Create Docker Secrets
Create secrets from .env file values:
```bash
cd /home/deploy/framework
# Create secrets (one-time setup)
echo "$DB_PASSWORD" | docker secret create db_password -
echo "$APP_KEY" | docker secret create app_key -
echo "$VAULT_ENCRYPTION_KEY" | docker secret create vault_encryption_key -
echo "$SHOPIFY_WEBHOOK_SECRET" | docker secret create shopify_webhook_secret -
echo "$RAPIDMAIL_PASSWORD" | docker secret create rapidmail_password -
```
Or use the automated script:
```bash
./scripts/setup-production-secrets.sh
```
Verify secrets:
```bash
docker secret ls
```
### 3. Build and Transfer Docker Image
On local machine:
**Option A: Via Private Registry** (if available):
```bash
# Build image
docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .
# Push to registry
docker push 94.16.110.151:5000/framework:latest
```
**Option B: Direct Transfer via SSH** (recommended for now):
```bash
# Build image
docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .
# Save and transfer to production
docker save 94.16.110.151:5000/framework:latest | \
ssh -i ~/.ssh/production deploy@94.16.110.151 'docker load'
```
### 4. Deploy Stack
On production server:
```bash
cd /home/deploy/framework
# Deploy the stack
docker stack deploy -c docker-compose.prod.yml framework
# Monitor deployment
watch docker stack ps framework
# Check service status
docker stack services framework
```
## Health Monitoring
### Check Service Status
```bash
# List all services
docker stack services framework
# Check specific service
docker service ps framework_web
# View service logs
docker service logs framework_web -f
docker service logs framework_traefik -f
docker service logs framework_db -f
```
### Health Check Endpoints
- **Main Health**: http://localhost/health (via Traefik)
- **Traefik Dashboard**: http://traefik.localhost:8080 (manager node only)
### Expected Service Replicas
| Service | Replicas | Purpose |
|---------|----------|---------|
| traefik | 1 | Reverse proxy + SSL |
| web | 3 | Application servers |
| db | 1 | PostgreSQL database |
| redis | 1 | Cache + sessions |
| queue-worker | 2 | Background jobs |
## Rolling Updates
### Update Application
1. Build new image with updated code:
```bash
docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .
```
2. Transfer to production (if no registry):
```bash
docker save 94.16.110.151:5000/framework:latest | \
ssh -i ~/.ssh/production deploy@94.16.110.151 'docker load'
```
3. Update the service:
```bash
# On production server
docker service update --image 94.16.110.151:5000/framework:latest framework_web
```
The update will:
- Roll out to 1 container at a time (`parallelism: 1`)
- Wait 10 seconds between updates (`delay: 10s`)
- Start new container before stopping old one (`order: start-first`)
- Automatically rollback on failure (`failure_action: rollback`)
### Monitor Update Progress
```bash
# Watch update status
watch docker service ps framework_web
# View update logs
docker service logs framework_web -f --tail 50
```
### Manual Rollback
If needed, rollback to previous version:
```bash
docker service rollback framework_web
```
## Troubleshooting
### Service Won't Start
Check service logs:
```bash
docker service logs framework_web --tail 100
```
Check task failures:
```bash
docker service ps framework_web --no-trunc
```
### Container Crashing
Inspect individual container:
```bash
# Get container ID
docker ps -a | grep framework_web
# View logs
docker logs <container_id>
# Exec into running container
docker exec -it <container_id> bash
```
### SSL/TLS Issues
Traefik handles SSL termination. Check Traefik logs:
```bash
docker service logs framework_traefik -f
```
Verify SSL certificates are mounted in docker-compose.prod.yml:
```yaml
volumes:
- ./ssl:/ssl:ro
```
### Database Connection Issues
Check PostgreSQL health:
```bash
docker service logs framework_db --tail 50
# Exec into db container
docker exec -it $(docker ps -q -f name=framework_db) psql -U postgres -d framework_prod
```
### Redis Connection Issues
Check Redis availability:
```bash
docker service logs framework_redis --tail 50
# Test Redis connection
docker exec -it $(docker ps -q -f name=framework_redis) redis-cli ping
```
### Performance Issues
Check resource usage:
```bash
# Service resource limits
docker service inspect framework_web --format='{{json .Spec.TaskTemplate.Resources}}' | jq
# Container stats
docker stats
```
## Scaling
### Scale Web Service
```bash
# Scale up to 5 replicas
docker service scale framework_web=5
# Scale down to 2 replicas
docker service scale framework_web=2
```
### Scale Queue Workers
```bash
# Scale workers based on queue backlog
docker service scale framework_queue-worker=4
```
## Cleanup
### Remove Stack
```bash
# Remove entire stack
docker stack rm framework
# Verify removal
docker stack ls
```
### Remove Secrets
```bash
# List secrets
docker secret ls
# Remove specific secret
docker secret rm db_password
# Remove all framework secrets
docker secret ls | grep -E "db_password|app_key|vault_encryption_key" | awk '{print $2}' | xargs docker secret rm
```
### Leave Swarm
```bash
# Force leave Swarm (removes all services and secrets)
docker swarm leave --force
```
## Network Architecture
### Overlay Networks
- **traefik-public**: External network for Traefik ↔ Web communication
- **backend**: Internal network for Web ↔ Database/Redis communication
### Port Mappings
| Port | Service | Purpose |
|------|---------|---------|
| 80 | Traefik | HTTP (redirects to 443) |
| 443 | Traefik | HTTPS (production traffic) |
| 8080 | Traefik | Dashboard (manager node only) |
## Volume Management
### Named Volumes
| Volume | Purpose | Mounted In |
|--------|---------|------------|
| traefik-logs | Traefik access logs | traefik |
| storage-logs | Application logs | web, queue-worker |
| storage-uploads | User uploads | web |
| storage-queue | Queue data | queue-worker |
| db-data | PostgreSQL data | db |
| redis-data | Redis persistence | redis |
### Backup Volumes
```bash
# Backup database
docker exec $(docker ps -q -f name=framework_db) pg_dump -U postgres framework_prod > backup.sql
# Backup Redis (if persistence enabled)
docker exec $(docker ps -q -f name=framework_redis) redis-cli --rdb /data/dump.rdb
```
## Security Best Practices
1. **Secrets Management**: Never commit secrets to version control, use Docker Secrets
2. **Network Isolation**: Backend network is internal-only, no external access
3. **SSL/TLS**: Traefik enforces HTTPS, redirects HTTP → HTTPS
4. **Health Checks**: All services have health checks with automatic restart
5. **Resource Limits**: Production services have memory/CPU limits
6. **Least Privilege**: Containers run as www-data (not root) where possible
## Phase 2 - Monitoring (Coming Soon)
- Prometheus for metrics collection
- Grafana dashboards
- Automated PostgreSQL backups
- Email/Slack alerting
## Phase 3 - CI/CD (Coming Soon)
- Gitea Actions workflow
- Loki + Promtail for log aggregation
- Performance tuning
## Phase 4 - High Availability (Future)
- Multi-node Swarm cluster
- Varnish CDN cache layer
- PostgreSQL Primary/Replica with pgpool
- MinIO object storage
## References
- [Docker Swarm Documentation](https://docs.docker.com/engine/swarm/)
- [Traefik v2 Documentation](https://doc.traefik.io/traefik/)
- [Docker Secrets Management](https://docs.docker.com/engine/swarm/secrets/)

View File

@@ -201,12 +201,16 @@ use App\Framework\Logging\Processors\WebInfoProcessor;
$webInfoProcessor = new WebInfoProcessor($request);
```
**3. Exception Processor**:
**3. Exception Enrichment Processor**:
```php
use App\Framework\Logging\Processors\ExceptionProcessor;
use App\Framework\Logging\Processors\ExceptionEnrichmentProcessor;
// Adds exception class, file, line, stack trace
$exceptionProcessor = new ExceptionProcessor();
// Converts Throwables to ExceptionContext with enriched metadata
// - Exception hash for pattern recognition
// - Severity categorization
// - Short stack trace for quick overview
// - Exception chain length
$exceptionEnrichmentProcessor = new ExceptionEnrichmentProcessor();
```
**4. Interpolation Processor**:

View File

@@ -0,0 +1,800 @@
# Production Deployment Guide
Umfassende Anleitung für das Deployment der Custom PHP Framework Anwendung auf dem Production Server.
## Inhaltsverzeichnis
1. [Architektur-Übersicht](#architektur-übersicht)
2. [Voraussetzungen](#voraussetzungen)
3. [Sicherheits-Setup](#sicherheits-setup)
4. [Docker Registry Setup](#docker-registry-setup)
5. [Production Image Build](#production-image-build)
6. [Deployment Prozess](#deployment-prozess)
7. [Troubleshooting](#troubleshooting)
8. [Monitoring](#monitoring)
---
## Architektur-Übersicht
### Development vs Production
**Development** (docker-compose.yml):
- Separate Container: Nginx + PHP-FPM
- Source Code via Volume Mounts
- Hot-Reload für Development
- Xdebug aktiviert
**Production** (docker-compose.prod.yml):
- Single Container: Supervisor → Nginx + PHP-FPM
- Code im Image eingebacken
- Minimale Volume Mounts (nur logs/uploads)
- Optimiert für Performance
### Production Stack
```
┌─────────────────────────────────────────────────┐
│ Production Server (94.16.110.151) │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐│
│ │ Web │ │ PHP │ │ Redis ││
│ │ (Supervisor│ │ │ │ Cache ││
│ │ Nginx + │ │ │ │ ││
│ │ PHP-FPM) │ │ │ │ ││
│ └────────────┘ └────────────┘ └────────────┘│
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐│
│ │ PostgreSQL │ │ Queue │ │ Watchtower ││
│ │ Database │ │ Worker │ │ Auto-Update││
│ └────────────┘ └────────────┘ └────────────┘│
│ │
│ Monitoring (VPN-only): │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐│
│ │ Prometheus │ │ Grafana │ │ Portainer ││
│ │ :9090 │ │ :3000 │ │ :9443 ││
│ └────────────┘ └────────────┘ └────────────┘│
└─────────────────────────────────────────────────┘
│ WireGuard VPN (10.8.0.0/24)
┌───┴────┐
│ Client │
└────────┘
```
---
## Voraussetzungen
### Server Requirements
- **OS**: Ubuntu 22.04 LTS (oder neuer)
- **RAM**: Minimum 4GB (empfohlen: 8GB+)
- **CPU**: 2+ Cores
- **Disk**: 50GB+ freier Speicherplatz
- **Network**: Statische IP oder DNS
### Installierte Software
```bash
# Docker & Docker Compose
docker --version # 24.0+
docker-compose --version # 2.20+
# WireGuard (für sicheren Zugriff)
wg --version
# SSL Tools
openssl version
```
### Ports
**Public (Firewall offen)**:
- `8888`: HTTP (optional, für HTTP→HTTPS Redirect)
- `8443`: HTTPS (Hauptzugang)
- `51820`: WireGuard VPN (UDP)
**VPN-only (über 10.8.0.1)**:
- `9090`: Prometheus
- `3000`: Grafana
- `9443`: Portainer
**Internal (nicht extern erreichbar)**:
- `5432`: PostgreSQL
- `6379`: Redis
- `9000`: PHP-FPM
---
## Sicherheits-Setup
### 1. WireGuard VPN
WireGuard bietet verschlüsselten Zugang zum Production Server für Administration und Monitoring.
**Server Installation**:
```bash
# Als root auf Production Server
apt update
apt install -y wireguard
# Schlüssel generieren
cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Server Config
cat > /etc/wireguard/wg0.conf <<'EOF'
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server_private_key>
# Client (Development Machine)
[Peer]
PublicKey = <client_public_key>
AllowedIPs = 10.8.0.2/32
EOF
# Service starten
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
systemctl status wg-quick@wg0
```
**Client Configuration** (`/etc/wireguard/wg0-production.conf`):
```ini
[Interface]
Address = 10.8.0.2/32
PrivateKey = <client_private_key>
DNS = 1.1.1.1
[Peer]
PublicKey = <server_public_key>
Endpoint = 94.16.110.151:51820
AllowedIPs = 10.8.0.0/24, 94.16.110.151/32
PersistentKeepalive = 25
```
**Client Start**:
```bash
sudo wg-quick up wg0-production
sudo wg show # Verify connection
ping 10.8.0.1 # Test connectivity
```
### 2. Firewall Configuration
**UFW Rules** (auf Production Server):
```bash
# Default policies
ufw default deny incoming
ufw default allow outgoing
# SSH (nur von spezifischen IPs)
ufw allow from <deine_ip> to any port 22
# WireGuard
ufw allow 51820/udp
# HTTP/HTTPS
ufw allow 8888/tcp # HTTP (optional)
ufw allow 8443/tcp # HTTPS
# Enable firewall
ufw enable
ufw status verbose
```
### 3. SSL/TLS Zertifikate
**Development/Testing** (Self-Signed):
```bash
# Bereits vorhanden in ./ssl/
# - cert.pem
# - key.pem
```
**Production** (Let's Encrypt empfohlen):
```bash
# Mit certbot
certbot certonly --standalone -d yourdomain.com
# Zertifikate nach ./ssl/ kopieren
```
---
## Docker Registry Setup
### Local Registry on Production Server
Für sichere, private Image-Verwaltung läuft eine lokale Docker Registry auf dem Production Server.
**Registry starten**:
```bash
docker run -d \
--restart=always \
--name registry \
-p 127.0.0.1:5000:5000 \
registry:2
```
**Verify**:
```bash
curl http://localhost:5000/v2/_catalog
```
**Registry in Docker konfigurieren**:
`/etc/docker/daemon.json`:
```json
{
"insecure-registries": ["94.16.110.151:5000"]
}
```
```bash
sudo systemctl restart docker
```
---
## Production Image Build
### Build-Prozess
Das Production Image wird lokal gebaut und dann zur Registry gepusht.
**1. Production Dockerfile** (`Dockerfile.production`):
```dockerfile
# Multi-stage build für optimale Image-Größe
FROM php:8.3-fpm-alpine AS base
# System dependencies
RUN apk add --no-cache \
nginx \
supervisor \
postgresql-dev \
libpq \
&& docker-php-ext-install pdo pdo_pgsql
# PHP Configuration
COPY docker/php/php.production.ini /usr/local/etc/php/conf.d/production.ini
COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
COPY docker/php/zz-docker.production.conf /usr/local/etc/php-fpm.d/zz-docker.conf
# Nginx Configuration
COPY docker/nginx/nginx.production.conf /etc/nginx/nginx.conf
COPY docker/nginx/default.production.conf /etc/nginx/http.d/default.conf
# Supervisor Configuration
COPY docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Application Code
WORKDIR /var/www/html
COPY --chown=www-data:www-data . .
# Composer dependencies (Production only)
RUN composer install --no-dev --optimize-autoloader --no-interaction
# NPM build
RUN npm ci && npm run build
# Permissions
RUN chown -R www-data:www-data /var/www/html/storage \
&& chmod -R 775 /var/www/html/storage
# Start Supervisor (manages nginx + php-fpm)
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
**2. Build Command**:
```bash
# Im Projekt-Root
docker build \
-f Dockerfile.production \
-t 94.16.110.151:5000/framework:latest \
.
```
**3. Push to Registry**:
```bash
docker push 94.16.110.151:5000/framework:latest
```
**4. Verify Push**:
```bash
curl http://94.16.110.151:5000/v2/framework/tags/list
```
### Wichtige Konfigurationsdateien
#### Supervisor Configuration (`docker/supervisor/supervisord.conf`)
```ini
[supervisord]
nodaemon=true
silent=false
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
loglevel=info
[program:php-fpm]
command=php-fpm -F
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startretries=3
[program:nginx]
command=nginx -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startretries=3
depends_on=php-fpm
```
**Wichtige Änderungen**:
- `silent=false` + `logfile=/dev/null`: Supervisor loggt nach stdout/stderr statt Datei
- Grund: Python's logging kann `/dev/stdout` oder `/proc/self/fd/1` nicht im append-mode öffnen
#### PHP-FPM Production Config (`docker/php/zz-docker.production.conf`)
```ini
[www]
user = www-data
group = www-data
listen = 9000
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
```
**Wichtig**: User/Group explizit auf `www-data` setzen, da Container als root läuft.
---
## Deployment Prozess
### Docker Compose Setup
**Base Configuration** (`docker-compose.yml`):
- Definiert alle Services für Development
- Wird **nicht** auf Production Server deployed
**Production Overrides** (`docker-compose.prod.yml`):
- Merged mit base config
- Production-spezifische Einstellungen
### Production Override Highlights
**Web Service**:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
pull_policy: always # Immer von Registry pullen, nie bauen
entrypoint: [] # Entrypoint von Base-Image clearen
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
user: root # Container läuft als root, PHP-FPM workers als www-data
volumes:
- ./storage/logs:/var/www/html/storage/logs:rw
- ./storage/uploads:/var/www/html/storage/uploads:rw
- ./ssl:/var/www/ssl:ro
environment:
- APP_ENV=production
labels:
com.centurylinklabs.watchtower.enable: "true"
```
**Wichtige Overrides**:
1. `pull_policy: always`: Verhindert lokales Build, zwingt Registry-Pull
2. `entrypoint: []`: Clearen des inherited entrypoint vom Base PHP-Image
3. `command: [...]`: Expliziter Start-Command für Supervisor
4. `user: root`: Nötig für Supervisor, PHP-FPM läuft intern als www-data
### Deployment Steps
**1. Files auf Server kopieren**:
```bash
# Lokale Entwicklungsmaschine (via WireGuard)
scp docker-compose.prod.yml deploy@94.16.110.151:/home/deploy/framework/
scp .env.production deploy@94.16.110.151:/home/deploy/framework/.env
```
**2. Auf Server: Pull und Deploy**:
```bash
# SSH auf Production Server
ssh deploy@94.16.110.151
# In Projekt-Verzeichnis
cd /home/deploy/framework
# Pull latest image
docker pull 94.16.110.151:5000/framework:latest
# Deploy Stack
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Check Status
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
```
**3. Logs überwachen**:
```bash
# Alle Container
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs -f
# Spezifischer Container
docker logs -f web
docker logs -f php
```
### Deployment Verification
**Container Health Checks**:
```bash
# Alle Container sollten "healthy" sein
docker-compose ps
# Output sollte zeigen:
# web Up (healthy)
# php Up (healthy)
# db Up (healthy)
# redis Up (healthy)
```
**Supervisor Status (im web container)**:
```bash
docker exec web supervisorctl status
# Output:
# nginx RUNNING pid 7, uptime 0:05:23
# php-fpm RUNNING pid 8, uptime 0:05:23
```
**Nginx & PHP-FPM Processes**:
```bash
docker exec web ps aux | grep -E 'nginx|php-fpm'
# Sollte zeigen:
# root 1 supervisor
# root 7 nginx: master
# www-data nginx: worker (mehrere)
# root 8 php-fpm: master
# www-data php-fpm: pool www (mehrere)
```
**Application Test**:
```bash
# Von lokalem Rechner (via WireGuard)
curl -k -I https://94.16.110.151:8443/
# Erwartete Response:
# HTTP/2 200
# server: nginx
# content-type: text/html
```
---
## Troubleshooting
### Problem 1: Supervisor Log File Permission Denied
**Symptom**:
```
PermissionError: [Errno 13] Permission denied: '/var/log/supervisor/supervisord.log'
```
**Ursache**: Supervisor kann nicht in `/var/log/supervisor/` schreiben, selbst als root.
**Lösung**: `supervisord.conf` ändern:
```ini
silent=false
logfile=/dev/null
logfile_maxbytes=0
```
**Grund**: Python's logging library kann `/dev/stdout` oder `/proc/self/fd/1` nicht im append-mode öffnen. `/dev/null` + `silent=false` macht Supervisor's logging auf stdout/stderr.
### Problem 2: EACCES Errors in Web Container
**Symptom**:
```
CRIT could not write pidfile /var/run/supervisord.pid
spawnerr: unknown error making dispatchers for 'nginx': EACCES
```
**Ursache**: Web container läuft nicht als root, sondern mit inherited user von base config.
**Lösung**: `docker-compose.prod.yml` - `user: root` setzen:
```yaml
web:
user: root
```
### Problem 3: Docker Entrypoint Override funktioniert nicht
**Symptom**: Container command zeigt entrypoint prepended:
```
/usr/local/bin/docker-entrypoint.sh /usr/bin/supervisord -c ...
```
**Ursache**: Base `docker-compose.yml` hat `web` service mit separate build context. Inherited ENTRYPOINT vom Base PHP-Image wird prepended.
**Lösung**: Explizit entrypoint clearen:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
pull_policy: always
entrypoint: [] # WICHTIG: Entrypoint clearen
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
### Problem 4: Queue Worker restarts kontinuierlich
**Symptom**:
```
docker ps # zeigt queue-worker als "Restarting"
```
**Ursache**: Base `docker-compose.yml` command sucht `/var/www/html/worker.php` das nicht existiert.
**Temporary Fix**: Service deaktivieren in `docker-compose.prod.yml`:
```yaml
queue-worker:
deploy:
replicas: 0
```
**Proper Fix**: Richtigen Worker-Command konfigurieren:
```yaml
queue-worker:
command: ["php", "/var/www/html/console.php", "queue:work"]
```
### Problem 5: HTTP Port 80 nicht erreichbar
**Symptom**: `curl http://94.16.110.151:8888/` → Connection refused
**Mögliche Ursachen**:
1. Nginx nicht auf Port 80 listening (nur 443)
2. Firewall blockiert Port 8888
3. Intentional (HTTPS-only Configuration)
**Debug**:
```bash
# Im Container checken
docker exec web netstat -tlnp | grep :80
# Nginx config testen
docker exec web nginx -t
# Nginx config anschauen
docker exec web cat /etc/nginx/http.d/default.conf
```
**Fix (falls HTTP→HTTPS Redirect gewünscht)**:
In `docker/nginx/default.production.conf`:
```nginx
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
```
---
## Monitoring
### Prometheus
**Zugang**: http://10.8.0.1:9090 (nur via WireGuard)
**Konfiguration**: `monitoring/prometheus/prometheus.yml`
**Scraped Targets**:
- Framework Application Metrics
- Container Metrics (cAdvisor)
- Node Exporter (Server Metrics)
### Grafana
**Zugang**: http://10.8.0.1:3000 (nur via WireGuard)
**Default Login**:
- User: `admin`
- Password: `${GRAFANA_PASSWORD}` (aus `.env`)
**Dashboards**: `monitoring/grafana/provisioning/dashboards/`
### Portainer
**Zugang**: https://10.8.0.1:9443 (nur via WireGuard)
**Features**:
- Container Management
- Stack Deployment
- Log Viewing
- Resource Usage
### Watchtower Auto-Update
Watchtower überwacht Container mit Label `com.centurylinklabs.watchtower.enable: "true"` und updated sie automatisch bei neuen Images.
**Konfiguration**:
```yaml
watchtower:
environment:
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_POLL_INTERVAL: 300 # 5 Minuten
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_NOTIFICATIONS: "shoutrrr"
WATCHTOWER_NOTIFICATION_URL: "${WATCHTOWER_NOTIFICATION_URL}"
```
**Monitoren**:
```bash
docker logs -f watchtower
```
---
## Maintenance
### Image Updates
**1. Lokal neues Image bauen**:
```bash
docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .
docker push 94.16.110.151:5000/framework:latest
```
**2. Auf Server**:
```bash
# Watchtower erkennt Update automatisch innerhalb von 5 Minuten
# Oder manuell:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
### Database Backups
```bash
# Manual Backup
docker exec db pg_dump -U framework_user framework_db > backup_$(date +%Y%m%d_%H%M%S).sql
# Automated (via cron)
0 2 * * * /home/deploy/scripts/backup-database.sh
```
### Log Rotation
Logs in `./storage/logs/` automatisch rotieren:
```bash
# /etc/logrotate.d/framework
/home/deploy/framework/storage/logs/*.log {
daily
rotate 14
compress
delaycompress
notifempty
missingok
create 0640 www-data www-data
}
```
### SSL Certificate Renewal
**Let's Encrypt** (automatisch via certbot):
```bash
certbot renew --deploy-hook "docker exec web nginx -s reload"
```
---
## Security Checklist
- [ ] WireGuard VPN konfiguriert und aktiv
- [ ] Firewall (UFW) konfiguriert und enabled
- [ ] Nur benötigte Ports offen (8443, 51820)
- [ ] Monitoring nur via VPN erreichbar (10.8.0.1:*)
- [ ] SSL/TLS Zertifikate gültig
- [ ] `.env` Secrets nicht in Git committed
- [ ] Database Credentials rotiert
- [ ] Redis Password gesetzt
- [ ] Docker Registry läuft lokal (nicht public)
- [ ] Container laufen mit minimal privileges
- [ ] Watchtower auto-updates aktiviert
- [ ] Backup-Strategie implementiert
- [ ] Log monitoring aktiv
---
## Performance Tuning
### PHP-FPM
`docker/php/zz-docker.production.conf`:
```ini
pm.max_children = 50 # Max. gleichzeitige Requests
pm.start_servers = 10 # Initial workers
pm.min_spare_servers = 5 # Min. idle workers
pm.max_spare_servers = 20 # Max. idle workers
pm.max_requests = 500 # Worker recycling
```
**Tuning basierend auf RAM**:
- 4GB RAM: max_children = 30
- 8GB RAM: max_children = 50
- 16GB RAM: max_children = 100
### OPcache
`docker/php/opcache.ini`:
```ini
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0 # Production: keine Timestamp-Checks
opcache.revalidate_freq=0
```
### Nginx
```nginx
worker_processes auto;
worker_connections 1024;
keepalive_timeout 65;
client_max_body_size 20M;
```
---
## Contact & Support
**Production Server**: 94.16.110.151
**VPN Gateway**: 10.8.0.1
**Documentation**: `/home/deploy/framework/docs/`
**Issue Tracker**: [GitHub/GitLab URL]
---
## Change Log
### 2025-10-28 - Initial Production Deployment
**Changes**:
- Supervisor logging: `/dev/null` + `silent=false`
- docker-compose.prod.yml: `user: root` für web, php, queue-worker
- docker-compose.prod.yml: `entrypoint: []` für web service
- docker-compose.prod.yml: `pull_policy: always` für registry images
**Deployed**:
- Image: `94.16.110.151:5000/framework:latest`
- Digest: `sha256:eee1db20b9293cf611f53d01de68e94df1cfb3c748fe967849e080d19b9e4c8b`
**Status**: ✅ Deployment erfolgreich, Container healthy

View File

@@ -0,0 +1,227 @@
# Quick Deploy Guide
Schnellanleitung für Production Deployments.
## Voraussetzungen
- WireGuard VPN aktiv: `sudo wg-quick up wg0-production`
- SSH-Zugang konfiguriert
- Docker Registry läuft auf Production Server
## Deployment in 5 Schritten
### 1. Image bauen und pushen
```bash
# Im Projekt-Root
docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .
docker push 94.16.110.151:5000/framework:latest
```
**Verify Push**:
```bash
curl http://94.16.110.151:5000/v2/framework/tags/list
```
### 2. Config-Files auf Server kopieren
```bash
# Falls docker-compose.prod.yml oder .env geändert wurden
scp docker-compose.prod.yml deploy@94.16.110.151:/home/deploy/framework/
scp .env.production deploy@94.16.110.151:/home/deploy/framework/.env
```
### 3. Auf Server deployen
```bash
ssh deploy@94.16.110.151
cd /home/deploy/framework
# Pull und Deploy
docker-compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
### 4. Status checken
```bash
# Container Status
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
# Logs anschauen
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs -f web php
# Supervisor Status (im web container)
docker exec web supervisorctl status
```
### 5. Application testen
```bash
# Von lokaler Maschine (via WireGuard)
curl -k -I https://94.16.110.151:8443/
# Erwartetes Ergebnis:
# HTTP/2 200
# server: nginx
```
## Rollback
Falls Probleme auftreten:
```bash
# Auf Server
cd /home/deploy/framework
# Vorheriges Image ID finden
docker images 94.16.110.151:5000/framework
# Zu spezifischem Image wechseln
docker-compose -f docker-compose.yml -f docker-compose.prod.yml down
docker tag 94.16.110.151:5000/framework@sha256:<old-digest> 94.16.110.151:5000/framework:latest
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
## Monitoring URLs
**Zugang nur via WireGuard VPN (10.8.0.1)**:
- Prometheus: http://10.8.0.1:9090
- Grafana: http://10.8.0.1:3000 (admin / $GRAFANA_PASSWORD)
- Portainer: https://10.8.0.1:9443
## Watchtower Auto-Updates
Watchtower überwacht automatisch und updated Container mit neuem Image (alle 5 Minuten).
**Status checken**:
```bash
docker logs watchtower
```
**Manuell triggern**:
```bash
# Watchtower neu starten (triggert sofortigen Check)
docker restart watchtower
```
## Troubleshooting
### Container nicht healthy
```bash
# Logs anschauen
docker logs web
docker logs php
# Im Container debuggen
docker exec -it web sh
docker exec -it php sh
# Supervisor Status
docker exec web supervisorctl status
# Nginx/PHP-FPM Prozesse
docker exec web ps aux | grep -E 'nginx|php-fpm'
```
### Database Connection Issues
```bash
# PostgreSQL Connection testen
docker exec php php -r "new PDO('pgsql:host=db;dbname=framework_db', 'framework_user', 'password');"
# Database Logs
docker logs db
# In Database connecten
docker exec -it db psql -U framework_user -d framework_db
```
### Redis Connection Issues
```bash
# Redis Connection testen
docker exec php php -r "var_dump((new Redis())->connect('redis', 6379));"
# Redis Logs
docker logs redis
# Redis CLI
docker exec -it redis redis-cli
```
## Maintenance Commands
### Database Backup
```bash
# Manual Backup
docker exec db pg_dump -U framework_user framework_db > backup_$(date +%Y%m%d_%H%M%S).sql
```
### Logs Cleanup
```bash
# Storage Logs leeren (auf Server)
docker exec web sh -c 'rm -rf /var/www/html/storage/logs/*.log'
# Docker Logs cleanup
docker system prune -f
docker volume prune -f
```
### Image Cleanup
```bash
# Alte Images entfernen
docker image prune -a -f
# Nur untagged images
docker image prune -f
```
## Performance Check
```bash
# Container Resource Usage
docker stats
# PHP-FPM Status
docker exec web curl http://localhost/php-fpm-status
# Nginx Status
docker exec web curl http://localhost/nginx-status
# Database Connections
docker exec db psql -U framework_user -d framework_db -c "SELECT count(*) FROM pg_stat_activity;"
```
## SSL Certificate Renewal
```bash
# Let's Encrypt Renewal (auf Server als root)
certbot renew
docker exec web nginx -s reload
```
## Nützliche Aliases
Füge zu `~/.bashrc` auf Production Server hinzu:
```bash
alias dc='docker-compose -f docker-compose.yml -f docker-compose.prod.yml'
alias dcup='dc up -d'
alias dcdown='dc down'
alias dcps='dc ps'
alias dclogs='dc logs -f'
alias dcrestart='dc restart'
```
Dann kannst du einfach verwenden:
```bash
dcup # Deploy
dcps # Status
dclogs # Logs anschauen
```

View File

@@ -0,0 +1,581 @@
# Production Deployment Troubleshooting Checklist
Systematische Problemlösung für häufige Deployment-Issues.
## Issue 1: Supervisor Log File Permission Denied
### Symptom
```
PermissionError: [Errno 13] Permission denied: '/var/log/supervisor/supervisord.log'
```
Container startet nicht, Supervisor kann Logfile nicht schreiben.
### Diagnose
```bash
docker logs web # Zeigt Permission Error
docker exec web ls -la /var/log/supervisor/ # Directory existiert nicht oder keine Permissions
```
### Root Cause
- Supervisor versucht in `/var/log/supervisor/supervisord.log` zu schreiben
- Directory existiert nicht oder keine Write-Permissions
- Auch als root problematisch in containerisierter Umgebung
### Lösung 1 (FUNKTIONIERT NICHT)
**Versuch**: `/proc/self/fd/1` verwenden
`docker/supervisor/supervisord.conf`:
```ini
logfile=/proc/self/fd/1
```
**Fehler**: `PermissionError: [Errno 13] Permission denied: '/proc/self/fd/1'`
**Grund**: Python's logging library (verwendet von Supervisor) kann `/proc/self/fd/1` oder `/dev/stdout` nicht im append-mode öffnen.
### Lösung 2 (ERFOLGREICH)
**Fix**: `/dev/null` mit `silent=false`
`docker/supervisor/supervisord.conf`:
```ini
[supervisord]
nodaemon=true
silent=false # WICHTIG: Logging trotz /dev/null
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
loglevel=info
```
**Warum funktioniert das?**
- `logfile=/dev/null`: Kein File-Logging
- `silent=false`: Supervisor loggt nach stdout/stderr
- Logs erscheinen in `docker logs web`
### Verification
```bash
docker logs web
# Output:
# 2025-10-28 16:29:59,976 INFO supervisord started with pid 1
# 2025-10-28 16:30:00,980 INFO spawned: 'nginx' with pid 7
# 2025-10-28 16:30:00,982 INFO spawned: 'php-fpm' with pid 8
# 2025-10-28 16:30:02,077 INFO success: nginx entered RUNNING state
# 2025-10-28 16:30:02,077 INFO success: php-fpm entered RUNNING state
```
### Related Files
- `docker/supervisor/supervisord.conf`
- `Dockerfile.production` (COPY supervisord.conf)
---
## Issue 2: Web Container EACCES Errors
### Symptom
```
2025-10-28 16:16:52,152 CRIT could not write pidfile /var/run/supervisord.pid
2025-10-28 16:16:53,154 INFO spawnerr: unknown error making dispatchers for 'nginx': EACCES
2025-10-28 16:16:53,154 INFO spawnerr: unknown error making dispatchers for 'php-fpm': EACCES
```
### Diagnose
```bash
# Container User checken
docker exec web whoami
# Falls nicht "root", dann ist das der Issue
# Docker Compose Config checken
docker inspect web | grep -i user
# Zeigt inherited user von base config
```
### Root Cause
- `web` service in `docker-compose.prod.yml` hat **kein** `user: root` gesetzt
- Inherited `user: 1000:1000` oder `user: www-data` von base `docker-compose.yml`
- Supervisor benötigt root um nginx/php-fpm master processes zu starten
### Lösung
**Fix**: `user: root` explizit setzen
`docker-compose.prod.yml`:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
user: root # ← HINZUFÜGEN
# ... rest der config
```
Auch für `php` und `queue-worker` services hinzufügen:
```yaml
php:
image: 94.16.110.151:5000/framework:latest
user: root # ← HINZUFÜGEN
queue-worker:
image: 94.16.110.151:5000/framework:latest
user: root # ← HINZUFÜGEN
```
### Warum user: root?
- **Container läuft als root**: Supervisor master process
- **Nginx master**: root (worker processes als www-data via nginx.conf)
- **PHP-FPM master**: root (pool workers als www-data via php-fpm.conf)
`docker/php/zz-docker.production.conf`:
```ini
[www]
user = www-data # ← Worker processes laufen als www-data
group = www-data
```
### Verification
```bash
docker exec web whoami
# root
docker exec web ps aux | grep -E 'nginx|php-fpm'
# root 1 supervisord
# root 7 nginx: master process
# www-data 10 nginx: worker process
# root 8 php-fpm: master process
# www-data 11 php-fpm: pool www
```
### Related Files
- `docker-compose.prod.yml` (web, php, queue-worker services)
- `docker/php/zz-docker.production.conf`
- `docker/nginx/nginx.production.conf`
---
## Issue 3: Docker Entrypoint Override funktioniert nicht
### Symptom
Container command zeigt Entrypoint prepended:
```bash
docker ps
# COMMAND: "/usr/local/bin/docker-entrypoint.sh /usr/bin/supervisord -c ..."
```
Supervisor wird nicht direkt gestartet, sondern durch einen wrapper script.
### Diagnose
```bash
# Container Command checken
docker inspect web --format='{{.Config.Entrypoint}}'
# [/usr/local/bin/docker-entrypoint.sh]
docker inspect web --format='{{.Config.Cmd}}'
# [/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf]
```
### Root Cause
1. Base `docker-compose.yml` hat `web` service mit separate build:
```yaml
web:
build:
context: docker/nginx
dockerfile: Dockerfile
```
2. Production override setzt `image:` aber cleared **nicht** den inherited ENTRYPOINT:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
3. Base PHP image hat ENTRYPOINT der prepended wird
4. Docker Compose merge: ENTRYPOINT + CMD = final command
### Lösung - Iteration 1 (FUNKTIONIERT NICHT)
❌ **Versuch**: Nur `command:` setzen
`docker-compose.prod.yml`:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
**Result**: Entrypoint wird trotzdem prepended
### Lösung - Iteration 2 (FUNKTIONIERT NICHT)
❌ **Versuch**: `pull_policy: always` hinzufügen
`docker-compose.prod.yml`:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
pull_policy: always # Force registry pull
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
```
**Result**: Image wird von Registry gepullt, aber Entrypoint wird trotzdem prepended
### Lösung - Iteration 3 (ERFOLGREICH)
✅ **Fix**: `entrypoint: []` explizit clearen
`docker-compose.prod.yml`:
```yaml
web:
image: 94.16.110.151:5000/framework:latest
pull_policy: always # Always pull from registry, never build
entrypoint: [] # ← WICHTIG: Entrypoint clearen
command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
user: root
```
**Warum `entrypoint: []`?**
- Leeres Array cleared den inherited entrypoint komplett
- `command:` wird dann direkt als PID 1 gestartet
- Keine wrapper scripts, keine indirection
### Verification
```bash
docker inspect web --format='{{.Config.Entrypoint}}'
# [] ← Leer!
docker inspect web --format='{{.Config.Cmd}}'
# [/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf]
docker exec web ps aux
# PID 1: /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
# Kein entrypoint wrapper!
```
### Related Files
- `docker-compose.prod.yml` (web service)
### Docker Compose Override Rules
```
Base Config + Override = Final Config
Base:
web:
build: docker/nginx
→ inherited ENTRYPOINT from base image
Override (insufficient):
web:
image: 94.16.110.151:5000/framework:latest
command: [...]
→ ENTRYPOINT still prepended to command
Override (correct):
web:
image: 94.16.110.151:5000/framework:latest
entrypoint: [] ← Clears inherited entrypoint
command: [...] ← Runs directly as PID 1
```
---
## Issue 4: Queue Worker Container Restarts
### Symptom
```bash
docker ps
# queue-worker Restarting (1) 5 seconds ago
```
Container restart loop, nie healthy.
### Diagnose
```bash
docker logs queue-worker
# Error: /var/www/html/worker.php not found
# oder
# php: command not found
```
### Root Cause
Base `docker-compose.yml` hat Queue Worker Command für Development:
```yaml
queue-worker:
command: ["php", "/var/www/html/worker.php"]
```
`worker.php` existiert nicht im Production Image.
### Lösung - Option 1: Service deaktivieren
✅ **Quick Fix**: Queue Worker deaktivieren
`docker-compose.prod.yml`:
```yaml
queue-worker:
deploy:
replicas: 0 # Disable service
```
### Lösung - Option 2: Richtigen Command setzen
✅ **Proper Fix**: Console Command verwenden
`docker-compose.prod.yml`:
```yaml
queue-worker:
image: 94.16.110.151:5000/framework:latest
user: root
command: ["php", "/var/www/html/console.php", "queue:work"]
# oder für Supervisor-managed:
# entrypoint: []
# command: ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/queue-worker-supervisord.conf"]
```
### Verification
```bash
docker logs queue-worker
# [timestamp] INFO Queue worker started
# [timestamp] INFO Processing job: ...
```
### Related Files
- `docker-compose.yml` (base queue-worker definition)
- `docker-compose.prod.yml` (production override)
- `console.php` (framework console application)
---
## Issue 5: HTTP Port 80 nicht erreichbar
### Symptom
```bash
curl http://94.16.110.151:8888/
# curl: (7) Failed to connect to 94.16.110.151 port 8888: Connection refused
docker exec web curl http://localhost/
# curl: (7) Failed to connect to localhost port 80: Connection refused
```
### Diagnose
```bash
# Nginx listening ports checken
docker exec web netstat -tlnp | grep nginx
# Zeigt nur: 0.0.0.0:443
# Nginx Config checken
docker exec web cat /etc/nginx/http.d/default.conf
# Kein "listen 80;" block
```
### Root Cause - Option 1: Intentional HTTPS-only
Möglicherweise ist HTTP absichtlich disabled (Security Best Practice).
### Root Cause - Option 2: Missing HTTP Block
Nginx config hat keinen HTTP listener, nur HTTPS.
### Lösung - HTTP→HTTPS Redirect hinzufügen
✅ **Fix**: HTTP Redirect konfigurieren
`docker/nginx/default.production.conf`:
```nginx
# HTTP → HTTPS Redirect
server {
listen 80;
server_name _;
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Server
server {
listen 443 ssl http2;
server_name _;
ssl_certificate /var/www/ssl/cert.pem;
ssl_certificate_key /var/www/ssl/key.pem;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
```
### Verification
```bash
curl -I http://94.16.110.151:8888/
# HTTP/1.1 301 Moved Permanently
# Location: https://94.16.110.151:8888/
curl -k -I https://94.16.110.151:8443/
# HTTP/2 200
# server: nginx
```
### Related Files
- `docker/nginx/default.production.conf`
- `Dockerfile.production` (COPY nginx config)
---
## General Debugging Commands
### Container Inspection
```bash
# Alle Container Status
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
# Container Details
docker inspect web
# Container Logs
docker logs -f web
docker logs --tail 100 web
# Inside Container
docker exec -it web sh
docker exec -it php sh
```
### Supervisor Debugging
```bash
# Supervisor Status
docker exec web supervisorctl status
# Supervisor Logs
docker exec web tail -f /dev/null # Logs gehen nach stdout/stderr
# Supervisor Config testen
docker exec web supervisord -c /etc/supervisor/conf.d/supervisord.conf -n
```
### Nginx Debugging
```bash
# Nginx Config testen
docker exec web nginx -t
# Nginx reload
docker exec web nginx -s reload
# Nginx listening ports
docker exec web netstat -tlnp | grep nginx
# Nginx processes
docker exec web ps aux | grep nginx
```
### PHP-FPM Debugging
```bash
# PHP-FPM Status
docker exec web curl http://localhost/php-fpm-status
# PHP-FPM Config testen
docker exec web php-fpm -t
# PHP-FPM processes
docker exec web ps aux | grep php-fpm
# PHP Version
docker exec web php -v
# PHP Modules
docker exec web php -m
```
### Network Debugging
```bash
# Port listening
docker exec web netstat -tlnp
# DNS resolution
docker exec web nslookup db
docker exec web nslookup redis
# Network connectivity
docker exec web ping db
docker exec web ping redis
# HTTP request
docker exec web curl http://localhost/
```
### Database Debugging
```bash
# PostgreSQL Connection
docker exec php php -r "new PDO('pgsql:host=db;dbname=framework_db', 'framework_user', 'password');"
# Database Logs
docker logs db
# Connect to DB
docker exec -it db psql -U framework_user -d framework_db
# Check connections
docker exec db psql -U framework_user -d framework_db -c "SELECT count(*) FROM pg_stat_activity;"
```
### Performance Monitoring
```bash
# Container Resource Usage
docker stats
# Disk Usage
docker system df
# Image Sizes
docker images
# Volume Sizes
docker system df -v
```
---
## Checklist für erfolgreichen Deploy
### Pre-Deployment
- [ ] Image gebaut: `docker build -f Dockerfile.production -t 94.16.110.151:5000/framework:latest .`
- [ ] Image gepusht: `docker push 94.16.110.151:5000/framework:latest`
- [ ] Registry verfügbar: `curl http://94.16.110.151:5000/v2/_catalog`
- [ ] WireGuard VPN aktiv: `wg show`
- [ ] `.env.production` auf Server aktuell
- [ ] `docker-compose.prod.yml` auf Server aktuell
### Deployment
- [ ] SSH auf Server: `ssh deploy@94.16.110.151`
- [ ] Image pullen: `docker-compose -f docker-compose.yml -f docker-compose.prod.yml pull`
- [ ] Stack starten: `docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d`
### Post-Deployment Verification
- [ ] Container laufen: `docker-compose ps` zeigt alle "Up (healthy)"
- [ ] Supervisor Status: `docker exec web supervisorctl status` zeigt nginx/php-fpm RUNNING
- [ ] Nginx lauscht: `docker exec web netstat -tlnp | grep :443`
- [ ] PHP-FPM lauscht: `docker exec web netstat -tlnp | grep :9000`
- [ ] Application erreichbar: `curl -k -I https://94.16.110.151:8443/` → HTTP/2 200
- [ ] Database erreichbar: `docker exec php php -r "new PDO(...);"`
- [ ] Redis erreichbar: `docker exec php php -r "new Redis()->connect('redis', 6379);"`
- [ ] Logs sauber: `docker logs web` zeigt keine Errors
### Monitoring
- [ ] Prometheus: http://10.8.0.1:9090 erreichbar
- [ ] Grafana: http://10.8.0.1:3000 erreichbar
- [ ] Portainer: https://10.8.0.1:9443 erreichbar
- [ ] Watchtower aktiv: `docker logs watchtower` zeigt Checks
---
## Quick Reference
### Häufigste Fehlerursachen
1. **Supervisor Logging**: Verwende `logfile=/dev/null` + `silent=false`
2. **User Permissions**: Setze `user: root` in docker-compose.prod.yml
3. **Entrypoint Override**: Setze `entrypoint: []` um inherited entrypoint zu clearen
4. **Pull Policy**: Verwende `pull_policy: always` um registry image zu forcen
### Wichtigste Config-Änderungen
- `docker/supervisor/supervisord.conf`: `logfile=/dev/null`, `silent=false`
- `docker-compose.prod.yml`: `user: root`, `entrypoint: []`, `pull_policy: always`
- `docker/php/zz-docker.production.conf`: `user = www-data`, `group = www-data`