801 lines
19 KiB
Markdown
801 lines
19 KiB
Markdown
# 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
|