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
|
### View Logs
|
||||||
```bash
|
```bash
|
||||||
# Application logs
|
# Application logs (Production)
|
||||||
docker compose -f stacks/application/docker-compose.yml logs -f app
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml logs -f php
|
||||||
|
|
||||||
# Traefik logs
|
# Traefik logs
|
||||||
docker compose -f stacks/traefik/docker-compose.yml logs -f
|
docker compose -f stacks/traefik/docker-compose.yml logs -f
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ all:
|
|||||||
# Only override-specific variables should be here
|
# Only override-specific variables should be here
|
||||||
# Override system_* defaults here when Wartungsfenster abweichen
|
# Override system_* defaults here when Wartungsfenster abweichen
|
||||||
|
|
||||||
# Legacy compose_file reference (deprecated - stacks now use deployment/stacks/)
|
# Legacy compose_file reference (deprecated)
|
||||||
compose_file: "{{ stacks_base_path }}/application/docker-compose.yml"
|
# 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**
|
**1. Backup aktueller Deployment-Status**
|
||||||
```bash
|
```bash
|
||||||
# Speichert aktuellen Container-Status
|
# 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
|
ps --format json > backups/<timestamp>/current_containers.json
|
||||||
|
|
||||||
# Speichert aktuelle docker-compose.yml Konfiguration
|
# 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
|
config > backups/<timestamp>/docker-compose-config.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ replace: '\1{{ app_image }}:{{ image_tag }}'
|
|||||||
**5. Application Stack neu starten**
|
**5. Application Stack neu starten**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
||||||
up -d \
|
up -d \
|
||||||
--pull always \
|
--pull always \
|
||||||
--force-recreate \
|
--force-recreate \
|
||||||
@@ -547,7 +547,7 @@ docker pull registry.michaelschiemer.de/framework:<tag>
|
|||||||
```bash
|
```bash
|
||||||
# Prüfe ob Regex korrekt ist
|
# Prüfe ob Regex korrekt ist
|
||||||
grep -E "image:\s+registry.michaelschiemer.de/framework" \
|
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
|
# Prüfe Backup für vorherige Version
|
||||||
ls -la ~/deployment/backups/
|
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)
|
# Mount .env file from shared directory (production environment variables)
|
||||||
- /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro
|
- /home/deploy/michaelschiemer/shared/.env.production:/var/www/html/.env:ro
|
||||||
|
|
||||||
db:
|
# Database service removed - using external PostgreSQL Stack (deployment/stacks/postgresql/)
|
||||||
# Production restart policy
|
# Connection via app-internal network using docker-compose.postgres-override.yml
|
||||||
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"
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
# Production restart policy
|
# Production restart policy
|
||||||
@@ -333,12 +293,91 @@ services:
|
|||||||
|
|
||||||
# Wait for dependencies to be healthy before starting
|
# Wait for dependencies to be healthy before starting
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
php:
|
php:
|
||||||
condition: service_healthy
|
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 Sidecar Container for Let's Encrypt
|
||||||
certbot:
|
certbot:
|
||||||
@@ -385,11 +424,5 @@ volumes:
|
|||||||
storage:
|
storage:
|
||||||
driver: local
|
driver: local
|
||||||
|
|
||||||
# Database volume with backup driver (optional)
|
# Database volume removed - using external PostgreSQL Stack
|
||||||
db_data:
|
# PostgreSQL data is managed by deployment/stacks/postgresql/
|
||||||
driver: local
|
|
||||||
# Optional: Use external volume for easier backups
|
|
||||||
# driver_opts:
|
|
||||||
# type: none
|
|
||||||
# o: bind
|
|
||||||
# device: /mnt/db-backups/michaelschiemer-prod
|
|
||||||
|
|||||||
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
|
- TZ=Europe/Berlin
|
||||||
- APP_ENV=staging
|
- APP_ENV=staging
|
||||||
- APP_DEBUG=${APP_DEBUG:-true}
|
- APP_DEBUG=${APP_DEBUG:-true}
|
||||||
# Database
|
# Database (can share with production or use separate)
|
||||||
- DB_HOST=${DB_HOST:-postgres}
|
- DB_HOST=${DB_HOST:-postgres}
|
||||||
- DB_PORT=${DB_PORT:-5432}
|
- DB_PORT=${DB_PORT:-5432}
|
||||||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||||||
@@ -408,7 +408,7 @@ services:
|
|||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- APP_ENV=staging
|
- APP_ENV=staging
|
||||||
- APP_DEBUG=${APP_DEBUG:-true}
|
- APP_DEBUG=${APP_DEBUG:-true}
|
||||||
# Database
|
# Database (can share with production or use separate)
|
||||||
- DB_HOST=${DB_HOST:-postgres}
|
- DB_HOST=${DB_HOST:-postgres}
|
||||||
- DB_PORT=${DB_PORT:-5432}
|
- DB_PORT=${DB_PORT:-5432}
|
||||||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||||||
@@ -471,6 +471,9 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
staging-internal:
|
staging-internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
app-internal:
|
||||||
|
external: true
|
||||||
|
name: app-internal
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
staging-code:
|
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
|
// Initialize environment with encryption support
|
||||||
$env = $this->initializeEnvironment();
|
$env = $this->initializeEnvironment();
|
||||||
|
|
||||||
|
|
||||||
// Workaround: If REDIS_PASSWORD_FILE is not set but expected file exists, set it manually
|
// 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
|
// This handles cases where Docker Compose doesn't set the variable correctly
|
||||||
$expectedRedisPasswordFile = '/run/secrets/redis_password';
|
#$expectedRedisPasswordFile = '/run/secrets/redis_password';
|
||||||
if (!$env->has('REDIS_PASSWORD_FILE') && file_exists($expectedRedisPasswordFile)) {
|
#if (!$env->has('REDIS_PASSWORD_FILE') && file_exists($expectedRedisPasswordFile)) {
|
||||||
// Add REDIS_PASSWORD_FILE to environment if file exists
|
# // Add REDIS_PASSWORD_FILE to environment if file exists
|
||||||
$env = $env->withVariable('REDIS_PASSWORD_FILE', $expectedRedisPasswordFile);
|
# #$env = $env->withVariable('REDIS_PASSWORD_FILE', $expectedRedisPasswordFile);
|
||||||
}
|
#
|
||||||
|
#}
|
||||||
|
|
||||||
// Make Environment available throughout the application
|
// Make Environment available throughout the application
|
||||||
$this->container->instance(Environment::class, $env);
|
$this->container->instance(Environment::class, $env);
|
||||||
@@ -128,12 +130,13 @@ final readonly class AppBootstrapper
|
|||||||
{
|
{
|
||||||
$this->collector->startTiming('bootstrap', PerformanceCategory::SYSTEM);
|
$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');
|
$this->collector->endTiming('bootstrap');
|
||||||
|
|
||||||
// Initialize secrets management after container is bootstrapped
|
// Initialize secrets management after container is bootstrapped
|
||||||
$env = $this->container->get(Environment::class);
|
|
||||||
$this->initializeSecretsManagement($env);
|
$this->initializeSecretsManagement($env);
|
||||||
|
|
||||||
// ErrorHandler wird jetzt kontextabhängig registriert
|
// ErrorHandler wird jetzt kontextabhängig registriert
|
||||||
|
|||||||
@@ -42,16 +42,17 @@ final readonly class ContainerBootstrapper
|
|||||||
public function bootstrap(
|
public function bootstrap(
|
||||||
string $basePath,
|
string $basePath,
|
||||||
PerformanceCollectorInterface $collector,
|
PerformanceCollectorInterface $collector,
|
||||||
|
Environment $environment,
|
||||||
): Container {
|
): Container {
|
||||||
// Temporarily disable compiled container to fix bootstrap issues
|
// Temporarily disable compiled container to fix bootstrap issues
|
||||||
// TODO: Re-enable once container interface compatibility is fixed
|
// TODO: Re-enable once container interface compatibility is fixed
|
||||||
// $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector);
|
// $optimizedContainer = $this->tryLoadCompiledContainer($basePath, $collector, $environment);
|
||||||
// if ($optimizedContainer !== null) {
|
// if ($optimizedContainer !== null) {
|
||||||
// return $optimizedContainer;
|
// return $optimizedContainer;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Fallback to fresh container bootstrap
|
// 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(
|
private function tryLoadCompiledContainer(
|
||||||
string $basePath,
|
string $basePath,
|
||||||
PerformanceCollectorInterface $collector
|
PerformanceCollectorInterface $collector,
|
||||||
|
Environment $environment,
|
||||||
): ?Container {
|
): ?Container {
|
||||||
try {
|
try {
|
||||||
$cacheDir = sys_get_temp_dir() . '/framework-cache';
|
$cacheDir = sys_get_temp_dir() . '/framework-cache';
|
||||||
@@ -71,7 +73,7 @@ final readonly class ContainerBootstrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create temporary fresh container to validate against
|
// Create temporary fresh container to validate against
|
||||||
$tempContainer = $this->createFreshContainer($basePath, $collector);
|
$tempContainer = $this->createFreshContainer($basePath, $collector, $environment);
|
||||||
|
|
||||||
// Create compiler for validation
|
// Create compiler for validation
|
||||||
$reflectionProvider = new CachedReflectionProvider();
|
$reflectionProvider = new CachedReflectionProvider();
|
||||||
@@ -86,8 +88,11 @@ final readonly class ContainerBootstrapper
|
|||||||
// Load and return compiled container
|
// Load and return compiled container
|
||||||
$compiledContainer = ContainerCompiler::load($compiledPath);
|
$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
|
// Add runtime instances that can't be compiled
|
||||||
$this->addRuntimeInstances($compiledContainer, $basePath, $collector);
|
$this->registerRuntimeServices($compiledContainer, $basePath, $collector, $environment);
|
||||||
|
|
||||||
return $compiledContainer;
|
return $compiledContainer;
|
||||||
|
|
||||||
@@ -108,9 +113,10 @@ final readonly class ContainerBootstrapper
|
|||||||
*/
|
*/
|
||||||
private function bootstrapFreshContainer(
|
private function bootstrapFreshContainer(
|
||||||
string $basePath,
|
string $basePath,
|
||||||
PerformanceCollectorInterface $collector
|
PerformanceCollectorInterface $collector,
|
||||||
|
Environment $environment,
|
||||||
): Container {
|
): Container {
|
||||||
$container = $this->createFreshContainer($basePath, $collector);
|
$container = $this->createFreshContainer($basePath, $collector, $environment);
|
||||||
|
|
||||||
// Compile for next request (async in production)
|
// Compile for next request (async in production)
|
||||||
$this->compileContainerAsync($container);
|
$this->compileContainerAsync($container);
|
||||||
@@ -123,47 +129,87 @@ final readonly class ContainerBootstrapper
|
|||||||
*/
|
*/
|
||||||
private function createFreshContainer(
|
private function createFreshContainer(
|
||||||
string $basePath,
|
string $basePath,
|
||||||
PerformanceCollectorInterface $collector
|
PerformanceCollectorInterface $collector,
|
||||||
|
Environment $environment,
|
||||||
): DefaultContainer {
|
): DefaultContainer {
|
||||||
// Use the existing container or create new DefaultContainer
|
// Use the existing container or create new DefaultContainer
|
||||||
$container = $this->container instanceof DefaultContainer
|
$container = $this->container instanceof DefaultContainer
|
||||||
? $this->container
|
? $this->container
|
||||||
: new DefaultContainer();
|
: 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);
|
$this->autowire($container);
|
||||||
|
|
||||||
return $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,
|
Container $container,
|
||||||
string $basePath,
|
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 {
|
): void {
|
||||||
// Core services that need runtime data
|
|
||||||
// Clock must be registered first as it's required by Logger
|
// Clock must be registered first as it's required by Logger
|
||||||
// Only create if not already registered (e.g. by ClockInitializer)
|
// Only create if not already registered (e.g. by ClockInitializer)
|
||||||
if (! $container->has(Clock::class)) {
|
if (! $container->has(Clock::class)) {
|
||||||
$container->instance(Clock::class, new SystemClock());
|
$container->instance(Clock::class, new SystemClock());
|
||||||
}
|
}
|
||||||
$clock = $container->get(Clock::class);
|
|
||||||
$container->instance(PathProvider::class, new PathProvider($basePath));
|
$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);
|
$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());
|
$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) {
|
$container->singleton(Request::class, function ($container) {
|
||||||
error_log("ContainerBootstrapper: Creating Request singleton");
|
error_log("ContainerBootstrapper: Creating Request singleton");
|
||||||
|
|
||||||
@@ -179,13 +225,24 @@ final readonly class ContainerBootstrapper
|
|||||||
|
|
||||||
return $request;
|
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
|
// 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");
|
error_log("ContainerBootstrapper: Creating DatabasePlatform singleton");
|
||||||
|
|
||||||
$env = $container->get(\App\Framework\Config\Environment::class);
|
$driver = $environment->get('DB_DRIVER', 'pgsql');
|
||||||
$driver = $env->get('DB_DRIVER', 'pgsql');
|
|
||||||
|
|
||||||
error_log("ContainerBootstrapper: DB_DRIVER = {$driver}");
|
error_log("ContainerBootstrapper: DB_DRIVER = {$driver}");
|
||||||
|
|
||||||
@@ -200,7 +257,6 @@ final readonly class ContainerBootstrapper
|
|||||||
|
|
||||||
return $platform;
|
return $platform;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user