docs: Add CI image setup documentation
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

This commit is contained in:
2025-11-08 13:38:46 +01:00
parent 50e58c6ba9
commit 1b9cda6dd3
4 changed files with 1127 additions and 9 deletions

View File

@@ -0,0 +1,457 @@
# 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