Files
michaelschiemer/deployment/docs/troubleshooting/initial-deployment-issues.md
Michael Schiemer 1b9cda6dd3
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
docs: Add CI image setup documentation
2025-11-08 13:38:46 +01:00

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:


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

  • .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:

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-autoloader wird im php Container ausgeführt
  • vendor/autoload.php wird erstellt
  • queue-worker und scheduler werden 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.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:

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-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:

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_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:

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) 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

[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

  1. Missing Secretssetup-production-secrets.yml ausführen
  2. CRLF Line Endings.gitattributes + Build-Prozess konvertiert zu LF
  3. Missing Codesync-application-code.yml ausführen
  4. Missing vendorinstall-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 Overrideentrypoint: "" 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