refactor(container): simplify Redis pool initialization flow
- Remove redundant `$container` parameter in `RedisPoolInitializer` instantiation. - Streamline container interactions for improved clarity and maintainability.
This commit is contained in:
@@ -272,8 +272,8 @@ docker network inspect traefik-public
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# Application logs
|
||||
docker compose -f stacks/application/docker-compose.yml logs -f app
|
||||
# Application logs (Production)
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml logs -f php
|
||||
|
||||
# Traefik logs
|
||||
docker compose -f stacks/traefik/docker-compose.yml logs -f
|
||||
|
||||
@@ -13,5 +13,6 @@ all:
|
||||
# Only override-specific variables should be here
|
||||
# Override system_* defaults here when Wartungsfenster abweichen
|
||||
|
||||
# Legacy compose_file reference (deprecated - stacks now use deployment/stacks/)
|
||||
compose_file: "{{ stacks_base_path }}/application/docker-compose.yml"
|
||||
# Legacy compose_file reference (deprecated)
|
||||
# Production now uses docker-compose.base.yml + docker-compose.production.yml from repository root
|
||||
# compose_file: "{{ stacks_base_path }}/application/docker-compose.yml"
|
||||
|
||||
@@ -126,11 +126,11 @@ Das Ansible Playbook führt folgende Schritte auf dem Production-Server aus:
|
||||
**1. Backup aktueller Deployment-Status**
|
||||
```bash
|
||||
# Speichert aktuellen Container-Status
|
||||
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
||||
ps --format json > backups/<timestamp>/current_containers.json
|
||||
|
||||
# Speichert aktuelle docker-compose.yml Konfiguration
|
||||
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
||||
config > backups/<timestamp>/docker-compose-config.yml
|
||||
```
|
||||
|
||||
@@ -186,7 +186,7 @@ replace: '\1{{ app_image }}:{{ image_tag }}'
|
||||
**5. Application Stack neu starten**
|
||||
|
||||
```bash
|
||||
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
||||
up -d \
|
||||
--pull always \
|
||||
--force-recreate \
|
||||
@@ -547,7 +547,7 @@ docker pull registry.michaelschiemer.de/framework:<tag>
|
||||
```bash
|
||||
# Prüfe ob Regex korrekt ist
|
||||
grep -E "image:\s+registry.michaelschiemer.de/framework" \
|
||||
~/deployment/stacks/application/docker-compose.yml
|
||||
docker-compose.base.yml docker-compose.production.yml
|
||||
|
||||
# Prüfe Backup für vorherige Version
|
||||
ls -la ~/deployment/backups/
|
||||
|
||||
33
docker-compose.postgres-override.yml
Normal file
33
docker-compose.postgres-override.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
# Shared PostgreSQL Connection Override for Production
|
||||
#
|
||||
# This file provides network configuration for connecting to the PostgreSQL Stack.
|
||||
# Used by Production Stack.
|
||||
#
|
||||
# Usage:
|
||||
# Production Stack:
|
||||
# docker compose -f docker-compose.base.yml \
|
||||
# -f docker-compose.production.yml \
|
||||
# -f docker-compose.postgres-override.yml up
|
||||
#
|
||||
# Prerequisites:
|
||||
# - PostgreSQL Stack must be running (creates app-internal network)
|
||||
# - app-internal network must exist as external network
|
||||
# - PostgreSQL Stack: deployment/stacks/postgresql/docker-compose.yml
|
||||
|
||||
services:
|
||||
# Production Stack Services
|
||||
# These services will be merged with existing definitions from base.yml + production.yml
|
||||
php:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access (merged with existing networks)
|
||||
queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access (merged with existing networks)
|
||||
scheduler:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access
|
||||
|
||||
networks:
|
||||
app-internal:
|
||||
external: true
|
||||
name: app-internal
|
||||
@@ -151,48 +151,8 @@ services:
|
||||
# Mount .env file from shared directory (production environment variables)
|
||||
- /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro
|
||||
|
||||
db:
|
||||
# Production restart policy
|
||||
restart: always
|
||||
|
||||
# Use Docker Secrets for database password
|
||||
environment:
|
||||
POSTGRES_PASSWORD_FILE: /run/secrets/db_user_password
|
||||
secrets:
|
||||
- db_user_password
|
||||
|
||||
# Use production PostgreSQL configuration
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
- ./docker/postgres/postgresql.production.conf:/etc/postgresql/postgresql.conf:ro
|
||||
- ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
|
||||
|
||||
# Production resource limits
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
cpus: '2.0'
|
||||
reservations:
|
||||
memory: 1G
|
||||
cpus: '1.0'
|
||||
|
||||
# Stricter health checks
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-postgres} -d ${DB_DATABASE:-michaelschiemer}"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
# JSON logging
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "10"
|
||||
compress: "true"
|
||||
labels: "service,environment"
|
||||
# Database service removed - using external PostgreSQL Stack (deployment/stacks/postgresql/)
|
||||
# Connection via app-internal network using docker-compose.postgres-override.yml
|
||||
|
||||
redis:
|
||||
# Production restart policy
|
||||
@@ -333,12 +293,91 @@ services:
|
||||
|
||||
# Wait for dependencies to be healthy before starting
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
php:
|
||||
condition: service_healthy
|
||||
# Note: PostgreSQL (postgres) is external service, connection via app-internal network
|
||||
|
||||
# Scheduler (Cron Jobs)
|
||||
scheduler:
|
||||
# Use same build as php service (has application code copied)
|
||||
image: git.michaelschiemer.de:5000/framework:latest
|
||||
container_name: scheduler
|
||||
|
||||
# Production restart policy
|
||||
restart: always
|
||||
|
||||
# Override user setting - container must start as root for gosu to work
|
||||
# The entrypoint script will use gosu to switch to appuser after setup
|
||||
user: "root"
|
||||
|
||||
# Scheduler command - direct PHP execution
|
||||
command: php console.php scheduler:run
|
||||
|
||||
# Production volumes
|
||||
volumes:
|
||||
# Mount application code from rsync deployment (read-only)
|
||||
- /home/deploy/michaelschiemer/current:/var/www/html:ro
|
||||
# Mount storage directory as writable volume (overlays the read-only code mount)
|
||||
- storage:/var/www/html/storage:rw
|
||||
# Mount var directory as writable volume for cache and logs (overlays read-only code mount)
|
||||
- var-data:/var/www/html/var:rw
|
||||
# Mount .env file from shared directory (production environment variables)
|
||||
- /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro
|
||||
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
# Use Docker Secrets via *_FILE pattern (Framework supports this automatically)
|
||||
- DB_PASSWORD_FILE=/run/secrets/db_user_password
|
||||
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
|
||||
- APP_KEY_FILE=/run/secrets/app_key
|
||||
- VAULT_ENCRYPTION_KEY_FILE=/run/secrets/vault_encryption_key
|
||||
secrets:
|
||||
- db_user_password
|
||||
- redis_password
|
||||
- app_key
|
||||
- vault_encryption_key
|
||||
|
||||
# Production resource limits
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 256M
|
||||
cpus: '0.25'
|
||||
|
||||
# Health checks
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "php -r 'exit(0);' && test -f /var/www/html/console.php || exit 1"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# JSON logging
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
compress: "true"
|
||||
labels: "service,environment"
|
||||
|
||||
# Graceful shutdown
|
||||
stop_grace_period: 30s
|
||||
|
||||
# Wait for dependencies to be healthy before starting
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
php:
|
||||
condition: service_healthy
|
||||
# Note: PostgreSQL (postgres) is external service, connection via app-internal network
|
||||
|
||||
# Certbot Sidecar Container for Let's Encrypt
|
||||
certbot:
|
||||
@@ -385,11 +424,5 @@ volumes:
|
||||
storage:
|
||||
driver: local
|
||||
|
||||
# Database volume with backup driver (optional)
|
||||
db_data:
|
||||
driver: local
|
||||
# Optional: Use external volume for easier backups
|
||||
# driver_opts:
|
||||
# type: none
|
||||
# o: bind
|
||||
# device: /mnt/db-backups/michaelschiemer-prod
|
||||
# Database volume removed - using external PostgreSQL Stack
|
||||
# PostgreSQL data is managed by deployment/stacks/postgresql/
|
||||
|
||||
33
docker-compose.staging-postgres-override.yml
Normal file
33
docker-compose.staging-postgres-override.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
# Shared PostgreSQL Connection Override for Staging
|
||||
#
|
||||
# This file provides network configuration for connecting to the PostgreSQL Stack.
|
||||
# Used by Staging Stack.
|
||||
#
|
||||
# Usage:
|
||||
# Staging Stack:
|
||||
# docker compose -f docker-compose.base.yml \
|
||||
# -f docker-compose.staging.yml \
|
||||
# -f docker-compose.staging-postgres-override.yml up
|
||||
#
|
||||
# Prerequisites:
|
||||
# - PostgreSQL Stack must be running (creates app-internal network)
|
||||
# - app-internal network must exist as external network
|
||||
# - PostgreSQL Stack: deployment/stacks/postgresql/docker-compose.yml
|
||||
|
||||
services:
|
||||
# Staging Stack Services
|
||||
# These services will be merged with existing definitions from base.yml + staging.yml
|
||||
staging-app:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access (merged with staging-internal)
|
||||
staging-queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access (merged with staging-internal)
|
||||
staging-scheduler:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access (merged with staging-internal)
|
||||
|
||||
networks:
|
||||
app-internal:
|
||||
external: true
|
||||
name: app-internal
|
||||
@@ -353,7 +353,7 @@ services:
|
||||
- TZ=Europe/Berlin
|
||||
- APP_ENV=staging
|
||||
- APP_DEBUG=${APP_DEBUG:-true}
|
||||
# Database
|
||||
# Database (can share with production or use separate)
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||||
@@ -408,7 +408,7 @@ services:
|
||||
- TZ=Europe/Berlin
|
||||
- APP_ENV=staging
|
||||
- APP_DEBUG=${APP_DEBUG:-true}
|
||||
# Database
|
||||
# Database (can share with production or use separate)
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||||
@@ -471,6 +471,9 @@ networks:
|
||||
external: true
|
||||
staging-internal:
|
||||
driver: bridge
|
||||
app-internal:
|
||||
external: true
|
||||
name: app-internal
|
||||
|
||||
volumes:
|
||||
staging-code:
|
||||
|
||||
262
docs/deployment/production-compose-consolidation-plan.md
Normal file
262
docs/deployment/production-compose-consolidation-plan.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Plan: Konsolidierung der Production Docker-Compose Konfiguration
|
||||
|
||||
**Datum**: 2025-11-04
|
||||
**Ziel**: `docker-compose.production.yml` im Root verwenden statt `deployment/stacks/application/docker-compose.yml`
|
||||
|
||||
## Aktuelle Situation
|
||||
|
||||
### Datei 1: `docker-compose.production.yml` (Repository Root)
|
||||
**Typ**: Override-Datei f?r `docker-compose.base.yml`
|
||||
**Status**: ? Aktiv verwendet in Deployment-Skripten
|
||||
**Services**: `web`, `php`, `db`, `redis`, `queue-worker`, `certbot`
|
||||
**Struktur**: Base + Override Pattern
|
||||
|
||||
**Verwendung**:
|
||||
```bash
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml up -d
|
||||
```
|
||||
|
||||
### Datei 2: `deployment/stacks/application/docker-compose.yml`
|
||||
**Typ**: Vollst?ndige Docker-Compose-Definition
|
||||
**Status**: ?? Wird noch in einigen Dokumenten referenziert, aber vermutlich nicht mehr aktiv verwendet
|
||||
**Services**: `app`, `nginx`, `redis`, `queue-worker`, `scheduler`
|
||||
**Struktur**: Standalone-Definition
|
||||
|
||||
**Referenzen gefunden in**:
|
||||
- `deployment/ansible/inventory/production.yml`
|
||||
- `deployment/docs/reference/application-stack.md`
|
||||
- `deployment/README.md`
|
||||
- Verschiedene Test-Dokumente
|
||||
|
||||
## Vergleich der Services
|
||||
|
||||
| Service | docker-compose.production.yml | application/docker-compose.yml |
|
||||
|---------|-------------------------------|-------------------------------|
|
||||
| **PHP Runtime** | `php` | `app` |
|
||||
| **Web Server** | `web` | `nginx` |
|
||||
| **Database** | `db` | ? (nicht definiert, nutzt externen PostgreSQL) |
|
||||
| **Redis** | `redis` | `redis` |
|
||||
| **Queue Worker** | `queue-worker` | `queue-worker` |
|
||||
| **Scheduler** | ? (nicht definiert) | `scheduler` ? |
|
||||
| **Certbot** | `certbot` ? | ? |
|
||||
|
||||
## Unterschiede
|
||||
|
||||
### 1. Service-Namen
|
||||
- **production.yml**: `web`, `php` (konsistent mit base.yml)
|
||||
- **application.yml**: `nginx`, `app` (eigene Namenskonvention)
|
||||
|
||||
### 2. Scheduler Service
|
||||
- **production.yml**: ? Fehlt
|
||||
- **application.yml**: ? Vorhanden (`scheduler`)
|
||||
|
||||
### 3. Certbot Service
|
||||
- **production.yml**: ? Vorhanden (f?r Let's Encrypt)
|
||||
- **application.yml**: ? Fehlt
|
||||
|
||||
### 4. Database Service
|
||||
- **production.yml**: ? Definiert (`db`)
|
||||
- **application.yml**: ? Nutzt externen PostgreSQL-Stack (`postgres`)
|
||||
|
||||
### 5. Netzwerk-Konfiguration
|
||||
- **production.yml**: Nutzt `frontend`, `backend`, `cache` (aus base.yml)
|
||||
- **application.yml**: Nutzt `app-internal`, `traefik-public` (external networks)
|
||||
|
||||
## Problemanalyse
|
||||
|
||||
### Problem 1: Service-Namen-Mismatch
|
||||
Die Service-Namen sind unterschiedlich, was zu Verwirrung f?hrt:
|
||||
- `php` vs `app`
|
||||
- `web` vs `nginx`
|
||||
|
||||
### Problem 2: Fehlender Scheduler
|
||||
`docker-compose.production.yml` hat keinen `scheduler` Service, der in `application/docker-compose.yml` vorhanden ist.
|
||||
|
||||
### Problem 3: Unterschiedliche Architektur
|
||||
- **production.yml**: Nutzt Base+Override Pattern mit `db` Service
|
||||
- **application.yml**: Nutzt externen PostgreSQL-Stack (`postgres`)
|
||||
|
||||
## L?sungsplan
|
||||
|
||||
### Phase 1: Scheduler zu production.yml hinzuf?gen
|
||||
|
||||
**Ziel**: `scheduler` Service in `docker-compose.production.yml` hinzuf?gen
|
||||
|
||||
**Begr?ndung**: Der Scheduler ist wichtig f?r Cron-Jobs und sollte in Production verf?gbar sein.
|
||||
|
||||
**Implementierung**:
|
||||
```yaml
|
||||
# In docker-compose.production.yml hinzuf?gen:
|
||||
scheduler:
|
||||
# Production restart policy
|
||||
restart: always
|
||||
|
||||
# Use same build as php service
|
||||
image: git.michaelschiemer.de:5000/framework:latest
|
||||
|
||||
# Production volumes
|
||||
volumes:
|
||||
- /home/deploy/michaelschiemer/current:/var/www/html:ro
|
||||
- storage:/var/www/html/storage:rw
|
||||
- var-data:/var/www/html/var:rw
|
||||
- /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro
|
||||
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
# Use Docker Secrets
|
||||
- DB_PASSWORD_FILE=/run/secrets/db_user_password
|
||||
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
|
||||
- APP_KEY_FILE=/run/secrets/app_key
|
||||
- VAULT_ENCRYPTION_KEY_FILE=/run/secrets/vault_encryption_key
|
||||
secrets:
|
||||
- db_user_password
|
||||
- redis_password
|
||||
- app_key
|
||||
- vault_encryption_key
|
||||
|
||||
command: php console.php scheduler:run
|
||||
|
||||
# Health checks
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "php -r 'exit(0);' && test -f /var/www/html/console.php || exit 1"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
php:
|
||||
condition: service_healthy
|
||||
```
|
||||
|
||||
### Phase 2: Referenzen aktualisieren
|
||||
|
||||
**Ziel**: Alle Referenzen auf `application/docker-compose.yml` entfernen/aktualisieren
|
||||
|
||||
**Dateien zu aktualisieren**:
|
||||
1. `deployment/ansible/inventory/production.yml` - `compose_file` entfernen/aktualisieren
|
||||
2. `deployment/docs/reference/application-stack.md` - Dokumentation aktualisieren
|
||||
3. `deployment/README.md` - Referenzen aktualisieren
|
||||
4. Test-Dokumente - Referenzen aktualisieren
|
||||
|
||||
### Phase 3: application/docker-compose.yml entfernen
|
||||
|
||||
**Voraussetzungen**:
|
||||
- ? Alle Referenzen aktualisiert
|
||||
- ? Scheduler in production.yml hinzugef?gt
|
||||
- ? Deployment-Skripte testen
|
||||
- ? Production-Deployment erfolgreich
|
||||
|
||||
**Aktion**:
|
||||
- `deployment/stacks/application/docker-compose.yml` l?schen
|
||||
- `deployment/stacks/application/docker-compose.base.yml` pr?fen (falls redundant)
|
||||
- Backup erstellen vor L?schung
|
||||
|
||||
### Phase 4: PostgreSQL-Stack Integration
|
||||
|
||||
**Wichtig**: Da `docker-compose.production.yml` aktuell einen eigenen `db` Service definiert, aber wir zum PostgreSQL-Stack verbinden wollen:
|
||||
|
||||
**Option A**: `db` Service aus production.yml entfernen
|
||||
- Nutzt externen PostgreSQL-Stack (`postgres`)
|
||||
- F?gt `app-internal` Netzwerk hinzu (via `docker-compose.postgres-override.yml`)
|
||||
|
||||
**Option B**: `db` Service behalten (wenn separate Production-DB gew?nscht)
|
||||
- Nutzt eigenen `db` Service
|
||||
- Keine Verbindung zum PostgreSQL-Stack n?tig
|
||||
|
||||
**Empfehlung**: Option A - Nutze externen PostgreSQL-Stack f?r Konsistenz
|
||||
|
||||
## Migrationspfad
|
||||
|
||||
### Schritt 1: Scheduler hinzuf?gen
|
||||
```bash
|
||||
# 1. Scheduler zu docker-compose.production.yml hinzuf?gen
|
||||
# 2. Testen lokal
|
||||
docker compose -f docker-compose.base.yml -f docker-compose.production.yml config
|
||||
```
|
||||
|
||||
### Schritt 2: Referenzen pr?fen
|
||||
```bash
|
||||
# Alle Referenzen finden
|
||||
grep -r "stacks/application/docker-compose" deployment/
|
||||
grep -r "application/docker-compose" deployment/
|
||||
```
|
||||
|
||||
### Schritt 3: Referenzen aktualisieren
|
||||
- Dokumentation aktualisieren
|
||||
- Ansible-Playbooks pr?fen
|
||||
- Deployment-Skripte aktualisieren
|
||||
|
||||
### Schritt 4: Testing
|
||||
```bash
|
||||
# Test Production-Deployment
|
||||
docker compose -f docker-compose.base.yml \
|
||||
-f docker-compose.production.yml \
|
||||
-f docker-compose.postgres-override.yml \
|
||||
config
|
||||
|
||||
# Alle Services pr?fen
|
||||
docker compose -f docker-compose.base.yml \
|
||||
-f docker-compose.production.yml \
|
||||
-f docker-compose.postgres-override.yml \
|
||||
ps
|
||||
```
|
||||
|
||||
### Schritt 5: Cleanup
|
||||
```bash
|
||||
# Backup erstellen
|
||||
cp deployment/stacks/application/docker-compose.yml \
|
||||
deployment/stacks/application/docker-compose.yml.backup
|
||||
|
||||
# Datei entfernen
|
||||
rm deployment/stacks/application/docker-compose.yml
|
||||
```
|
||||
|
||||
## Checkliste
|
||||
|
||||
- [ ] Scheduler Service zu `docker-compose.production.yml` hinzuf?gen
|
||||
- [ ] Alle Referenzen auf `application/docker-compose.yml` finden
|
||||
- [ ] Dokumentation aktualisieren
|
||||
- [ ] Ansible-Playbooks aktualisieren
|
||||
- [ ] Deployment-Skripte testen
|
||||
- [ ] Production-Deployment testen
|
||||
- [ ] Backup von `application/docker-compose.yml` erstellen
|
||||
- [ ] `application/docker-compose.yml` entfernen
|
||||
- [ ] `docker-compose.postgres-override.yml` integrieren (f?r PostgreSQL-Stack)
|
||||
|
||||
## Risiken
|
||||
|
||||
### Risiko 1: Service-Namen-?nderungen
|
||||
**Problem**: Services haben unterschiedliche Namen (`php` vs `app`, `web` vs `nginx`)
|
||||
|
||||
**L?sung**:
|
||||
- Deployment-Skripte m?ssen Service-Namen aktualisieren
|
||||
- `php` statt `app`, `web` statt `nginx`
|
||||
|
||||
### Risiko 2: Fehlende Features
|
||||
**Problem**: `application/docker-compose.yml` k?nnte Features haben, die in `production.yml` fehlen
|
||||
|
||||
**L?sung**:
|
||||
- Vergleich durchf?hren
|
||||
- Scheduler bereits identifiziert und wird hinzugef?gt
|
||||
- Weitere Features pr?fen
|
||||
|
||||
### Risiko 3: Referenzen vergessen
|
||||
**Problem**: Versteckte Referenzen k?nnten zu Fehlern f?hren
|
||||
|
||||
**L?sung**:
|
||||
- Systematische Suche nach allen Referenzen
|
||||
- Tests durchf?hren
|
||||
|
||||
## N?chste Schritte
|
||||
|
||||
1. ? Plan erstellt
|
||||
2. ? Scheduler zu `docker-compose.production.yml` hinzuf?gen
|
||||
3. ? Referenzen systematisch finden und aktualisieren
|
||||
4. ? Testing durchf?hren
|
||||
5. ? Cleanup: `application/docker-compose.yml` entfernen
|
||||
303
docs/deployment/shared-postgres-override-plan.md
Normal file
303
docs/deployment/shared-postgres-override-plan.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Plan: Gemeinsame Docker-Compose Override f?r PostgreSQL-Verbindung
|
||||
|
||||
**Datum**: 2025-11-04
|
||||
**Ziel**: Gemeinsame Override-Datei f?r Application- und Staging-Stack, die beide mit dem PostgreSQL-Stack verbinden k?nnen
|
||||
|
||||
## Aktuelle Situation
|
||||
|
||||
### Application Stack (`deployment/stacks/application/docker-compose.yml`)
|
||||
- ? **Bereits im `app-internal` Netzwerk**
|
||||
- ? **Kann sich bereits mit PostgreSQL verbinden**
|
||||
- Services: `app`, `nginx`, `queue-worker`, `scheduler`
|
||||
- Netzwerk: `app-internal` (external: true)
|
||||
|
||||
### Staging Stack (`docker-compose.staging.yml`)
|
||||
- ? **Nur im `staging-internal` Netzwerk**
|
||||
- ? **Kann sich NICHT mit PostgreSQL verbinden**
|
||||
- Services: `staging-app`, `staging-nginx`, `staging-queue-worker`, `staging-scheduler`
|
||||
- Netzwerk: `staging-internal` (driver: bridge)
|
||||
|
||||
### PostgreSQL Stack (`deployment/stacks/postgresql/docker-compose.yml`)
|
||||
- Service: `postgres`
|
||||
- Netzwerk: `app-internal` (external: true)
|
||||
|
||||
## L?sung: Gemeinsame Override-Datei
|
||||
|
||||
### Datei-Struktur
|
||||
|
||||
```
|
||||
Repository Root/
|
||||
??? docker-compose.base.yml
|
||||
??? docker-compose.staging.yml
|
||||
??? docker-compose.postgres-override.yml # NEU: Gemeinsame Override
|
||||
??? deployment/
|
||||
??? stacks/
|
||||
??? application/
|
||||
? ??? docker-compose.yml
|
||||
??? postgresql/
|
||||
??? docker-compose.yml
|
||||
```
|
||||
|
||||
### Vorteile
|
||||
|
||||
1. ? **DRY-Prinzip**: Netzwerk-Konfiguration zentral
|
||||
2. ? **Wartbarkeit**: ?nderungen an einem Ort
|
||||
3. ? **Konsistenz**: Beide Stacks nutzen identische Konfiguration
|
||||
4. ? **Flexibilit?t**: Kann einfach aktiviert/deaktiviert werden
|
||||
|
||||
## Implementierungsplan
|
||||
|
||||
### 1. Neue Datei: `docker-compose.postgres-override.yml`
|
||||
|
||||
**Inhalt**: Netzwerk-Definition und Service-Overrides f?r PostgreSQL-Verbindung
|
||||
|
||||
```yaml
|
||||
# Shared PostgreSQL Connection Override
|
||||
# Usage:
|
||||
# Application Stack:
|
||||
# docker compose -f deployment/stacks/application/docker-compose.yml \
|
||||
# -f docker-compose.postgres-override.yml up
|
||||
#
|
||||
# Staging Stack:
|
||||
# docker compose -f docker-compose.base.yml \
|
||||
# -f docker-compose.staging.yml \
|
||||
# -f docker-compose.postgres-override.yml up
|
||||
|
||||
services:
|
||||
# Application Stack Services
|
||||
app:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is used
|
||||
nginx:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is used
|
||||
queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is used
|
||||
scheduler:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is used
|
||||
|
||||
# Staging Stack Services
|
||||
staging-app:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network
|
||||
staging-queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network
|
||||
staging-scheduler:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network
|
||||
|
||||
networks:
|
||||
app-internal:
|
||||
external: true
|
||||
name: app-internal
|
||||
```
|
||||
|
||||
### 2. Problem: Service-Namen sind unterschiedlich
|
||||
|
||||
**Application Stack**: `app`, `nginx`, `queue-worker`, `scheduler`
|
||||
**Staging Stack**: `staging-app`, `staging-nginx`, `staging-queue-worker`, `staging-scheduler`
|
||||
|
||||
**L?sung**: Die Override-Datei kann beide Service-Namen unterst?tzen (Docker Compose ignoriert nicht-existierende Services).
|
||||
|
||||
### 3. Alternative: Zwei separate Override-Dateien
|
||||
|
||||
**Option A**: Eine Datei f?r beide (einfacher)
|
||||
- ? Einfacher zu warten
|
||||
- ?? Enth?lt Service-Definitionen f?r beide Stacks
|
||||
|
||||
**Option B**: Zwei Dateien (sauberer)
|
||||
- `docker-compose.postgres-override-app.yml` (f?r Application Stack)
|
||||
- `docker-compose.postgres-override-staging.yml` (f?r Staging Stack)
|
||||
- ? Klarer getrennt
|
||||
- ?? Mehr Dateien
|
||||
|
||||
**Empfehlung**: Option A - eine Datei, da die Konfiguration identisch ist
|
||||
|
||||
## Detaillierte Implementierung
|
||||
|
||||
### Datei: `docker-compose.postgres-override.yml`
|
||||
|
||||
```yaml
|
||||
# Shared PostgreSQL Connection Override
|
||||
#
|
||||
# This file provides network configuration for connecting to the PostgreSQL Stack.
|
||||
# It can be used by both Application Stack and Staging Stack.
|
||||
#
|
||||
# Usage:
|
||||
# Application Stack:
|
||||
# docker compose -f deployment/stacks/application/docker-compose.yml \
|
||||
# -f docker-compose.postgres-override.yml up
|
||||
#
|
||||
# Staging Stack:
|
||||
# docker compose -f docker-compose.base.yml \
|
||||
# -f docker-compose.staging.yml \
|
||||
# -f docker-compose.postgres-override.yml up
|
||||
#
|
||||
# Prerequisites:
|
||||
# - PostgreSQL Stack must be running (creates app-internal network)
|
||||
# - app-internal network must exist as external network
|
||||
|
||||
services:
|
||||
# Application Stack Services
|
||||
# (These will be ignored if not present in the base compose file)
|
||||
app:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is available
|
||||
nginx:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is available
|
||||
queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is available
|
||||
scheduler:
|
||||
networks:
|
||||
app-internal: {} # Ensure app-internal network is available
|
||||
|
||||
# Staging Stack Services
|
||||
# (These will be ignored if not present in the base compose file)
|
||||
staging-app:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access
|
||||
staging-queue-worker:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access
|
||||
staging-scheduler:
|
||||
networks:
|
||||
app-internal: {} # Add app-internal network for PostgreSQL access
|
||||
|
||||
networks:
|
||||
app-internal:
|
||||
external: true
|
||||
name: app-internal
|
||||
```
|
||||
|
||||
### Wichtig: Netzwerk-Merge-Verhalten
|
||||
|
||||
Docker Compose **merged** Netzwerke, wenn ein Service mehrere Netzwerke hat:
|
||||
|
||||
```yaml
|
||||
# In docker-compose.staging.yml
|
||||
staging-app:
|
||||
networks:
|
||||
- staging-internal
|
||||
|
||||
# In docker-compose.postgres-override.yml
|
||||
staging-app:
|
||||
networks:
|
||||
app-internal: {}
|
||||
|
||||
# Ergebnis: staging-app ist in BEIDEN Netzwerken
|
||||
# - staging-internal (aus staging.yml)
|
||||
# - app-internal (aus override.yml)
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Application Stack
|
||||
|
||||
**Aktuell**:
|
||||
```bash
|
||||
cd deployment/stacks/application
|
||||
docker compose -f docker-compose.yml up -d
|
||||
```
|
||||
|
||||
**Mit Override** (optional, da bereits im app-internal Netzwerk):
|
||||
```bash
|
||||
cd deployment/stacks/application
|
||||
docker compose -f docker-compose.yml \
|
||||
-f ../../docker-compose.postgres-override.yml up -d
|
||||
```
|
||||
|
||||
### Staging Stack
|
||||
|
||||
**Aktuell**:
|
||||
```bash
|
||||
docker compose -f docker-compose.base.yml \
|
||||
-f docker-compose.staging.yml up -d
|
||||
```
|
||||
|
||||
**Mit Override** (f?r PostgreSQL-Verbindung):
|
||||
```bash
|
||||
docker compose -f docker-compose.base.yml \
|
||||
-f docker-compose.staging.yml \
|
||||
-f docker-compose.postgres-override.yml up -d
|
||||
```
|
||||
|
||||
## Deployment-Skripte anpassen
|
||||
|
||||
### Ansible Playbooks
|
||||
|
||||
**`deployment/ansible/playbooks/deploy-staging.yml`**:
|
||||
```yaml
|
||||
- name: Start staging stack
|
||||
command: >
|
||||
docker compose
|
||||
-f docker-compose.base.yml
|
||||
-f docker-compose.staging.yml
|
||||
-f docker-compose.postgres-override.yml
|
||||
up -d
|
||||
args:
|
||||
chdir: "{{ staging_deploy_path }}"
|
||||
```
|
||||
|
||||
**`deployment/ansible/playbooks/deploy-production.yml`**:
|
||||
```yaml
|
||||
- name: Start application stack
|
||||
command: >
|
||||
docker compose
|
||||
-f docker-compose.yml
|
||||
-f ../../docker-compose.postgres-override.yml
|
||||
up -d
|
||||
args:
|
||||
chdir: "{{ app_deploy_path }}/deployment/stacks/application"
|
||||
```
|
||||
|
||||
## Vorteile dieser L?sung
|
||||
|
||||
1. ? **Zentrale Konfiguration**: Netzwerk-Setup an einem Ort
|
||||
2. ? **Wiederverwendbar**: Beide Stacks nutzen die gleiche Datei
|
||||
3. ? **Optional**: Kann bei Bedarf weggelassen werden
|
||||
4. ? **Flexibel**: Einfach erweiterbar f?r weitere Services
|
||||
5. ? **DRY**: Keine Duplikation von Netzwerk-Konfiguration
|
||||
|
||||
## Nachteile / ?berlegungen
|
||||
|
||||
1. ?? **Pfad-Abh?ngigkeit**: Relative Pfade in verschiedenen Verzeichnissen
|
||||
- **L?sung**: Absolute Pfade oder von Repository-Root aus ausf?hren
|
||||
|
||||
2. ?? **Service-Namen**: Docker Compose ignoriert nicht-existierende Services
|
||||
- **Status**: ? Funktioniert - Docker Compose merged nur existierende Services
|
||||
|
||||
3. ?? **Netzwerk muss existieren**: `app-internal` muss vorher erstellt sein
|
||||
- **Status**: ? Wird automatisch vom PostgreSQL-Stack erstellt
|
||||
|
||||
## Alternative: Inline-Netzwerk-Konfiguration
|
||||
|
||||
**Alternativ** k?nnte das Netzwerk auch direkt in den jeweiligen Compose-Dateien definiert werden:
|
||||
|
||||
- `docker-compose.staging.yml`: Netzwerk `app-internal` hinzuf?gen
|
||||
- `deployment/stacks/application/docker-compose.yml`: Bereits vorhanden
|
||||
|
||||
**Nachteil**: Duplikation, weniger wartbar
|
||||
|
||||
**Vorteil**: Keine zus?tzliche Override-Datei n?tig
|
||||
|
||||
## Empfehlung
|
||||
|
||||
**Gemeinsame Override-Datei** ist die beste L?sung, weil:
|
||||
1. **DRY-Prinzip**: Keine Duplikation
|
||||
2. **Wartbarkeit**: ?nderungen an einem Ort
|
||||
3. **Flexibilit?t**: Kann optional verwendet werden
|
||||
4. **Klarheit**: Explizite PostgreSQL-Verbindung sichtbar
|
||||
|
||||
## N?chste Schritte
|
||||
|
||||
1. ? Plan erstellt
|
||||
2. ? Implementierung: `docker-compose.postgres-override.yml` erstellen
|
||||
3. ? Staging Stack: Verwendung in Dokumentation/Deployment-Skripten
|
||||
4. ? Application Stack: Optional (bereits funktionsf?hig, aber f?r Konsistenz)
|
||||
5. ? Testing: Beide Stacks mit Override testen
|
||||
6. ? Dokumentation: README-Dateien aktualisieren
|
||||
185
docs/deployment/staging-database-connection-analysis.md
Normal file
185
docs/deployment/staging-database-connection-analysis.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Analyse: Datenbankverbindung im staging-app Container
|
||||
|
||||
**Datum**: 2025-11-04
|
||||
**Problem**: `staging-app` Container kann nicht zur Datenbank verbinden
|
||||
**Fehlermeldung**: `could not translate host name "postgres" to address: Name or service not known`
|
||||
|
||||
## Problem-Identifikation
|
||||
|
||||
### Symptom
|
||||
Der `staging-app` Container schl?gt fehl mit folgendem Fehler:
|
||||
```
|
||||
[2025-11-04 01:32:28] [ERROR] appConsoleHandler --- Failed to invoke initializer method __invoke for class App\Framework\Database\ConnectionInitializer: Failed to connect to database 'michaelschiemer' on 'postgres': SQLSTATE[08006] [7] could not translate host name "postgres" to address: Name or service not known
|
||||
```
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
#### 1. **Fehlender PostgreSQL-Service im Staging-Setup**
|
||||
|
||||
**Problem**: In `docker-compose.staging.yml` wird `DB_HOST=${DB_HOST:-postgres}` konfiguriert (Zeilen 32, 357, 412), aber es existiert kein PostgreSQL-Service mit dem Namen `postgres` im Staging-Setup.
|
||||
|
||||
**Beweis**:
|
||||
- `docker-compose.staging.yml` Zeile 32: `DB_HOST=${DB_HOST:-postgres}`
|
||||
- `docker-compose.staging.yml` Zeilen 456-458: Der `db` Service aus `docker-compose.base.yml` wird explizit deaktiviert:
|
||||
```yaml
|
||||
db:
|
||||
profiles:
|
||||
- never
|
||||
```
|
||||
|
||||
#### 2. **Netzwerk-Isolation**
|
||||
|
||||
**Problem**: Der `staging-app` Container ist nur im `staging-internal` Netzwerk (Zeile 18), aber es gibt keinen Datenbank-Service in diesem Netzwerk.
|
||||
|
||||
**Netzwerk-Konfiguration**:
|
||||
- `staging-app`: Netzwerk `staging-internal` (Zeile 18)
|
||||
- `staging-redis`: Netzwerk `staging-internal` (Zeile 302)
|
||||
- **Kein PostgreSQL-Service** im `staging-internal` Netzwerk
|
||||
|
||||
#### 3. **Vergleich mit Production-Setup**
|
||||
|
||||
**Production (`docker-compose.production.yml`)**:
|
||||
- Zeile 154-196: `db` Service ist definiert und aktiv
|
||||
- `db` Service ist im `backend` Netzwerk (aus `docker-compose.base.yml`)
|
||||
- PHP-Services k?nnen sich mit `DB_HOST=postgres` verbinden, da der Service existiert
|
||||
|
||||
**Staging (`docker-compose.staging.yml`)**:
|
||||
- Zeilen 456-458: `db` Service wird deaktiviert
|
||||
- Kein PostgreSQL-Service definiert
|
||||
- Services versuchen trotzdem, sich mit `postgres` zu verbinden
|
||||
|
||||
## Detaillierte Analyse der Konfiguration
|
||||
|
||||
### Staging-Services, die DB_HOST verwenden
|
||||
|
||||
1. **staging-app** (Zeile 13-205)
|
||||
- `DB_HOST=${DB_HOST:-postgres}` (Zeile 32)
|
||||
- Netzwerk: `staging-internal`
|
||||
|
||||
2. **staging-queue-worker** (Zeile 346-399)
|
||||
- `DB_HOST=${DB_HOST:-postgres}` (Zeile 357)
|
||||
- Netzwerk: `staging-internal`
|
||||
|
||||
3. **staging-scheduler** (Zeile 401-447)
|
||||
- `DB_HOST=${DB_HOST:-postgres}` (Zeile 412)
|
||||
- Netzwerk: `staging-internal`
|
||||
|
||||
### Fehlende Komponenten
|
||||
|
||||
1. **PostgreSQL-Service**: Nicht definiert in Staging-Konfiguration
|
||||
2. **Netzwerk-Verbindung**: Kein Datenbank-Service im `staging-internal` Netzwerk
|
||||
3. **Dependencies**: Keine `depends_on` f?r Datenbank-Service
|
||||
|
||||
## L?sungsoptionen
|
||||
|
||||
### Option 1: Eigener PostgreSQL-Service f?r Staging (Empfohlen)
|
||||
|
||||
**Vorteile**:
|
||||
- Vollst?ndige Isolation zwischen Staging und Production
|
||||
- Keine Abh?ngigkeit von externen Datenbanken
|
||||
- Konsistente Konfiguration mit anderen Services
|
||||
|
||||
**Implementierung**:
|
||||
```yaml
|
||||
# In docker-compose.staging.yml hinzuf?gen:
|
||||
staging-postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: staging-postgres
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- staging-internal
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- POSTGRES_DB=${DB_DATABASE:-michaelschiemer_staging}
|
||||
- POSTGRES_USER=${DB_USERNAME}
|
||||
- POSTGRES_PASSWORD_FILE=/run/secrets/db_user_password
|
||||
secrets:
|
||||
- db_user_password
|
||||
volumes:
|
||||
- staging-postgres-data:/var/lib/postgresql/data
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME} -d ${DB_DATABASE:-michaelschiemer_staging}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
volumes:
|
||||
staging-postgres-data:
|
||||
name: staging-postgres-data
|
||||
|
||||
# DB_HOST in allen Services ?ndern:
|
||||
- DB_HOST=staging-postgres # statt postgres
|
||||
```
|
||||
|
||||
### Option 2: Externe Datenbank verwenden
|
||||
|
||||
**Vorteile**:
|
||||
- Keine zus?tzliche Container-Verwaltung
|
||||
- Kann auf bestehende Datenbank-Infrastruktur zur?ckgreifen
|
||||
|
||||
**Nachteile**:
|
||||
- Abh?ngigkeit von externer Infrastruktur
|
||||
- Netzwerk-Konfiguration komplexer (muss extern erreichbar sein)
|
||||
|
||||
**Implementierung**:
|
||||
- `DB_HOST` auf externe IP/Hostname setzen
|
||||
- Sicherstellen, dass `staging-internal` Netzwerk Zugriff hat (oder externes Netzwerk verwenden)
|
||||
|
||||
### Option 3: Base-Service `db` aktivieren und umbenennen
|
||||
|
||||
**Vorteile**:
|
||||
- Nutzt bestehende Konfiguration aus `docker-compose.base.yml`
|
||||
- Minimaler Aufwand
|
||||
|
||||
**Nachteile**:
|
||||
- Kann Konflikte mit anderen Umgebungen geben
|
||||
- Nicht so isoliert wie Option 1
|
||||
|
||||
**Implementierung**:
|
||||
```yaml
|
||||
# In docker-compose.staging.yml:
|
||||
staging-postgres:
|
||||
extends:
|
||||
service: db
|
||||
file: docker-compose.base.yml
|
||||
container_name: staging-postgres
|
||||
networks:
|
||||
- staging-internal
|
||||
# DB_HOST auf staging-postgres ?ndern
|
||||
```
|
||||
|
||||
## Empfohlene L?sung
|
||||
|
||||
**Option 1** ist die beste L?sung, weil:
|
||||
1. **Vollst?ndige Isolation**: Staging hat eigene Datenbank
|
||||
2. **Konsistenz**: Gleiche Struktur wie `staging-redis`
|
||||
3. **Einfachheit**: Alle Services im gleichen Netzwerk
|
||||
4. **Wartbarkeit**: Klare Struktur, leicht zu verstehen
|
||||
|
||||
## N?chste Schritte
|
||||
|
||||
1. ? Analyse abgeschlossen
|
||||
2. ? Implementierung: PostgreSQL-Service f?r Staging hinzuf?gen
|
||||
3. ? `DB_HOST` in allen Staging-Services auf `staging-postgres` ?ndern
|
||||
4. ? `depends_on` f?r Datenbank-Service hinzuf?gen
|
||||
5. ? Volume f?r PostgreSQL-Daten definieren
|
||||
6. ? Testing: Verbindung testen nach Implementierung
|
||||
|
||||
## Zus?tzliche ?berlegungen
|
||||
|
||||
### Netzwerk-Architektur
|
||||
- `staging-internal`: Interne Services (app, postgres, redis, queue-worker, scheduler)
|
||||
- `traefik-public`: Externes Netzwerk f?r Traefik (nur staging-nginx)
|
||||
|
||||
### Sicherheit
|
||||
- Docker Secrets f?r Datenbank-Passwort verwenden (bereits konfiguriert)
|
||||
- Keine Ports nach au?en f?r PostgreSQL (nur intern)
|
||||
- Gesundheitschecks f?r Datenbank-Service
|
||||
|
||||
### Performance
|
||||
- PostgreSQL-Volumes f?r Persistenz
|
||||
- Gesundheitschecks f?r alle Services
|
||||
- `depends_on` mit `condition: service_healthy` f?r Datenbank
|
||||
209
docs/deployment/staging-postgres-connection-plan.md
Normal file
209
docs/deployment/staging-postgres-connection-plan.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Plan: Verbindung zum PostgreSQL-Stack f?r Staging
|
||||
|
||||
**Datum**: 2025-11-04
|
||||
**Ziel**: Staging-Services sollen sich mit dem PostgreSQL-Container aus dem PostgreSQL-Stack verbinden k?nnen
|
||||
|
||||
## Aktuelle Situation
|
||||
|
||||
### PostgreSQL-Stack (`deployment/stacks/postgresql/docker-compose.yml`)
|
||||
- **Service-Name**: `postgres`
|
||||
- **Container-Name**: `postgres`
|
||||
- **Netzwerk**: `app-internal` (external: true)
|
||||
- **Port**: 5432
|
||||
|
||||
### Staging-Stack (`docker-compose.staging.yml`)
|
||||
- **Netzwerk**: `staging-internal` (driver: bridge, **nicht external**)
|
||||
- **Problem**: `staging-app` ist nur im `staging-internal` Netzwerk und kann daher den `postgres` Container nicht erreichen
|
||||
|
||||
### Application-Stack (Production) (`deployment/stacks/application/docker-compose.yml`)
|
||||
- **Netzwerk**: `app-internal` (external: true)
|
||||
- **Status**: ? Kann sich mit `postgres` verbinden (gleiches Netzwerk)
|
||||
|
||||
## L?sung: Multi-Network-Architektur
|
||||
|
||||
Die Staging-Services m?ssen **beide Netzwerke** nutzen:
|
||||
1. **`app-internal`** (external) - f?r Zugriff auf PostgreSQL-Stack
|
||||
2. **`staging-internal`** (internal) - f?r interne Staging-Services (Redis, etc.)
|
||||
|
||||
## Implementierungsplan
|
||||
|
||||
### 1. Netzwerk-Konfiguration anpassen
|
||||
|
||||
**?nderungen in `docker-compose.staging.yml`:**
|
||||
|
||||
#### A. `app-internal` Netzwerk als external definieren
|
||||
|
||||
```yaml
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
staging-internal:
|
||||
driver: bridge
|
||||
app-internal: # NEU: F?r PostgreSQL-Zugriff
|
||||
external: true
|
||||
name: app-internal
|
||||
```
|
||||
|
||||
#### B. Staging-Services an beide Netzwerke anschlie?en
|
||||
|
||||
**Services die PostgreSQL ben?tigen:**
|
||||
- `staging-app` ? `app-internal` + `staging-internal`
|
||||
- `staging-queue-worker` ? `app-internal` + `staging-internal`
|
||||
- `staging-scheduler` ? `app-internal` + `staging-internal`
|
||||
|
||||
**Services die KEINE PostgreSQL ben?tigen:**
|
||||
- `staging-nginx` ? bleibt bei `traefik-public` + `staging-internal`
|
||||
- `staging-redis` ? bleibt bei `staging-internal` (nur intern)
|
||||
|
||||
### 2. DB_HOST Konfiguration
|
||||
|
||||
**Aktuell**: `DB_HOST=${DB_HOST:-postgres}`
|
||||
**Status**: ? Korrekt - `postgres` ist der Service-Name im PostgreSQL-Stack
|
||||
|
||||
**Keine ?nderung n?tig**, da `postgres` der korrekte Hostname ist, sobald die Services im `app-internal` Netzwerk sind.
|
||||
|
||||
### 3. Dependencies hinzuf?gen
|
||||
|
||||
**Optional**: `depends_on` f?r PostgreSQL k?nnte hinzugef?gt werden, aber da der PostgreSQL-Stack extern ist, sollte besser auf Health-Checks verzichtet werden (externer Service kann nicht direkt abh?ngig sein).
|
||||
|
||||
**Alternative**: Warten auf PostgreSQL-Verf?gbarkeit im Entrypoint-Script.
|
||||
|
||||
### 4. Netzwerk-Architektur
|
||||
|
||||
```
|
||||
???????????????????????????????????????????????????????????
|
||||
? PostgreSQL Stack (deployment/stacks/postgresql/) ?
|
||||
? ??????????????????????????????????????????????????????? ?
|
||||
? ? postgres Container ? ?
|
||||
? ? Network: app-internal (external) ? ?
|
||||
? ??????????????????????????????????????????????????????? ?
|
||||
???????????????????????????????????????????????????????????
|
||||
?
|
||||
?
|
||||
? app-internal (external network)
|
||||
?
|
||||
????????????????????????????????????????????????????????????
|
||||
? Staging Stack (docker-compose.staging.yml) ?
|
||||
? ?
|
||||
? ???????????????????? ???????????????????? ?
|
||||
? ? staging-app ? ? staging-queue- ? ?
|
||||
? ? Networks: ? ? worker ? ?
|
||||
? ? ? app-internal ? ? Networks: ? ?
|
||||
? ? ? staging-internal? ? ? app-internal ? ?
|
||||
? ???????????????????? ? ? staging-internal? ?
|
||||
? ???????????????????? ?
|
||||
? ?
|
||||
? ???????????????????? ???????????????????? ?
|
||||
? ? staging-redis ? ? staging-nginx ? ?
|
||||
? ? Networks: ? ? Networks: ? ?
|
||||
? ? ? staging- ? ? ? traefik-public ? ?
|
||||
? ? internal ? ? ? staging-internal? ?
|
||||
? ???????????????????? ???????????????????? ?
|
||||
????????????????????????????????????????????????????????????
|
||||
```
|
||||
|
||||
## Konkrete ?nderungen
|
||||
|
||||
### Datei: `docker-compose.staging.yml`
|
||||
|
||||
#### 1. Networks-Sektion erweitern
|
||||
|
||||
```yaml
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
staging-internal:
|
||||
driver: bridge
|
||||
app-internal: # NEU: F?r PostgreSQL-Zugriff
|
||||
external: true
|
||||
name: app-internal
|
||||
```
|
||||
|
||||
#### 2. staging-app: app-internal Netzwerk hinzuf?gen
|
||||
|
||||
```yaml
|
||||
staging-app:
|
||||
networks:
|
||||
- app-internal # NEU: F?r PostgreSQL
|
||||
- staging-internal # Bestehend: F?r Redis
|
||||
```
|
||||
|
||||
#### 3. staging-queue-worker: app-internal Netzwerk hinzuf?gen
|
||||
|
||||
```yaml
|
||||
staging-queue-worker:
|
||||
networks:
|
||||
- app-internal # NEU: F?r PostgreSQL
|
||||
- staging-internal # Bestehend: F?r Redis
|
||||
```
|
||||
|
||||
#### 4. staging-scheduler: app-internal Netzwerk hinzuf?gen
|
||||
|
||||
```yaml
|
||||
staging-scheduler:
|
||||
networks:
|
||||
- app-internal # NEU: F?r PostgreSQL
|
||||
- staging-internal # Bestehend: F?r Redis
|
||||
```
|
||||
|
||||
## Vorteile dieser L?sung
|
||||
|
||||
1. ? **Keine ?nderung am PostgreSQL-Stack n?tig**
|
||||
2. ? **Staging nutzt bestehende PostgreSQL-Infrastruktur**
|
||||
3. ? **Isolation bleibt erhalten**: Staging-Services kommunizieren intern ?ber `staging-internal`
|
||||
4. ? **Konsistent mit Production**: Production nutzt ebenfalls `app-internal`
|
||||
5. ? **Minimale ?nderungen**: Nur Netzwerk-Konfiguration, keine DB_HOST-?nderung n?tig
|
||||
|
||||
## Nachteile / ?berlegungen
|
||||
|
||||
1. ?? **Netzwerk-Abh?ngigkeit**: Staging h?ngt von externem `app-internal` Netzwerk ab
|
||||
- **L?sung**: Netzwerk muss vor Staging-Start existieren (sollte automatisch sein)
|
||||
|
||||
2. ?? **Shared Database**: Staging und Production k?nnten theoretisch die gleiche DB nutzen
|
||||
- **Aktuell**: `DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}` verhindert dies
|
||||
- **Empfehlung**: Separate Datenbank f?r Staging (`michaelschiemer_staging`)
|
||||
|
||||
## Testing-Schritte nach Implementierung
|
||||
|
||||
1. **Netzwerk-Verf?gbarkeit pr?fen**:
|
||||
```bash
|
||||
docker network inspect app-internal | grep postgres
|
||||
```
|
||||
|
||||
2. **Container-Verbindung testen**:
|
||||
```bash
|
||||
docker exec staging-app ping -c 1 postgres
|
||||
```
|
||||
|
||||
3. **PostgreSQL-Verbindung testen**:
|
||||
```bash
|
||||
docker exec staging-app php -r "
|
||||
\$dsn = 'pgsql:host=postgres;port=5432;dbname=' . getenv('DB_DATABASE');
|
||||
\$pdo = new PDO(\$dsn, getenv('DB_USERNAME'), file_get_contents(getenv('DB_PASSWORD_FILE')));
|
||||
echo 'Connection successful: ' . \$pdo->query('SELECT version()')->fetchColumn();
|
||||
"
|
||||
```
|
||||
|
||||
4. **Application-Logs pr?fen**:
|
||||
```bash
|
||||
docker logs staging-app | grep -i "database\|postgres\|connection"
|
||||
```
|
||||
|
||||
## N?chste Schritte
|
||||
|
||||
1. ? Plan erstellt
|
||||
2. ? Implementierung: Netzwerk-Konfiguration anpassen
|
||||
3. ? Testing: Verbindung testen
|
||||
4. ? Dokumentation: README aktualisieren
|
||||
|
||||
## Alternative Ans?tze (nicht empfohlen)
|
||||
|
||||
### Alternative 1: Eigener PostgreSQL f?r Staging
|
||||
- **Vorteil**: Vollst?ndige Isolation
|
||||
- **Nachteil**: Mehr Ressourcen, mehr Wartungsaufwand
|
||||
- **Status**: Nicht gew?nscht (Benutzer m?chte PostgreSQL-Stack nutzen)
|
||||
|
||||
### Alternative 2: Externe PostgreSQL-URL
|
||||
- **Vorteil**: Flexibler
|
||||
- **Nachteil**: Komplexer, erfordert externe Netzwerk-Konfiguration
|
||||
- **Status**: Nicht n?tig (Docker-Netzwerke sind ausreichend)
|
||||
@@ -51,13 +51,15 @@ final readonly class AppBootstrapper
|
||||
// Initialize environment with encryption support
|
||||
$env = $this->initializeEnvironment();
|
||||
|
||||
|
||||
// Workaround: If REDIS_PASSWORD_FILE is not set but expected file exists, set it manually
|
||||
// This handles cases where Docker Compose doesn't set the variable correctly
|
||||
$expectedRedisPasswordFile = '/run/secrets/redis_password';
|
||||
if (!$env->has('REDIS_PASSWORD_FILE') && file_exists($expectedRedisPasswordFile)) {
|
||||
// Add REDIS_PASSWORD_FILE to environment if file exists
|
||||
$env = $env->withVariable('REDIS_PASSWORD_FILE', $expectedRedisPasswordFile);
|
||||
}
|
||||
#$expectedRedisPasswordFile = '/run/secrets/redis_password';
|
||||
#if (!$env->has('REDIS_PASSWORD_FILE') && file_exists($expectedRedisPasswordFile)) {
|
||||
# // Add REDIS_PASSWORD_FILE to environment if file exists
|
||||
# #$env = $env->withVariable('REDIS_PASSWORD_FILE', $expectedRedisPasswordFile);
|
||||
#
|
||||
#}
|
||||
|
||||
// Make Environment available throughout the application
|
||||
$this->container->instance(Environment::class, $env);
|
||||
@@ -128,12 +130,13 @@ final readonly class AppBootstrapper
|
||||
{
|
||||
$this->collector->startTiming('bootstrap', PerformanceCategory::SYSTEM);
|
||||
|
||||
$this->bootstrapper->bootstrap($this->basePath, $this->collector);
|
||||
// Get Environment from container (registered in constructor)
|
||||
$env = $this->container->get(Environment::class);
|
||||
$this->bootstrapper->bootstrap($this->basePath, $this->collector, $env);
|
||||
|
||||
$this->collector->endTiming('bootstrap');
|
||||
|
||||
// Initialize secrets management after container is bootstrapped
|
||||
$env = $this->container->get(Environment::class);
|
||||
$this->initializeSecretsManagement($env);
|
||||
|
||||
// ErrorHandler wird jetzt kontextabhängig registriert
|
||||
|
||||
@@ -42,16 +42,17 @@ final readonly class ContainerBootstrapper
|
||||
public function bootstrap(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): Container {
|
||||
// Temporarily disable compiled container to fix bootstrap issues
|
||||
// TODO: Re-enable once container interface compatibility is fixed
|
||||
// $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector);
|
||||
// $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector, $environment);
|
||||
// if ($optimizedContainer !== null) {
|
||||
// return $optimizedContainer;
|
||||
// }
|
||||
|
||||
// Fallback to fresh container bootstrap
|
||||
return $this->bootstrapFreshContainer($basePath, $collector);
|
||||
return $this->bootstrapFreshContainer($basePath, $collector, $environment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +60,8 @@ final readonly class ContainerBootstrapper
|
||||
*/
|
||||
private function tryLoadCompiledContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): ?Container {
|
||||
try {
|
||||
$cacheDir = sys_get_temp_dir() . '/framework-cache';
|
||||
@@ -71,7 +73,7 @@ final readonly class ContainerBootstrapper
|
||||
}
|
||||
|
||||
// Create temporary fresh container to validate against
|
||||
$tempContainer = $this->createFreshContainer($basePath, $collector);
|
||||
$tempContainer = $this->createFreshContainer($basePath, $collector, $environment);
|
||||
|
||||
// Create compiler for validation
|
||||
$reflectionProvider = new CachedReflectionProvider();
|
||||
@@ -86,8 +88,11 @@ final readonly class ContainerBootstrapper
|
||||
// Load and return compiled container
|
||||
$compiledContainer = ContainerCompiler::load($compiledPath);
|
||||
|
||||
// Environment must be registered first as runtime data that can't be compiled
|
||||
$compiledContainer->instance(Environment::class, $environment);
|
||||
|
||||
// Add runtime instances that can't be compiled
|
||||
$this->addRuntimeInstances($compiledContainer, $basePath, $collector);
|
||||
$this->registerRuntimeServices($compiledContainer, $basePath, $collector, $environment);
|
||||
|
||||
return $compiledContainer;
|
||||
|
||||
@@ -108,9 +113,10 @@ final readonly class ContainerBootstrapper
|
||||
*/
|
||||
private function bootstrapFreshContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): Container {
|
||||
$container = $this->createFreshContainer($basePath, $collector);
|
||||
$container = $this->createFreshContainer($basePath, $collector, $environment);
|
||||
|
||||
// Compile for next request (async in production)
|
||||
$this->compileContainerAsync($container);
|
||||
@@ -123,47 +129,87 @@ final readonly class ContainerBootstrapper
|
||||
*/
|
||||
private function createFreshContainer(
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): DefaultContainer {
|
||||
// Use the existing container or create new DefaultContainer
|
||||
$container = $this->container instanceof DefaultContainer
|
||||
? $this->container
|
||||
: new DefaultContainer();
|
||||
|
||||
$this->addRuntimeInstances($container, $basePath, $collector);
|
||||
// Environment must be registered first as it's required by other services
|
||||
// Check if already registered (e.g. by AppBootstrapper) to avoid overwriting
|
||||
if (! $container->has(Environment::class)) {
|
||||
$container->instance(Environment::class, $environment);
|
||||
}
|
||||
|
||||
$this->registerRuntimeServices($container, $basePath, $collector, $environment);
|
||||
$this->autowire($container);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add instances that must be created at runtime
|
||||
* Register all runtime services that must be created at runtime
|
||||
*/
|
||||
private function addRuntimeInstances(
|
||||
private function registerRuntimeServices(
|
||||
Container $container,
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): void {
|
||||
$this->registerCoreServices($container, $basePath, $collector);
|
||||
$this->registerRedisAndCache($container, $collector, $environment);
|
||||
$this->registerHttpServices($container);
|
||||
$this->registerDatabaseServices($container, $environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register core services: Clock, PathProvider, Logger, PerformanceCollector
|
||||
*/
|
||||
private function registerCoreServices(
|
||||
Container $container,
|
||||
string $basePath,
|
||||
PerformanceCollectorInterface $collector,
|
||||
): void {
|
||||
// Core services that need runtime data
|
||||
// Clock must be registered first as it's required by Logger
|
||||
// Only create if not already registered (e.g. by ClockInitializer)
|
||||
if (! $container->has(Clock::class)) {
|
||||
$container->instance(Clock::class, new SystemClock());
|
||||
}
|
||||
$clock = $container->get(Clock::class);
|
||||
|
||||
$container->instance(PathProvider::class, new PathProvider($basePath));
|
||||
$init = $container->get(LoggerInitializer::class);
|
||||
|
||||
$logger = $container->invoker->invoke(LoggerInitializer::class, '__invoke');
|
||||
|
||||
$container->instance(Logger::class, $logger);
|
||||
$container->instance(PerformanceCollectorInterface::class, $collector);
|
||||
$pool = new RedisPoolInitializer($container->get(Environment::class))->initialize();
|
||||
$container->instance(Cache::class, new CacheInitializer($collector, $container, $pool)());
|
||||
|
||||
// Initialize Logger
|
||||
$logger = $container->invoker->invoke(LoggerInitializer::class, '__invoke');
|
||||
$container->instance(Logger::class, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Redis connection pool and Cache services
|
||||
* Requires Environment to be registered in container
|
||||
*/
|
||||
private function registerRedisAndCache(
|
||||
Container $container,
|
||||
PerformanceCollectorInterface $collector,
|
||||
Environment $environment,
|
||||
): void {
|
||||
$pool = new RedisPoolInitializer($environment)->initialize();
|
||||
$container->instance(Cache::class, new CacheInitializer($collector, $container, $pool)());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register HTTP-related services
|
||||
*
|
||||
* TODO: Remove manual Request binding once Discovery system properly handles RequestFactory Initializer
|
||||
* The RequestFactory should be discovered via Initializer attribute and registered automatically
|
||||
*/
|
||||
private function registerHttpServices(Container $container): void {
|
||||
$container->instance(ResponseEmitter::class, new ResponseEmitter());
|
||||
|
||||
// TEMPORARY FIX: Manual RequestFactory binding until Discovery issue is resolved
|
||||
// TEMPORARY FIX: Manual Request binding until Discovery issue is resolved
|
||||
// This should be handled by RequestFactory Initializer via Discovery system
|
||||
$container->singleton(Request::class, function ($container) {
|
||||
error_log("ContainerBootstrapper: Creating Request singleton");
|
||||
|
||||
@@ -179,13 +225,24 @@ final readonly class ContainerBootstrapper
|
||||
|
||||
return $request;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register database-related services
|
||||
*
|
||||
* TODO: Remove manual DatabasePlatform binding once Discovery system properly handles DatabasePlatformInitializer
|
||||
* The DatabasePlatformInitializer should be discovered via Initializer attribute and registered automatically
|
||||
*/
|
||||
private function registerDatabaseServices(
|
||||
Container $container,
|
||||
Environment $environment,
|
||||
): void {
|
||||
// TEMPORARY FIX: Manual DatabasePlatform binding until Initializer Discovery issue is resolved
|
||||
$container->singleton(\App\Framework\Database\Platform\DatabasePlatform::class, function ($container) {
|
||||
// This should be handled by DatabasePlatformInitializer via Discovery system
|
||||
$container->singleton(\App\Framework\Database\Platform\DatabasePlatform::class, function ($container) use ($environment) {
|
||||
error_log("ContainerBootstrapper: Creating DatabasePlatform singleton");
|
||||
|
||||
$env = $container->get(\App\Framework\Config\Environment::class);
|
||||
$driver = $env->get('DB_DRIVER', 'pgsql');
|
||||
$driver = $environment->get('DB_DRIVER', 'pgsql');
|
||||
|
||||
error_log("ContainerBootstrapper: DB_DRIVER = {$driver}");
|
||||
|
||||
@@ -200,7 +257,6 @@ final readonly class ContainerBootstrapper
|
||||
|
||||
return $platform;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user