Files
AFC-Demo/docs/deployment-guide.md
T

1036 lines
22 KiB
Markdown
Raw Normal View History

2025-12-03 12:59:30 +01:00
# Deployment-Guide: Risk Intelligence Platform
## 📋 Inhaltsverzeichnis
1. [Übersicht](#übersicht)
2. [Voraussetzungen](#voraussetzungen)
3. [Docker Deployment](#docker-deployment)
4. [Kubernetes Deployment](#kubernetes-deployment)
5. [Production Checklist](#production-checklist)
6. [Environment Configuration](#environment-configuration)
7. [Database Migration](#database-migration)
8. [Monitoring & Logging](#monitoring--logging)
9. [Backup & Recovery](#backup--recovery)
10. [Rollback-Strategie](#rollback-strategie)
11. [Troubleshooting](#troubleshooting)
---
## Übersicht
Die Risk Intelligence Platform kann auf verschiedene Arten deployed werden:
- **Docker** - Standalone Container oder Docker Compose
- **Kubernetes** - Production-ready mit CronJobs für Scheduled Tasks
- **Traditional** - Apache/Nginx + PHP-FPM auf Linux-Server
### Deployment-Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ Production Environment │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Nginx │ │ PHP-FPM │ │ Supervisor │ │
│ │ (Port 80) │ │ (Laravel) │ │ (Queue) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────────┴─────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ PostgreSQL Database │ │
│ │ (Public + Backend Schema) │ │
│ └────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Kubernetes CronJob (Laravel Scheduler) │ │
│ │ - SyncBackendDataPool (every 6h) │ │
│ │ - TransformDataPool (every 6h at :30) │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## Voraussetzungen
### System Requirements
| Komponente | Minimum | Empfohlen |
|------------|---------|-----------|
| **CPU** | 2 Cores | 4 Cores |
| **RAM** | 2 GB | 4 GB |
| **Disk** | 20 GB | 50 GB SSD |
| **OS** | Linux (Ubuntu 22.04+) | Alpine Linux 3.22 |
### Software Requirements
| Software | Version | Zweck |
|----------|---------|-------|
| **PHP** | 8.2+ | Runtime |
| **Composer** | 2.0+ | Dependencies |
| **Node.js** | 18+ | Asset Building |
| **PostgreSQL** | 14+ | Database |
| **Nginx** | 1.24+ | Web Server |
| **Supervisor** | 4.0+ | Process Management |
| **Docker** | 24.0+ | Containerization (optional) |
| **Kubernetes** | 1.28+ | Orchestration (optional) |
---
## Docker Deployment
### Multi-Stage Dockerfile
Die Applikation nutzt einen **Multi-Stage Build** für optimale Image-Größe.
```dockerfile
# Stage 1: Composer Dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
# Stage 2: Runtime
FROM alpine:3.22
# Install PHP 8.2, Nginx, Supervisor, PostgreSQL Client
RUN apk add --no-cache \
nginx supervisor php82 php82-fpm \
php82-pgsql php82-pdo_pgsql \
nodejs npm
# Copy vendor from build stage
COPY --from=vendor /app/vendor ./vendor
COPY . ./
# Build frontend assets
RUN npm ci && npm run build
# Optimize Laravel
RUN php artisan config:cache && \
php artisan route:cache && \
php artisan view:cache
EXPOSE 80
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]
```
### Build & Run
```bash
# Build Image
docker build -t risk-platform:latest .
# Run Container
docker run -d \
--name risk-platform \
-p 8080:80 \
-v $(pwd)/.env:/var/www/html/.env \
-v $(pwd)/storage:/var/www/html/storage \
risk-platform:latest
```
### Docker Compose
```yaml
# docker-compose.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: risk-platform
restart: unless-stopped
ports:
- "8080:80"
volumes:
- .env:/var/www/html/.env
- ./storage:/var/www/html/storage
depends_on:
- postgres
networks:
- risk-net
postgres:
image: postgres:16-alpine
container_name: risk-postgres
restart: unless-stopped
environment:
POSTGRES_DB: risk_platform_db
POSTGRES_USER: risk_user
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- risk-net
scheduler:
build:
context: .
dockerfile: Dockerfile
container_name: risk-scheduler
restart: unless-stopped
command: ["php", "artisan", "schedule:work"]
volumes:
- .env:/var/www/html/.env
depends_on:
- postgres
networks:
- risk-net
volumes:
postgres-data:
networks:
risk-net:
driver: bridge
```
**Starten:**
```bash
docker-compose up -d
```
---
## Kubernetes Deployment
### Namespace erstellen
```yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: risk-platform
```
```bash
kubectl apply -f namespace.yaml
```
### ConfigMap für Environment
```yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: risk-platform-config
namespace: risk-platform
data:
env: |
APP_NAME="Risk Intelligence Platform"
APP_ENV=production
APP_KEY=base64:xxx
APP_DEBUG=false
APP_URL=https://risk.example.com
DB_CONNECTION2=pgsql
DB_HOST2=postgres-service
DB_PORT2=5432
DB_DATABASE2=risk_platform_db
DB_USERNAME2=risk_user
DB_PASSWORD2=xxx
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=redis-service
REDIS_PORT=6379
```
```bash
kubectl apply -f configmap.yaml
```
### Deployment
```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: risk-platform
namespace: risk-platform
labels:
app: risk-platform
spec:
replicas: 2
selector:
matchLabels:
app: risk-platform
template:
metadata:
labels:
app: risk-platform
spec:
containers:
- name: app
image: registry.example.com/risk-platform:latest
imagePullPolicy: Always
ports:
- containerPort: 80
name: http
env:
- name: APP_ENV
value: "production"
volumeMounts:
- name: config
mountPath: /var/www/html/.env
subPath: env
- name: storage
mountPath: /var/www/html/storage
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: config
configMap:
name: risk-platform-config
- name: storage
persistentVolumeClaim:
claimName: risk-platform-storage
```
```bash
kubectl apply -f deployment.yaml
```
### Service
```yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: risk-platform-service
namespace: risk-platform
spec:
selector:
app: risk-platform
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
```
### Ingress (Optional)
```yaml
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: risk-platform-ingress
namespace: risk-platform
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- risk.example.com
secretName: risk-platform-tls
rules:
- host: risk.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: risk-platform-service
port:
number: 80
```
### CronJob für Laravel Scheduler
```yaml
# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: sync-backend-data-pool
namespace: risk-platform
spec:
schedule: "0 */6 * * *" # Every 6 hours
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
metadata:
labels:
app: sync-backend-data-pool
spec:
restartPolicy: OnFailure
containers:
- name: sync
image: registry.example.com/risk-platform:latest
command: ["php", "artisan", "schedule:run"]
volumeMounts:
- name: config
mountPath: /var/www/html/.env
subPath: env
volumes:
- name: config
configMap:
name: risk-platform-config
```
```bash
kubectl apply -f cronjob.yaml
```
### Alle Ressourcen deployen
```bash
# Complete Deployment
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl apply -f cronjob.yaml
# Status prüfen
kubectl get all -n risk-platform
# Logs anschauen
kubectl logs -f deployment/risk-platform -n risk-platform
```
---
## Production Checklist
### Pre-Deployment
- [ ] **Environment Configuration**
- [ ] `.env` auf `production` gesetzt
- [ ] `APP_DEBUG=false`
- [ ] `APP_KEY` generiert
- [ ] Sichere Passwörter für DB
- [ ] **Database**
- [ ] PostgreSQL läuft
- [ ] Schemas erstellt (public, backend)
- [ ] User-Rechte vergeben
- [ ] Backup-Strategie definiert
- [ ] **Dependencies**
- [ ] `composer install --no-dev --optimize-autoloader`
- [ ] `npm ci && npm run build`
- [ ] **Laravel Optimization**
- [ ] `php artisan config:cache`
- [ ] `php artisan route:cache`
- [ ] `php artisan view:cache`
- [ ] `php artisan event:cache`
- [ ] **Security**
- [ ] HTTPS aktiviert (SSL/TLS)
- [ ] Firewall konfiguriert
- [ ] `.env` nicht im Git
- [ ] Secrets in Vault/ConfigMap
### Post-Deployment
- [ ] **Migrations**
- [ ] `php artisan migrate --force`
- [ ] Backup vor Migration
- [ ] **Tests**
- [ ] Health Check (`/`)
- [ ] Login funktioniert
- [ ] Database Connection
- [ ] Scheduled Jobs
- [ ] **Monitoring**
- [ ] Logs werden geschrieben
- [ ] Metrics werden gesammelt
- [ ] Alerts konfiguriert
- [ ] **Performance**
- [ ] OPcache aktiviert
- [ ] Redis für Cache/Session
- [ ] Database Indizes
---
## Environment Configuration
### Production .env
```ini
# Application
APP_NAME="Risk Intelligence Platform"
APP_ENV=production
APP_KEY=base64:xxx # Generate with: php artisan key:generate
APP_DEBUG=false
APP_URL=https://risk.example.com
# Locale
APP_LOCALE=de
APP_FALLBACK_LOCALE=en
# Database (PostgreSQL)
DB_CONNECTION2=pgsql
DB_HOST2=postgres-host
DB_PORT2=5432
DB_DATABASE2=risk_platform_db
DB_USERNAME2=risk_user
DB_PASSWORD2=xxx # Use strong password!
# Cache & Session (Redis)
CACHE_STORE=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120
# Queue
QUEUE_CONNECTION=redis
# Redis
REDIS_HOST=redis-host
REDIS_PASSWORD=null
REDIS_PORT=6379
# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=noreply@example.com
MAIL_PASSWORD=xxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="${APP_NAME}"
# Logging
LOG_CHANNEL=stack
LOG_LEVEL=error # Production: error, nicht debug!
# Security
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=strict
```
### Secrets Management
**Kubernetes Secrets:**
```bash
# Create Secret
kubectl create secret generic risk-platform-secrets \
--from-literal=APP_KEY=base64:xxx \
--from-literal=DB_PASSWORD=xxx \
--from-literal=REDIS_PASSWORD=xxx \
-n risk-platform
# Use in Deployment
env:
- name: APP_KEY
valueFrom:
secretKeyRef:
name: risk-platform-secrets
key: APP_KEY
```
---
## Database Migration
### Pre-Migration Backup
```bash
# PostgreSQL Backup
pg_dump -h localhost -U risk_user -d risk_platform_db > backup_$(date +%Y%m%d_%H%M%S).sql
# Verify Backup
psql -h localhost -U risk_user -d risk_platform_db_test < backup_xxx.sql
```
### Run Migrations
```bash
# Production Migration
php artisan migrate --force
# With Output
php artisan migrate --force --verbose
# Rollback (if needed)
php artisan migrate:rollback --force
```
### Zero-Downtime Migration
```bash
# 1. Maintenance Mode
php artisan down --message="Updating..." --retry=60
# 2. Pull latest code
git pull origin main
# 3. Update dependencies
composer install --no-dev --optimize-autoloader
# 4. Run migrations
php artisan migrate --force
# 5. Clear & cache
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
# 6. Restart services
sudo supervisorctl restart all
# 7. Exit maintenance mode
php artisan up
```
---
## Monitoring & Logging
### Application Logs
```bash
# Laravel Logs
tail -f storage/logs/laravel.log
# Nur Errors
tail -f storage/logs/laravel.log | grep ERROR
# Mit Laravel Pail
php artisan pail --filter=error
```
### Nginx Logs
```bash
# Access Log
tail -f /var/log/nginx/access.log
# Error Log
tail -f /var/log/nginx/error.log
```
### Kubernetes Logs
```bash
# Pod Logs
kubectl logs -f deployment/risk-platform -n risk-platform
# Alle Pods
kubectl logs -f -l app=risk-platform -n risk-platform
# CronJob Logs
kubectl logs -f cronjob/sync-backend-data-pool -n risk-platform
```
### Health Checks
```bash
# Application Health
curl https://risk.example.com/
# Database Connection
php artisan tinker
>>> DB::connection()->getPdo()
# Queue Status
php artisan queue:monitor
# Scheduler Status
php artisan schedule:list
```
### Monitoring-Tools (Optional)
**Laravel Telescope:**
```bash
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
```
**Prometheus + Grafana:**
- Metrics-Export via Laravel Package
- Custom Dashboards für Transactions, Companies
- Alerts bei hoher Error-Rate
---
## Backup & Recovery
### Automated Backup Script
```bash
#!/bin/bash
# backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
DB_NAME="risk_platform_db"
# Database Backup
pg_dump -h localhost -U risk_user $DB_NAME > $BACKUP_DIR/db_$DATE.sql
# Storage Backup
tar -czf $BACKUP_DIR/storage_$DATE.tar.gz storage/
# Keep only last 7 days
find $BACKUP_DIR -type f -mtime +7 -delete
echo "Backup completed: $DATE"
```
**Crontab:**
```bash
# Daily backup at 2 AM
0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
```
### Kubernetes Backup
```yaml
# backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
namespace: risk-platform
spec:
schedule: "0 2 * * *" # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:16-alpine
command:
- /bin/sh
- -c
- |
pg_dump -h postgres-service -U risk_user risk_platform_db > /backup/db_$(date +%Y%m%d).sql
volumeMounts:
- name: backup-storage
mountPath: /backup
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: backup-pvc
restartPolicy: OnFailure
```
### Recovery Procedure
```bash
# 1. Stop Application
kubectl scale deployment risk-platform --replicas=0 -n risk-platform
# 2. Restore Database
psql -h localhost -U risk_user -d risk_platform_db < backup_xxx.sql
# 3. Verify Data
psql -h localhost -U risk_user -d risk_platform_db
\dt
SELECT COUNT(*) FROM companies;
# 4. Restart Application
kubectl scale deployment risk-platform --replicas=2 -n risk-platform
```
---
## Rollback-Strategie
### Git-based Rollback
```bash
# 1. Identify previous version
git log --oneline -10
# 2. Checkout previous version
git checkout <commit-hash>
# 3. Rebuild & Deploy
docker build -t risk-platform:rollback .
docker push registry.example.com/risk-platform:rollback
# 4. Update Kubernetes
kubectl set image deployment/risk-platform \
app=registry.example.com/risk-platform:rollback \
-n risk-platform
```
### Database Rollback
```bash
# Rollback last migration
php artisan migrate:rollback --step=1 --force
# Rollback specific batch
php artisan migrate:rollback --batch=3 --force
# Full rollback to fresh state (CAUTION!)
php artisan migrate:fresh --force
```
### Blue-Green Deployment
```yaml
# deployment-blue.yaml (current)
metadata:
name: risk-platform-blue
labels:
app: risk-platform
version: blue
# deployment-green.yaml (new version)
metadata:
name: risk-platform-green
labels:
app: risk-platform
version: green
# Switch Traffic
# Change service selector from blue to green
kubectl patch service risk-platform-service \
-p '{"spec":{"selector":{"version":"green"}}}' \
-n risk-platform
```
---
## Troubleshooting
### Problem: Container startet nicht
**Diagnose:**
```bash
docker logs risk-platform
kubectl logs deployment/risk-platform -n risk-platform
```
**Häufige Ursachen:**
- `.env` fehlt oder ungültig
- Database Connection fehlgeschlagen
- Permissions auf `storage/` fehlen
**Lösung:**
```bash
# Check .env
docker exec -it risk-platform cat /var/www/html/.env
# Fix permissions
docker exec -it risk-platform chown -R nginx:nginx storage
```
---
### Problem: "500 Internal Server Error"
**Diagnose:**
```bash
# Laravel Logs
tail -f storage/logs/laravel.log
# Nginx Error Log
tail -f /var/log/nginx/error.log
```
**Häufige Ursachen:**
- `APP_KEY` nicht gesetzt
- Config Cache veraltet
- Database Connection Error
**Lösung:**
```bash
# Generate APP_KEY
php artisan key:generate --force
# Clear caches
php artisan optimize:clear
# Test DB connection
php artisan tinker
>>> DB::connection()->getPdo()
```
---
### Problem: Scheduled Jobs laufen nicht
**Diagnose:**
```bash
# Check CronJob Status
kubectl get cronjobs -n risk-platform
# Check Job History
kubectl get jobs -n risk-platform
# Check Logs
kubectl logs job/sync-backend-data-pool-xxx -n risk-platform
```
**Lösung:**
```bash
# Test Schedule manually
php artisan schedule:run
# Test specific Job
php artisan backend:sync-data-pool --incremental
```
---
### Problem: High Memory Usage
**Diagnose:**
```bash
# Container Stats
docker stats risk-platform
# Pod Resource Usage
kubectl top pod -n risk-platform
```
**Lösung:**
- Reduce batch size in Jobs
- Increase memory limits
- Enable OPcache
- Use Redis for cache/session
---
## Performance Optimization
### OPcache Configuration
```ini
; /etc/php82/conf.d/opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
```
### Redis Configuration
```yaml
# redis.conf
maxmemory 512mb
maxmemory-policy allkeys-lru
```
### Database Optimization
```sql
-- Index on frequently queried columns
CREATE INDEX idx_transactions_status ON transactions(status);
CREATE INDEX idx_transactions_risk_score ON transactions(risk_score);
CREATE INDEX idx_companies_kyc_risk_level ON companies(kyc_risk_level);
-- Vacuum regularly
VACUUM ANALYZE companies;
VACUUM ANALYZE transactions;
```
---
## Security Best Practices
### SSL/TLS
```yaml
# ingress.yaml with TLS
spec:
tls:
- hosts:
- risk.example.com
secretName: risk-platform-tls
```
### Firewall Rules
```bash
# Allow HTTP/HTTPS only
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
```
### Security Headers
```nginx
# nginx.conf
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
```
---
## Zusammenfassung
### Deployment-Optionen
| Option | Komplexität | Skalierbarkeit | Empfohlen für |
|--------|-------------|----------------|---------------|
| **Docker** | Niedrig | Mittel | Development, Small Production |
| **Kubernetes** | Hoch | Hoch | Production, Enterprise |
| **Traditional** | Mittel | Niedrig | Legacy Systems |
### Checkliste für Production
✅ Environment auf `production` gesetzt
`APP_DEBUG=false`
✅ HTTPS aktiviert
✅ Database-Backup automatisiert
✅ Monitoring & Logging konfiguriert
✅ Scheduled Jobs laufen
✅ Health Checks aktiv
✅ Secrets sicher gespeichert
✅ Rollback-Strategie getestet
---
**Erstellt:** 2025-11-24
**Version:** 1.0
**Autor:** Risk Intelligence Platform Team