Some checks failed
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Failing after 40s
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
🚀 Build & Deploy Image / Determine Build Necessity (push) Successful in 46s
Security Vulnerability Scan / Check for Dependency Changes (push) Successful in 1m0s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Successful in 11s
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
Security Vulnerability Scan / Composer Security Audit (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Successful in 12s
458 lines
13 KiB
Markdown
458 lines
13 KiB
Markdown
# Initial Deployment Troubleshooting Guide
|
|
|
|
**Stand:** 2025-11-07
|
|
**Status:** Vollständige Dokumentation aller Initial Deployment Probleme und Lösungen
|
|
|
|
---
|
|
|
|
## Übersicht
|
|
|
|
Dieses Dokument dokumentiert alle Probleme, die während des Initial Deployments aufgetreten sind, sowie deren Lösungen. Diese Informationen sind für zukünftige Troubleshooting-Sessions und als Referenz für ähnliche Probleme gedacht.
|
|
|
|
**📖 Verwandte Dokumentation:**
|
|
- [Initial Deployment Guide](../guides/initial-deployment-guide.md) - Schritt-für-Schritt Anleitung
|
|
- [Application Stack Deployment](../reference/application-stack.md) - Detaillierter Deployment-Ablauf
|
|
- [Gitea, Traefik & CI/CD Setup](gitea-traefik-cicd-setup.md) ⭐ - Komplette Anleitung für Gitea, Traefik und CI/CD Setup
|
|
|
|
---
|
|
|
|
## Problem 1: Missing Docker Secret Files
|
|
|
|
### Symptom
|
|
```
|
|
ERROR: secret file production_db_user_password does not exist
|
|
```
|
|
|
|
### Root Cause
|
|
Docker Secrets werden aus Dateien in `/home/deploy/deployment/stacks/production/secrets/` geladen. Diese Dateien existieren nicht, wenn sie nicht explizit erstellt werden.
|
|
|
|
### Lösung
|
|
**Datei:** `deployment/ansible/roles/application/tasks/sync.yml`
|
|
|
|
**Hinzugefügte Tasks:**
|
|
```yaml
|
|
- name: Ensure secrets directory exists
|
|
file:
|
|
path: "{{ application_stack_dest }}/secrets"
|
|
state: directory
|
|
mode: '0700'
|
|
|
|
- name: Create Docker Secret files from Ansible Vault
|
|
copy:
|
|
content: "{{ vault_db_user_password }}"
|
|
dest: "{{ application_stack_dest }}/secrets/db_user_password.txt"
|
|
mode: '0600'
|
|
# ... weitere Secrets
|
|
```
|
|
|
|
**Playbook ausführen:**
|
|
```bash
|
|
ansible-playbook -i inventory/production.yml \
|
|
playbooks/setup-production-secrets.yml \
|
|
--vault-password-file secrets/.vault_pass
|
|
```
|
|
|
|
### Prävention
|
|
- Secrets werden automatisch bei jedem Deployment erstellt
|
|
- Ansible Vault wird als Single Source of Truth verwendet
|
|
|
|
---
|
|
|
|
## Problem 2: Entrypoint Script "no such file or directory"
|
|
|
|
### Symptom
|
|
```
|
|
exec /usr/local/bin/entrypoint.sh: no such file or directory
|
|
```
|
|
|
|
Container startet nicht und crasht in einer Endlosschleife.
|
|
|
|
### Root Cause
|
|
**CRLF Line Endings** im `docker/entrypoint.sh` Script. Linux Shells können `#!/bin/bash\r\n` nicht interpretieren (das `\r` verursacht Probleme).
|
|
|
|
**Verifikation:**
|
|
```bash
|
|
# Im Container oder auf Server
|
|
file /usr/local/bin/entrypoint.sh
|
|
# Output: ASCII text, with CRLF line terminators
|
|
```
|
|
|
|
### Lösung
|
|
|
|
**1. `.gitattributes` erstellen/aktualisieren:**
|
|
```gitattributes
|
|
*.sh text eol=lf
|
|
Dockerfile text eol=lf
|
|
docker-compose*.yml text eol=lf
|
|
*.php text eol=lf
|
|
```
|
|
|
|
**2. Line Endings im Build-Prozess konvertieren:**
|
|
**Datei:** `deployment/ansible/playbooks/build-initial-image.yml`
|
|
|
|
```yaml
|
|
- name: Convert entrypoint script to LF line endings
|
|
shell: |
|
|
sed -i 's/\r$//' docker/entrypoint.sh
|
|
delegate_to: localhost
|
|
changed_when: true
|
|
```
|
|
|
|
**3. Verifikation im gebauten Image:**
|
|
```yaml
|
|
- name: Verify entrypoint script in built image
|
|
shell: |
|
|
docker create --name temp-check localhost:5000/framework:latest
|
|
docker cp temp-check:/usr/local/bin/entrypoint.sh /tmp/entrypoint_check.sh
|
|
docker rm temp-check
|
|
file /tmp/entrypoint_check.sh
|
|
register: entrypoint_file_check
|
|
```
|
|
|
|
### Prävention
|
|
- `.gitattributes` erzwingt LF für alle Shell Scripts
|
|
- Build-Prozess konvertiert automatisch zu LF
|
|
- Image-Verifikation prüft Line Endings
|
|
|
|
---
|
|
|
|
## Problem 3: Missing Application Code (worker.php, console.php)
|
|
|
|
### Symptom
|
|
```
|
|
PHP Warning: require(/var/www/html/worker.php): Failed to open stream: No such file or directory
|
|
```
|
|
|
|
Container `queue-worker` und `scheduler` crashen, da `worker.php` und `console.php` fehlen.
|
|
|
|
### Root Cause
|
|
Application Code wurde nicht zum Server synchronisiert. Der Code existiert nur lokal im Repository, nicht auf dem Server in `/home/deploy/michaelschiemer/current`.
|
|
|
|
### Lösung
|
|
**Playbook:** `deployment/ansible/playbooks/sync-application-code.yml`
|
|
|
|
**Was passiert:**
|
|
- Code wird via `rsync` vom lokalen Repository zum Server synchronisiert
|
|
- Ziel: `/home/deploy/michaelschiemer/current`
|
|
- Excludes: `vendor`, `node_modules`, `.env`, `deployment`, `docker`, `docs`, `tests`
|
|
- Executable Permissions werden gesetzt
|
|
|
|
**Ausführen:**
|
|
```bash
|
|
ansible-playbook -i inventory/production.yml \
|
|
playbooks/sync-application-code.yml \
|
|
--vault-password-file secrets/.vault_pass
|
|
```
|
|
|
|
**Verifikation:**
|
|
```bash
|
|
# Auf Server prüfen
|
|
ls -la /home/deploy/michaelschiemer/current/worker.php
|
|
ls -la /home/deploy/michaelschiemer/current/console.php
|
|
```
|
|
|
|
### Prävention
|
|
- Initial Deployment Guide dokumentiert Code-Sync als erforderlichen Schritt
|
|
- Playbook verifiziert automatisch, ob kritische Dateien existieren
|
|
|
|
---
|
|
|
|
## Problem 4: Missing vendor/autoload.php
|
|
|
|
### Symptom
|
|
```
|
|
PHP Warning: require(/var/www/html/vendor/autoload.php): Failed to open stream: No such file or directory
|
|
```
|
|
|
|
Container `queue-worker` und `scheduler` crashen nach Code-Sync.
|
|
|
|
### Root Cause
|
|
`vendor` Directory wird **nicht** via `rsync` synchronisiert (ist in Excludes). Composer Dependencies müssen im Container installiert werden.
|
|
|
|
### Lösung
|
|
**Playbook:** `deployment/ansible/playbooks/install-composer-dependencies.yml`
|
|
|
|
**Was passiert:**
|
|
- `composer install --no-dev --optimize-autoloader` wird im `php` Container ausgeführt
|
|
- `vendor/autoload.php` wird erstellt
|
|
- `queue-worker` und `scheduler` werden neu gestartet
|
|
|
|
**Ausführen:**
|
|
```bash
|
|
ansible-playbook -i inventory/production.yml \
|
|
playbooks/install-composer-dependencies.yml \
|
|
--vault-password-file secrets/.vault_pass
|
|
```
|
|
|
|
**Verifikation:**
|
|
```bash
|
|
# Im Container prüfen
|
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
|
exec php test -f /var/www/html/vendor/autoload.php && echo "EXISTS"
|
|
```
|
|
|
|
### Prävention
|
|
- Initial Deployment Guide dokumentiert Composer Installation als erforderlichen Schritt
|
|
- Playbook verifiziert automatisch, ob `vendor/autoload.php` existiert
|
|
|
|
---
|
|
|
|
## Problem 5: PHP-FPM Permission Error (setgid)
|
|
|
|
### Symptom
|
|
```
|
|
ERROR: [pool www] failed to setgid(33): Operation not permitted (1)
|
|
```
|
|
|
|
Container `web` ist `unhealthy`, PHP-FPM kann nicht starten.
|
|
|
|
### Root Cause
|
|
PHP-FPM benötigt `SETGID` und `SETUID` Capabilities, um zum `www-data` User (UID 33) zu wechseln. Docker Security Settings (`no-new-privileges: true`) verhindern dies.
|
|
|
|
### Lösung
|
|
**Datei:** `docker-compose.production.yml`
|
|
|
|
**Änderungen:**
|
|
```yaml
|
|
web:
|
|
# Security hardening
|
|
# Note: no-new-privileges prevents PHP-FPM from switching to www-data
|
|
# We need to allow privilege escalation for PHP-FPM user switching
|
|
# security_opt:
|
|
# - no-new-privileges:true # ← Kommentiert
|
|
cap_drop:
|
|
- ALL
|
|
cap_add:
|
|
- CHOWN
|
|
- DAC_OVERRIDE
|
|
- NET_BIND_SERVICE # Required for binding to ports 80/443
|
|
- SETGID # ← Hinzugefügt: Required for PHP-FPM to switch to www-data
|
|
- SETUID # ← Hinzugefügt: Required for PHP-FPM to switch to www-data
|
|
```
|
|
|
|
**Container neu erstellen:**
|
|
```bash
|
|
ansible-playbook -i inventory/production.yml \
|
|
playbooks/fix-web-container.yml \
|
|
--vault-password-file secrets/.vault_pass
|
|
```
|
|
|
|
**Oder manuell:**
|
|
```bash
|
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
|
up -d --force-recreate web
|
|
```
|
|
|
|
### Prävention
|
|
- Docker Compose Konfiguration enthält korrekte Capabilities
|
|
- Security Trade-off dokumentiert: `no-new-privileges` vs. PHP-FPM User Switching
|
|
|
|
---
|
|
|
|
## Problem 6: Environment Variables Missing (DB_DATABASE)
|
|
|
|
### Symptom
|
|
```
|
|
Uncaught App\Framework\Config\Exceptions\RequiredEnvironmentVariableException:
|
|
Required environment variable 'DB_DATABASE' is not set.
|
|
```
|
|
|
|
Container `queue-worker` und `scheduler` crashen, obwohl `.env` Datei auf dem Host existiert.
|
|
|
|
### Root Cause
|
|
Docker Compose `env_file` verwendet **relative Pfade**. Wenn `docker compose` nicht im richtigen Verzeichnis ausgeführt wird, wird die `.env` Datei nicht gefunden.
|
|
|
|
**Problem:**
|
|
```yaml
|
|
env_file:
|
|
- .env # ← Relativer Pfad - funktioniert nur wenn docker compose im richtigen Verzeichnis ausgeführt wird
|
|
```
|
|
|
|
### Lösung
|
|
**Datei:** `docker-compose.production.yml`
|
|
|
|
**Änderungen:**
|
|
```yaml
|
|
php:
|
|
# Load environment variables from .env file (generated by Ansible)
|
|
# Use absolute path to ensure .env is found regardless of working directory
|
|
env_file:
|
|
- /home/deploy/deployment/stacks/production/.env # ← Absoluter Pfad
|
|
|
|
queue-worker:
|
|
env_file:
|
|
- /home/deploy/deployment/stacks/production/.env # ← Absoluter Pfad
|
|
|
|
scheduler:
|
|
env_file:
|
|
- /home/deploy/deployment/stacks/production/.env # ← Absoluter Pfad
|
|
```
|
|
|
|
**Container neu erstellen:**
|
|
```bash
|
|
ansible-playbook -i inventory/production.yml \
|
|
playbooks/recreate-containers-with-env.yml \
|
|
--vault-password-file secrets/.vault_pass
|
|
```
|
|
|
|
**Oder manuell:**
|
|
```bash
|
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
|
up -d --force-recreate queue-worker scheduler php
|
|
```
|
|
|
|
### Prävention
|
|
- Docker Compose verwendet immer **absolute Pfade** für `env_file`
|
|
- Dokumentation erklärt Unterschied zwischen `env_file` und `environment`
|
|
|
|
---
|
|
|
|
## Problem 7: Container Entrypoint Override (queue-worker, scheduler)
|
|
|
|
### Symptom
|
|
Container `queue-worker` und `scheduler` starten PHP-FPM und Nginx (via `entrypoint.sh`), obwohl sie nur PHP CLI benötigen.
|
|
|
|
**Logs zeigen:**
|
|
```
|
|
Starting PHP-FPM...
|
|
Starting Nginx...
|
|
```
|
|
|
|
### Root Cause
|
|
Docker Image `ENTRYPOINT` ist `["/usr/local/bin/entrypoint.sh"]`. Alle Container, die dieses Image verwenden, führen automatisch den Entrypoint aus, auch wenn sie nur PHP CLI benötigen.
|
|
|
|
### Lösung
|
|
**Datei:** `docker-compose.production.yml`
|
|
|
|
**Änderungen:**
|
|
```yaml
|
|
queue-worker:
|
|
# Override entrypoint to skip PHP-FPM/Nginx startup - queue-worker only needs PHP CLI
|
|
entrypoint: "" # ← Leerer Entrypoint überschreibt Image Entrypoint
|
|
# Worker command - direct PHP execution
|
|
command: ["php", "/var/www/html/worker.php"]
|
|
|
|
scheduler:
|
|
# Override entrypoint to skip PHP-FPM/Nginx startup - scheduler only needs PHP CLI
|
|
entrypoint: "" # ← Leerer Entrypoint überschreibt Image Entrypoint
|
|
# Scheduler command - direct PHP execution
|
|
command: php console.php scheduler:run
|
|
```
|
|
|
|
**Container neu erstellen:**
|
|
```bash
|
|
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
|
|
up -d --force-recreate queue-worker scheduler
|
|
```
|
|
|
|
### Prävention
|
|
- Docker Compose Konfiguration überschreibt Entrypoint für CLI-only Container
|
|
- Dokumentation erklärt Unterschied zwischen `php` (PHP-FPM) und `queue-worker`/`scheduler` (PHP CLI)
|
|
|
|
---
|
|
|
|
## Problem 8: Ansible Debug Messages mit \n statt Zeilenumbrüchen
|
|
|
|
### Symptom
|
|
Ansible Debug Messages zeigen `\n` als Text statt als Zeilenumbrüche:
|
|
```
|
|
"msg": "Line 1\nLine 2\nLine 3"
|
|
```
|
|
|
|
### Root Cause
|
|
Ansible's Default Callback Plugin formatiert multiline `msg` Felder nicht automatisch.
|
|
|
|
### Lösung
|
|
**Custom Callback Plugin:** `deployment/ansible/callback_plugins/default_with_clean_msg.py`
|
|
|
|
**Funktionalität:**
|
|
- Erweitert Default Callback Plugin
|
|
- Formatiert multiline `msg` Felder als lesbare Blöcke mit Borders
|
|
- Unterdrückt Warnings bei Option Access
|
|
|
|
**Konfiguration:** `deployment/ansible/ansible.cfg`
|
|
```ini
|
|
[defaults]
|
|
stdout_callback = default_with_clean_msg
|
|
callback_plugins = ./callback_plugins
|
|
```
|
|
|
|
### Prävention
|
|
- Custom Callback Plugin ist Standard in Ansible Konfiguration
|
|
- Dokumentation erklärt Plugin-Funktionalität
|
|
|
|
---
|
|
|
|
## Problem 9: Jinja2 Template Errors in Ansible
|
|
|
|
### Symptom
|
|
```
|
|
ERROR! An unhandled exception occurred while templating '{{ application_stack_dest | default(app_stack_path | default(stacks_base_path + '/production')) }}'
|
|
```
|
|
|
|
### Root Cause
|
|
**Falsche Jinja2 Syntax:**
|
|
- `+` Operator für String-Konkatenation (sollte `~` sein)
|
|
- Rekursive Default-Werte (Variable verwendet sich selbst)
|
|
|
|
### Lösung
|
|
**Datei:** `deployment/ansible/roles/application/defaults/main.yml`
|
|
|
|
**Vorher (falsch):**
|
|
```yaml
|
|
application_stack_dest: "{{ app_stack_path | default(stacks_base_path + '/production') }}"
|
|
```
|
|
|
|
**Nachher (korrekt):**
|
|
```yaml
|
|
# Use ~ for string concatenation in Jinja2 templates
|
|
# Don't use application_stack_dest in the default chain to avoid recursion
|
|
application_stack_dest: "{{ app_stack_path | default((stacks_base_path | default('/home/deploy/deployment/stacks')) ~ '/production') }}"
|
|
```
|
|
|
|
### Prävention
|
|
- Jinja2 Best Practices dokumentiert
|
|
- Code Review prüft Template-Syntax
|
|
|
|
---
|
|
|
|
## Zusammenfassung: Häufigste Probleme
|
|
|
|
1. **Missing Secrets** → `setup-production-secrets.yml` ausführen
|
|
2. **CRLF Line Endings** → `.gitattributes` + Build-Prozess konvertiert zu LF
|
|
3. **Missing Code** → `sync-application-code.yml` ausführen
|
|
4. **Missing vendor** → `install-composer-dependencies.yml` ausführen
|
|
5. **PHP-FPM Permissions** → Docker Capabilities (SETGID/SETUID) hinzufügen
|
|
6. **Environment Variables** → Absolute Pfade für `env_file` verwenden
|
|
7. **Entrypoint Override** → `entrypoint: ""` für CLI-only Container
|
|
|
|
---
|
|
|
|
## Quick Reference: Troubleshooting Commands
|
|
|
|
```bash
|
|
# Container Status prüfen
|
|
ansible-playbook -i inventory/production.yml playbooks/check-container-status.yml
|
|
|
|
# Container Logs prüfen
|
|
ansible-playbook -i inventory/production.yml playbooks/check-container-logs.yml
|
|
|
|
# Final Status prüfen
|
|
ansible-playbook -i inventory/production.yml playbooks/check-final-status.yml
|
|
|
|
# Container Issues beheben
|
|
ansible-playbook -i inventory/production.yml playbooks/fix-container-issues.yml
|
|
|
|
# Web Container beheben
|
|
ansible-playbook -i inventory/production.yml playbooks/fix-web-container.yml
|
|
```
|
|
|
|
---
|
|
|
|
## Referenz
|
|
|
|
- [Initial Deployment Guide](../guides/initial-deployment-guide.md) - Schritt-für-Schritt Anleitung
|
|
- [Application Stack Deployment](../reference/application-stack.md) - Detaillierter Ablauf
|
|
- [Deployment Commands](../guides/deployment-commands.md) - Command-Referenz
|
|
|