13 KiB
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 - Schritt-für-Schritt Anleitung
- Application Stack Deployment - Detaillierter Deployment-Ablauf
- Gitea, Traefik & CI/CD Setup ⭐ - 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:
- 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:
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:
# Im Container oder auf Server
file /usr/local/bin/entrypoint.sh
# Output: ASCII text, with CRLF line terminators
Lösung
1. .gitattributes erstellen/aktualisieren:
*.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
- 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:
- 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
.gitattributeserzwingt 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
rsyncvom 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:
ansible-playbook -i inventory/production.yml \
playbooks/sync-application-code.yml \
--vault-password-file secrets/.vault_pass
Verifikation:
# 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-autoloaderwird imphpContainer ausgeführtvendor/autoload.phpwird erstelltqueue-workerundschedulerwerden neu gestartet
Ausführen:
ansible-playbook -i inventory/production.yml \
playbooks/install-composer-dependencies.yml \
--vault-password-file secrets/.vault_pass
Verifikation:
# 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.phpexistiert
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:
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:
ansible-playbook -i inventory/production.yml \
playbooks/fix-web-container.yml \
--vault-password-file secrets/.vault_pass
Oder manuell:
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-privilegesvs. 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:
env_file:
- .env # ← Relativer Pfad - funktioniert nur wenn docker compose im richtigen Verzeichnis ausgeführt wird
Lösung
Datei: docker-compose.production.yml
Änderungen:
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:
ansible-playbook -i inventory/production.yml \
playbooks/recreate-containers-with-env.yml \
--vault-password-file secrets/.vault_pass
Oder manuell:
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_fileundenvironment
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:
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:
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) undqueue-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
msgFelder als lesbare Blöcke mit Borders - Unterdrückt Warnings bei Option Access
Konfiguration: deployment/ansible/ansible.cfg
[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):
application_stack_dest: "{{ app_stack_path | default(stacks_base_path + '/production') }}"
Nachher (korrekt):
# 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
- Missing Secrets →
setup-production-secrets.ymlausführen - CRLF Line Endings →
.gitattributes+ Build-Prozess konvertiert zu LF - Missing Code →
sync-application-code.ymlausführen - Missing vendor →
install-composer-dependencies.ymlausführen - PHP-FPM Permissions → Docker Capabilities (SETGID/SETUID) hinzufügen
- Environment Variables → Absolute Pfade für
env_fileverwenden - Entrypoint Override →
entrypoint: ""für CLI-only Container
Quick Reference: Troubleshooting Commands
# 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 - Schritt-für-Schritt Anleitung
- Application Stack Deployment - Detaillierter Ablauf
- Deployment Commands - Command-Referenz