Build and Push Docker Image only on Conventional Commits / build (push) Has been cancelled
22 KiB
22 KiB
Deployment-Guide: Risk Intelligence Platform
📋 Inhaltsverzeichnis
- Übersicht
- Voraussetzungen
- Docker Deployment
- Kubernetes Deployment
- Production Checklist
- Environment Configuration
- Database Migration
- Monitoring & Logging
- Backup & Recovery
- Rollback-Strategie
- 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.
# 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
# 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
# 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:
docker-compose up -d
Kubernetes Deployment
Namespace erstellen
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: risk-platform
kubectl apply -f namespace.yaml
ConfigMap für Environment
# 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
kubectl apply -f configmap.yaml
Deployment
# 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
kubectl apply -f deployment.yaml
Service
# 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)
# 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
# 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
kubectl apply -f cronjob.yaml
Alle Ressourcen deployen
# 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
.envaufproductiongesetztAPP_DEBUG=falseAPP_KEYgeneriert- 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-autoloadernpm ci && npm run build
-
Laravel Optimization
php artisan config:cachephp artisan route:cachephp artisan view:cachephp artisan event:cache
-
Security
- HTTPS aktiviert (SSL/TLS)
- Firewall konfiguriert
.envnicht 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
- Health Check (
-
Monitoring
- Logs werden geschrieben
- Metrics werden gesammelt
- Alerts konfiguriert
-
Performance
- OPcache aktiviert
- Redis für Cache/Session
- Database Indizes
Environment Configuration
Production .env
# 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:
# 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
# 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
# Production Migration
php artisan migrate --force
# With Output
php artisan migrate --force --verbose
# Rollback (if needed)
php artisan migrate:rollback --force
Zero-Downtime Migration
# 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
# 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
# Access Log
tail -f /var/log/nginx/access.log
# Error Log
tail -f /var/log/nginx/error.log
Kubernetes Logs
# 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
# 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:
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
#!/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:
# Daily backup at 2 AM
0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
Kubernetes Backup
# 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
# 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
# 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
# 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
# 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:
docker logs risk-platform
kubectl logs deployment/risk-platform -n risk-platform
Häufige Ursachen:
.envfehlt oder ungültig- Database Connection fehlgeschlagen
- Permissions auf
storage/fehlen
Lösung:
# 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:
# Laravel Logs
tail -f storage/logs/laravel.log
# Nginx Error Log
tail -f /var/log/nginx/error.log
Häufige Ursachen:
APP_KEYnicht gesetzt- Config Cache veraltet
- Database Connection Error
Lösung:
# 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:
# 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:
# Test Schedule manually
php artisan schedule:run
# Test specific Job
php artisan backend:sync-data-pool --incremental
Problem: High Memory Usage
Diagnose:
# 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
; /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
# redis.conf
maxmemory 512mb
maxmemory-policy allkeys-lru
Database Optimization
-- 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
# ingress.yaml with TLS
spec:
tls:
- hosts:
- risk.example.com
secretName: risk-platform-tls
Firewall Rules
# Allow HTTP/HTTPS only
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
Security Headers
# 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