# 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