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
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:
@@ -1,7 +1,7 @@
|
|||||||
# Deployment Dokumentation - Index
|
# Deployment Dokumentation - Index
|
||||||
|
|
||||||
**Stand:** 2025-11-01
|
**Stand:** 2025-11-07
|
||||||
**Status:** ? Vollst?ndige Dokumentation vorhanden
|
**Status:** ✅ Vollständige Dokumentation vorhanden (inkl. CI/CD, Backup & Rollback)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,13 +33,36 @@
|
|||||||
- Secrets Konfiguration
|
- Secrets Konfiguration
|
||||||
- Schritt-f?r-Schritt Anleitung
|
- Schritt-f?r-Schritt Anleitung
|
||||||
|
|
||||||
4. **[deployment-commands.md](guides/deployment-commands.md)**
|
4. **[server-directory-structure.md](server-directory-structure.md)**
|
||||||
- Command-Referenz f?r Deployment
|
- Erklärung der Server-Verzeichnisstruktur
|
||||||
|
- Unterschied zwischen `~/deployment/` und `~/michaelschiemer/`
|
||||||
|
- Verwendungszweck und Wartung
|
||||||
|
- Troubleshooting
|
||||||
|
|
||||||
|
5. **[deployment-commands.md](guides/deployment-commands.md)**
|
||||||
|
- Command-Referenz für Deployment
|
||||||
- Ansible Playbook Befehle
|
- Ansible Playbook Befehle
|
||||||
- Docker Compose Kommandos
|
- Docker Compose Kommandos
|
||||||
- Troubleshooting Kommandos
|
- Troubleshooting Kommandos
|
||||||
|
|
||||||
5. **[vault-password.md](guides/vault-password.md)**
|
6. **[cicd-workflow-guide.md](guides/cicd-workflow-guide.md)** ⭐
|
||||||
|
- CI/CD Workflow-Dokumentation
|
||||||
|
- Automatische und manuelle Deployments
|
||||||
|
- Ansible Playbooks in Workflows
|
||||||
|
- Troubleshooting
|
||||||
|
|
||||||
|
7. **[backup-and-rollback-guide.md](guides/backup-and-rollback-guide.md)**
|
||||||
|
- Backup-Strategie
|
||||||
|
- Rollback-Prozess
|
||||||
|
- Automatisierung
|
||||||
|
- Best Practices
|
||||||
|
|
||||||
|
7. **[pipeline-testing-guide.md](guides/pipeline-testing-guide.md)** ⭐
|
||||||
|
- End-to-End Pipeline-Tests
|
||||||
|
- Staging & Production Test-Anleitung
|
||||||
|
- Verifikation & Troubleshooting
|
||||||
|
|
||||||
|
8. **[vault-password.md](guides/vault-password.md)**
|
||||||
- Ansible Vault Password Dokumentation
|
- Ansible Vault Password Dokumentation
|
||||||
- Setup und Verwaltung
|
- Setup und Verwaltung
|
||||||
- CI/CD Integration
|
- CI/CD Integration
|
||||||
@@ -47,7 +70,7 @@
|
|||||||
|
|
||||||
### Deployment-Prozess
|
### Deployment-Prozess
|
||||||
|
|
||||||
6. **[application-stack.md](reference/application-stack.md)**
|
9. **[application-stack.md](reference/application-stack.md)**
|
||||||
- Detaillierter Deployment-Ablauf Schritt f?r Schritt
|
- Detaillierter Deployment-Ablauf Schritt f?r Schritt
|
||||||
- Was passiert bei jedem Deployment
|
- Was passiert bei jedem Deployment
|
||||||
- Container-Neustart Details
|
- Container-Neustart Details
|
||||||
@@ -106,7 +129,7 @@
|
|||||||
|
|
||||||
### Application Stack
|
### Application Stack
|
||||||
|
|
||||||
- **[stacks/application/README.md](../stacks/application/README.md)** - Application Stack Details
|
- **[stacks/production/README.md](../stacks/production/README.md)** - Application Stack Details
|
||||||
- **[ansible/README.md](../ansible/README.md)** - Ansible Playbooks Dokumentation
|
- **[ansible/README.md](../ansible/README.md)** - Ansible Playbooks Dokumentation
|
||||||
|
|
||||||
### CI/CD
|
### CI/CD
|
||||||
@@ -136,6 +159,10 @@
|
|||||||
|
|
||||||
- **[staging-502-nginx-phpfpm.md](troubleshooting/staging-502-nginx-phpfpm.md)** - Staging 502 Bad Gateway: Nginx kann nicht zu PHP-FPM verbinden
|
- **[staging-502-nginx-phpfpm.md](troubleshooting/staging-502-nginx-phpfpm.md)** - Staging 502 Bad Gateway: Nginx kann nicht zu PHP-FPM verbinden
|
||||||
|
|
||||||
|
### Gitea & CI/CD Setup
|
||||||
|
|
||||||
|
- **[gitea-traefik-cicd-setup.md](troubleshooting/gitea-traefik-cicd-setup.md)** ⭐ - Komplette Anleitung: Gitea, Traefik & CI/CD Setup (alle Probleme und Lösungen)
|
||||||
|
|
||||||
### Test-Dokumentation
|
### Test-Dokumentation
|
||||||
|
|
||||||
- **[test-results.md](tests/test-results.md)** - Test Ergebnisse
|
- **[test-results.md](tests/test-results.md)** - Test Ergebnisse
|
||||||
@@ -200,5 +227,5 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Letzte Aktualisierung:** 2025-11-01
|
**Letzte Aktualisierung:** 2025-11-07
|
||||||
**Status:** ? Dokumentation vollst?ndig
|
**Status:** ✅ Dokumentation vollständig (inkl. CI/CD, Backup & Rollback)
|
||||||
|
|||||||
@@ -353,4 +353,5 @@ ansible-playbook -i inventory/production.yml playbooks/<playbook>.yml
|
|||||||
**Test-Ressourcen:**
|
**Test-Ressourcen:**
|
||||||
- [Pipeline Test Checklist](../guides/pipeline-test-checklist.md) ⭐ - Detaillierte Schritt-für-Schritt Anleitung
|
- [Pipeline Test Checklist](../guides/pipeline-test-checklist.md) ⭐ - Detaillierte Schritt-für-Schritt Anleitung
|
||||||
- [Pipeline Testing Guide](../guides/pipeline-testing-guide.md) - Übersicht und Troubleshooting
|
- [Pipeline Testing Guide](../guides/pipeline-testing-guide.md) - Übersicht und Troubleshooting
|
||||||
|
- [Gitea, Traefik & CI/CD Setup](../troubleshooting/gitea-traefik-cicd-setup.md) ⭐⭐ - **WICHTIG:** Komplette Anleitung für alle Setup-Schritte (Traefik, Gitea, Runner)
|
||||||
- `deployment/scripts/test-pipeline-prerequisites.sh` - Prüft alle Voraussetzungen automatisch
|
- `deployment/scripts/test-pipeline-prerequisites.sh` - Prüft alle Voraussetzungen automatisch
|
||||||
633
deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md
Normal file
633
deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
# Gitea, Traefik & CI/CD Setup - Komplette Anleitung
|
||||||
|
|
||||||
|
**Stand:** 2025-11-08
|
||||||
|
**Status:** ✅ Vollständig dokumentiert - Alle Probleme gelöst
|
||||||
|
**Zweck:** Referenz für zukünftige Deployments und Troubleshooting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieses Dokument dokumentiert alle Schritte, Probleme und Lösungen, die notwendig waren, um Gitea, Traefik und die CI/CD-Pipeline vollständig zum Laufen zu bringen. Diese Anleitung sollte bei jedem neuen Deployment oder bei ähnlichen Problemen als Referenz dienen.
|
||||||
|
|
||||||
|
**📖 Verwandte Dokumentation:**
|
||||||
|
- [Initial Deployment Guide](../guides/initial-deployment-guide.md)
|
||||||
|
- [CI/CD Workflow Guide](../guides/cicd-workflow-guide.md)
|
||||||
|
- [Pipeline Testing Guide](../guides/pipeline-testing-guide.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 1: Traefik Dashboard nicht erreichbar
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Traefik Dashboard unter `https://traefik.michaelschiemer.de` nicht erreichbar
|
||||||
|
- Passwort unbekannt oder vergessen
|
||||||
|
|
||||||
|
### Lösung: Traefik-Passwort zurücksetzen
|
||||||
|
|
||||||
|
**1. Neues Passwort-Hash generieren:**
|
||||||
|
```bash
|
||||||
|
htpasswd -nb admin "dein_neues_passwort"
|
||||||
|
# Output: admin:$apr1$xyz123$abc456
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Hash in `docker-compose.yml` aktualisieren:**
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/traefik/docker-compose.yml`
|
||||||
|
|
||||||
|
**Wichtig:** In Docker Compose Labels müssen `$` Zeichen mit `$$` escaped werden!
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Zeile 49
|
||||||
|
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$xyz123$$abc456"
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Konfiguration synchronisieren und Traefik neu starten:**
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml playbooks/sync-stacks.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
|
||||||
|
ansible -i inventory/production.yml server -m shell \
|
||||||
|
-a "cd /home/deploy/deployment/stacks/traefik && docker compose restart traefik" \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erfolgreich getestet mit Passwort:** `4qcdnnqF7NV4bLZnXmJna7Sd2FCsd2zM`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 2: Gitea nicht erreichbar (504 Bad Gateway)
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Gitea unter `https://git.michaelschiemer.de` liefert 504 Bad Gateway
|
||||||
|
- Traefik Logs zeigen: "Error while dialing backend"
|
||||||
|
- Gitea Container läuft, aber Traefik kann nicht verbinden
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
Die File-Provider-Konfiguration `dynamic/gitea-service.yml` überschreibt die Docker-Service-Definition und verursacht Konflikte. Traefik findet kein Backend, obwohl Gitea läuft.
|
||||||
|
|
||||||
|
### Lösung: File-Provider-Konfiguration entfernen
|
||||||
|
|
||||||
|
**1. Datei deaktivieren (lokal):**
|
||||||
|
```bash
|
||||||
|
cd deployment/stacks/traefik/dynamic
|
||||||
|
mv gitea-service.yml gitea-service.yml.disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Konfiguration synchronisieren:**
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml playbooks/sync-stacks.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Alte Datei auf Server entfernen:**
|
||||||
|
```bash
|
||||||
|
ansible -i inventory/production.yml server -m file \
|
||||||
|
-a "path=/home/deploy/deployment/stacks/traefik/dynamic/gitea-service.yml state=absent" \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Traefik neu starten:**
|
||||||
|
```bash
|
||||||
|
ansible -i inventory/production.yml server -m shell \
|
||||||
|
-a "cd /home/deploy/deployment/stacks/traefik && docker compose restart traefik" \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum funktioniert das?**
|
||||||
|
- Docker-Labels definieren bereits den Service korrekt: `traefik.http.services.gitea.loadbalancer.server.port=3000`
|
||||||
|
- Router referenziert explizit: `traefik.http.routers.gitea.service=gitea@docker`
|
||||||
|
- File-Provider-Definition überschreibt diese Konfiguration und verursacht Konflikte
|
||||||
|
|
||||||
|
**Wichtig:** Die Datei `gitea-service.yml.disabled` sollte **nicht** wieder aktiviert werden, da die Docker-Labels ausreichend sind.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 3: Gitea im Restart-Loop (Redis-Verbindung)
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Gitea Container startet, crasht sofort, startet wieder (Endlosschleife)
|
||||||
|
- Logs zeigen: `dial tcp 127.0.0.1:6379: connect: connection refused`
|
||||||
|
- Gitea versucht, sich mit `127.0.0.1:6379` statt `redis:6379` zu verbinden
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
Gitea 1.25 interpretiert `GITEA__cache__HOST=redis:6379` nicht korrekt und verbindet sich mit `127.0.0.1:6379`. Die `app.ini` Datei im Volume enthält möglicherweise noch alte `127.0.0.1` Werte.
|
||||||
|
|
||||||
|
### Lösung: Gitea app.ini bereinigen
|
||||||
|
|
||||||
|
**1. Gitea Container stoppen:**
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible -i inventory/production.yml server -m shell \
|
||||||
|
-a "cd /home/deploy/deployment/stacks/gitea && docker compose stop gitea" \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. app.ini im Volume aktualisieren (minimale Konfiguration):**
|
||||||
|
|
||||||
|
**Wichtig:** Die `[cache]` Sektion muss komplett entfernt werden, da sie über ENV-Variablen gesteuert wird!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible -i inventory/production.yml server -m shell -a 'docker run --rm -v gitea-data:/data alpine sh -c "cat > /data/gitea/conf/app.ini << EOF
|
||||||
|
APP_NAME = Gitea: Git with a cup of tea
|
||||||
|
RUN_MODE = prod
|
||||||
|
|
||||||
|
[server]
|
||||||
|
PROTOCOL = http
|
||||||
|
DOMAIN = git.michaelschiemer.de
|
||||||
|
HTTP_ADDR = 0.0.0.0
|
||||||
|
HTTP_PORT = 3000
|
||||||
|
ROOT_URL = https://git.michaelschiemer.de/
|
||||||
|
DISABLE_SSH = false
|
||||||
|
START_SSH_SERVER = false
|
||||||
|
SSH_DOMAIN = git.michaelschiemer.de
|
||||||
|
SSH_PORT = 2222
|
||||||
|
SSH_LISTEN_PORT = 2222
|
||||||
|
|
||||||
|
[database]
|
||||||
|
DB_TYPE = postgres
|
||||||
|
HOST = postgres:5432
|
||||||
|
NAME = gitea
|
||||||
|
USER = gitea
|
||||||
|
PASSWD = gitea_password
|
||||||
|
SSL_MODE = disable
|
||||||
|
|
||||||
|
[security]
|
||||||
|
INSTALL_LOCK = true
|
||||||
|
|
||||||
|
[service]
|
||||||
|
DISABLE_REGISTRATION = true
|
||||||
|
|
||||||
|
[actions]
|
||||||
|
ENABLED = true
|
||||||
|
EOF
|
||||||
|
chown 1000:1000 /data/gitea/conf/app.ini && chmod 644 /data/gitea/conf/app.ini"' \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Gitea wieder starten:**
|
||||||
|
```bash
|
||||||
|
ansible -i inventory/production.yml server -m shell \
|
||||||
|
-a "cd /home/deploy/deployment/stacks/gitea && docker compose up -d gitea" \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Cache-Konfiguration (aktuell deaktiviert):**
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/gitea/docker-compose.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Cache deaktiviert - Gitea 1.25 interpretiert GITEA__cache__HOST nicht korrekt
|
||||||
|
- GITEA__cache__ENABLED=false
|
||||||
|
- GITEA__cache__ADAPTER=memory
|
||||||
|
# Session und Queue nutzen weiterhin Redis
|
||||||
|
- GITEA__session__PROVIDER=redis
|
||||||
|
- GITEA__session__PROVIDER_CONFIG=network=tcp,addr=redis:6379,password=${REDIS_PASSWORD:-gitea_redis_password},db=0,pool_size=100,idle_timeout=180
|
||||||
|
- GITEA__queue__TYPE=redis
|
||||||
|
- GITEA__queue__CONN_STR=redis://:${REDIS_PASSWORD:-gitea_redis_password}@redis:6379/0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Cache kann später wieder aktiviert werden, wenn Gitea aktualisiert wird oder ein Workaround gefunden wird.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 4: Gitea Repository fehlt
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Repository `michael/michaelschiemer` existiert nicht in Gitea
|
||||||
|
- Git Push schlägt fehl: "repository not found"
|
||||||
|
|
||||||
|
### Lösung: Repository erstellen
|
||||||
|
|
||||||
|
**1. Repository über Gitea API erstellen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
|
||||||
|
# Admin-Credentials aus Vault holen
|
||||||
|
ADMIN_USER=$(ansible-vault view secrets/production.vault.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_gitea_admin_username:' | \
|
||||||
|
sed 's/.*vault_gitea_admin_username: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
ADMIN_PASS=$(ansible-vault view secrets/production.vault.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_gitea_admin_password:' | \
|
||||||
|
sed 's/.*vault_gitea_admin_password: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
# Repository erstellen
|
||||||
|
curl -k -s -X POST \
|
||||||
|
-u "${ADMIN_USER}:${ADMIN_PASS}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "User-Agent: Mozilla/5.0" \
|
||||||
|
-d '{
|
||||||
|
"name":"michaelschiemer",
|
||||||
|
"description":"Main application repository",
|
||||||
|
"private":false,
|
||||||
|
"auto_init":false
|
||||||
|
}' \
|
||||||
|
https://git.michaelschiemer.de/api/v1/user/repos
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Git Remote konfigurieren:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/michael/dev/michaelschiemer
|
||||||
|
git remote set-url origin https://git.michaelschiemer.de/michael/michaelschiemer.git
|
||||||
|
git remote -v # Verifizieren
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Push testen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mit Admin-Credentials (temporär)
|
||||||
|
git remote set-url origin https://${ADMIN_USER}:${ADMIN_PASS}@git.michaelschiemer.de/michael/michaelschiemer.git
|
||||||
|
git push origin staging
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternativ:** Ansible Playbook verwenden:
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml \
|
||||||
|
playbooks/setup-gitea-repository.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass \
|
||||||
|
-e "repo_name=michaelschiemer" \
|
||||||
|
-e "repo_owner=michael" \
|
||||||
|
-e "repo_private=false"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 5: Gitea Runner nicht registriert oder läuft nicht
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Workflows werden nicht ausgeführt
|
||||||
|
- Runner erscheint nicht in Gitea UI
|
||||||
|
- Runner Container läuft, aber zeigt "unregistered runner" Fehler
|
||||||
|
|
||||||
|
### Lösung: Runner registrieren und starten
|
||||||
|
|
||||||
|
**1. Registration Token von Gitea holen:**
|
||||||
|
|
||||||
|
1. Gehe zu: `https://git.michaelschiemer.de/admin/actions/runners`
|
||||||
|
2. Klicke auf "Create New Runner" oder "New Runner"
|
||||||
|
3. Kopiere das Registration Token
|
||||||
|
|
||||||
|
**2. Token in `.env` Datei aktualisieren:**
|
||||||
|
|
||||||
|
**Datei:** `deployment/gitea-runner/.env`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deployment/gitea-runner
|
||||||
|
nano .env
|
||||||
|
# Setze: GITEA_RUNNER_REGISTRATION_TOKEN=<neues-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Alte Registrierung entfernen (falls vorhanden):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deployment/gitea-runner
|
||||||
|
docker compose down
|
||||||
|
rm -f data/.runner
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Runner registrieren:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deployment/gitea-runner
|
||||||
|
./register.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Runner-Status prüfen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container-Status
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
# Runner-Logs
|
||||||
|
docker compose logs -f gitea-runner
|
||||||
|
|
||||||
|
# In Gitea UI prüfen
|
||||||
|
# https://git.michaelschiemer.de/admin/actions/runners
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erfolgreiche Registrierung zeigt:**
|
||||||
|
```
|
||||||
|
✓ Runner registered successfully!
|
||||||
|
Runner is now active and will start accepting jobs.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Runner-Logs sollten zeigen:**
|
||||||
|
```
|
||||||
|
level=info msg="Runner registered successfully."
|
||||||
|
level=info msg="declare successfully"
|
||||||
|
level=info msg="task 1 repo is michael/michaelschiemer"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 6: Git Push schlägt fehl (Authentication)
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- `fatal: Authentication failed for 'https://git.michaelschiemer.de/...'`
|
||||||
|
- Push funktioniert nicht, obwohl Repository existiert
|
||||||
|
|
||||||
|
### Lösung: Git Remote mit Credentials konfigurieren
|
||||||
|
|
||||||
|
**Option 1: Temporär mit Admin-Credentials (für ersten Push):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/michael/dev/michaelschiemer
|
||||||
|
|
||||||
|
# Credentials aus Vault holen
|
||||||
|
ADMIN_USER=$(ansible-vault view deployment/ansible/secrets/production.vault.yml \
|
||||||
|
--vault-password-file deployment/ansible/secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_gitea_admin_username:' | \
|
||||||
|
sed 's/.*vault_gitea_admin_username: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
ADMIN_PASS=$(ansible-vault view deployment/ansible/secrets/production.vault.yml \
|
||||||
|
--vault-password-file deployment/ansible/secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_gitea_admin_password:' | \
|
||||||
|
sed 's/.*vault_gitea_admin_password: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
# Remote mit Credentials setzen
|
||||||
|
git remote set-url origin https://${ADMIN_USER}:${ADMIN_PASS}@git.michaelschiemer.de/michael/michaelschiemer.git
|
||||||
|
|
||||||
|
# Push
|
||||||
|
git push origin staging
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2: Personal Access Token verwenden (empfohlen für dauerhafte Nutzung):**
|
||||||
|
|
||||||
|
1. In Gitea: Settings > Applications > Generate New Token
|
||||||
|
2. Scopes: `write:repository`, `read:repository`
|
||||||
|
3. Token kopieren
|
||||||
|
4. Git Remote konfigurieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote set-url origin https://michael:<token>@git.michaelschiemer.de/michael/michaelschiemer.git
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 3: Git Credential Helper konfigurieren:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global credential.helper store
|
||||||
|
git push origin staging
|
||||||
|
# Bei Aufforderung: Username und Passwort eingeben
|
||||||
|
# Credentials werden in ~/.git-credentials gespeichert
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checkliste für zukünftige Deployments
|
||||||
|
|
||||||
|
### Vor dem ersten Deployment
|
||||||
|
|
||||||
|
- [ ] **Traefik-Passwort setzen/resetzen**
|
||||||
|
- Hash generieren: `htpasswd -nb admin "passwort"`
|
||||||
|
- In `deployment/stacks/traefik/docker-compose.yml` aktualisieren (mit `$$` escapen)
|
||||||
|
- Synchronisieren und Traefik neu starten
|
||||||
|
|
||||||
|
- [ ] **Traefik File-Provider-Konfiguration prüfen**
|
||||||
|
- `deployment/stacks/traefik/dynamic/gitea-service.yml` sollte **nicht** existieren
|
||||||
|
- Falls vorhanden: deaktivieren oder löschen
|
||||||
|
- Docker-Labels sind ausreichend
|
||||||
|
|
||||||
|
- [ ] **Gitea app.ini bereinigen**
|
||||||
|
- Keine `[cache]` Sektion mit `127.0.0.1` Werten
|
||||||
|
- Minimale Konfiguration verwenden
|
||||||
|
- Cache über ENV-Variablen steuern
|
||||||
|
|
||||||
|
- [ ] **Gitea Repository erstellen**
|
||||||
|
- Über API oder Ansible Playbook
|
||||||
|
- Git Remote konfigurieren
|
||||||
|
|
||||||
|
- [ ] **Gitea Runner registrieren**
|
||||||
|
- Registration Token von Gitea Admin UI holen
|
||||||
|
- In `deployment/gitea-runner/.env` eintragen
|
||||||
|
- `./register.sh` ausführen
|
||||||
|
- Status in Gitea UI prüfen
|
||||||
|
|
||||||
|
### Nach dem Deployment
|
||||||
|
|
||||||
|
- [ ] **Gitea erreichbar?**
|
||||||
|
- `https://git.michaelschiemer.de` sollte funktionieren
|
||||||
|
- Keine 504 Fehler
|
||||||
|
|
||||||
|
- [ ] **Traefik Dashboard erreichbar?**
|
||||||
|
- `https://traefik.michaelschiemer.de` mit korrektem Passwort
|
||||||
|
|
||||||
|
- [ ] **Runner läuft?**
|
||||||
|
- `docker compose ps` in `deployment/gitea-runner/`
|
||||||
|
- Runner erscheint als "Idle" in Gitea UI
|
||||||
|
|
||||||
|
- [ ] **CI-Images verfügbar?**
|
||||||
|
- `docker exec gitea-runner-dind docker images | grep docker-build`
|
||||||
|
- Falls nicht: Image bauen und in docker-dind laden (siehe Problem 7)
|
||||||
|
|
||||||
|
- [ ] **Workflows werden ausgeführt?**
|
||||||
|
- Test-Commit pushen
|
||||||
|
- In Gitea Actions UI prüfen: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
|
||||||
|
- Keine "pull access denied" Fehler
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wichtige Dateien und Konfigurationen
|
||||||
|
|
||||||
|
### Traefik
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/traefik/docker-compose.yml`
|
||||||
|
- Zeile 49: BasicAuth Passwort-Hash (mit `$$` escapen)
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/traefik/dynamic/gitea-service.yml`
|
||||||
|
- **Sollte nicht existieren** - Docker-Labels sind ausreichend
|
||||||
|
|
||||||
|
### Gitea
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/gitea/docker-compose.yml`
|
||||||
|
- Cache deaktiviert (Gitea 1.25 Bug)
|
||||||
|
- Session und Queue nutzen Redis
|
||||||
|
|
||||||
|
**Datei:** `deployment/stacks/gitea/app.ini` (im Volume)
|
||||||
|
- Minimale Konfiguration
|
||||||
|
- Keine `[cache]` Sektion
|
||||||
|
|
||||||
|
**Datei:** `deployment/ansible/templates/gitea-app.ini.j2`
|
||||||
|
- Template für minimale app.ini
|
||||||
|
- Wird von Ansible verwendet
|
||||||
|
|
||||||
|
### Gitea Runner
|
||||||
|
|
||||||
|
**Datei:** `deployment/gitea-runner/.env`
|
||||||
|
- `GITEA_INSTANCE_URL=https://git.michaelschiemer.de`
|
||||||
|
- `GITEA_RUNNER_REGISTRATION_TOKEN=<token>`
|
||||||
|
- `GITEA_RUNNER_NAME=dev-runner-01`
|
||||||
|
|
||||||
|
**Datei:** `deployment/gitea-runner/data/.runner`
|
||||||
|
- Wird automatisch bei Registrierung erstellt
|
||||||
|
- Enthält Runner-Credentials
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Häufige Fehler und Lösungen
|
||||||
|
|
||||||
|
### Fehler: "runner registration token not found"
|
||||||
|
|
||||||
|
**Ursache:** Token ist ungültig oder abgelaufen
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Neues Token in Gitea Admin UI erstellen
|
||||||
|
2. Token in `.env` aktualisieren
|
||||||
|
3. Runner neu registrieren: `./register.sh`
|
||||||
|
|
||||||
|
### Fehler: "unregistered runner"
|
||||||
|
|
||||||
|
**Ursache:** Runner wurde nicht korrekt registriert oder Token ist ungültig
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Alte Registrierung entfernen: `rm -f data/.runner`
|
||||||
|
2. Container stoppen: `docker compose down`
|
||||||
|
3. Neues Token holen und in `.env` eintragen
|
||||||
|
4. Neu registrieren: `./register.sh`
|
||||||
|
|
||||||
|
### Fehler: "dial tcp 127.0.0.1:6379: connect: connection refused"
|
||||||
|
|
||||||
|
**Ursache:** Gitea versucht, sich mit `127.0.0.1:6379` statt `redis:6379` zu verbinden
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Gitea stoppen
|
||||||
|
2. `app.ini` im Volume bereinigen (keine `[cache]` Sektion mit `127.0.0.1`)
|
||||||
|
3. Cache in `docker-compose.yml` deaktivieren
|
||||||
|
4. Gitea neu starten
|
||||||
|
|
||||||
|
### Fehler: "504 Bad Gateway" bei Gitea
|
||||||
|
|
||||||
|
**Ursache:** Traefik File-Provider überschreibt Docker-Service-Definition
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. `deployment/stacks/traefik/dynamic/gitea-service.yml` entfernen
|
||||||
|
2. Konfiguration synchronisieren
|
||||||
|
3. Traefik neu starten
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ansible Playbooks Referenz
|
||||||
|
|
||||||
|
### Stacks synchronisieren
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml playbooks/sync-stacks.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gitea Repository erstellen
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml \
|
||||||
|
playbooks/setup-gitea-repository.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass \
|
||||||
|
-e "repo_name=michaelschiemer" \
|
||||||
|
-e "repo_owner=michael" \
|
||||||
|
-e "repo_private=false"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gitea app.ini aktualisieren
|
||||||
|
```bash
|
||||||
|
cd deployment/ansible
|
||||||
|
ansible-playbook -i inventory/production.yml \
|
||||||
|
playbooks/setup-gitea-initial-config.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass \
|
||||||
|
-e "force_update_app_ini=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zusammenfassung der kritischen Schritte
|
||||||
|
|
||||||
|
1. ✅ **Traefik-Passwort setzen** - Hash in `docker-compose.yml` (mit `$$` escapen)
|
||||||
|
2. ✅ **File-Provider entfernen** - `gitea-service.yml` löschen/deaktivieren
|
||||||
|
3. ✅ **Gitea app.ini bereinigen** - Keine `[cache]` Sektion mit `127.0.0.1`
|
||||||
|
4. ✅ **Repository erstellen** - Über API oder Ansible Playbook
|
||||||
|
5. ✅ **Runner registrieren** - Token in `.env`, dann `./register.sh`
|
||||||
|
6. ✅ **Git Remote konfigurieren** - Mit Credentials für ersten Push
|
||||||
|
7. ✅ **CI-Images bauen und laden** - `docker-build` Image in docker-dind laden
|
||||||
|
|
||||||
|
**Nach diesen Schritten sollte die CI/CD-Pipeline vollständig funktionieren!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem 7: Workflow schlägt fehl - "pull access denied" für docker-build Image
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
- Workflow startet, aber schlägt fehl mit: `pull access denied, repository does not exist or may require authorization`
|
||||||
|
- Runner versucht, `registry.michaelschiemer.de/ci/docker-build:latest` zu pullen
|
||||||
|
- Fehler tritt auf, bevor der Workflow die Registry-Authentifizierung durchführt
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
Der Runner versucht, Container-Images für Jobs zu pullen, bevor der Workflow die Registry-Authentifizierung durchführt. Das Image existiert möglicherweise nicht in der Registry oder der Runner hat keine Registry-Credentials.
|
||||||
|
|
||||||
|
### Lösung: CI-Image bauen und in docker-dind laden
|
||||||
|
|
||||||
|
**1. CI-Image bauen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/michael/dev/michaelschiemer
|
||||||
|
docker build -f docker/ci/Dockerfile.build \
|
||||||
|
-t registry.michaelschiemer.de/ci/docker-build:latest \
|
||||||
|
--platform linux/amd64 .
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Image in docker-dind laden (temporäre Lösung):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker save registry.michaelschiemer.de/ci/docker-build:latest | \
|
||||||
|
docker exec -i gitea-runner-dind docker load
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Verifizieren:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec gitea-runner-dind docker images | grep docker-build
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Runner neu starten:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deployment/gitea-runner
|
||||||
|
docker compose restart gitea-runner
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative: Image in Registry pushen (empfohlen für Production):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bei Registry einloggen
|
||||||
|
cd deployment/ansible
|
||||||
|
REGISTRY_USER=$(ansible-vault view secrets/production.vault.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_docker_registry_username:' | \
|
||||||
|
sed 's/.*vault_docker_registry_username: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
REGISTRY_PASS=$(ansible-vault view secrets/production.vault.yml \
|
||||||
|
--vault-password-file secrets/.vault_pass 2>/dev/null | \
|
||||||
|
grep 'vault_docker_registry_password:' | \
|
||||||
|
sed 's/.*vault_docker_registry_password: *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
cd /home/michael/dev/michaelschiemer
|
||||||
|
echo "$REGISTRY_PASS" | docker login registry.michaelschiemer.de \
|
||||||
|
-u "$REGISTRY_USER" --password-stdin
|
||||||
|
|
||||||
|
# Image pushen
|
||||||
|
docker push registry.michaelschiemer.de/ci/docker-build:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Wenn der Push fehlschlägt (z.B. 499 Error), kann das Image lokal in docker-dind geladen werden. Für Production sollte das Image jedoch in der Registry sein.
|
||||||
|
|
||||||
|
**Für zukünftige Deployments:** Das Image sollte beim ersten Setup gebaut und in die Registry gepusht werden, oder das Build-Script `deployment/gitea-runner/build-ci-image.sh` verwenden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Letzte Aktualisierung:** 2025-11-08
|
||||||
|
**Status:** ✅ Vollständig dokumentiert und getestet
|
||||||
|
|
||||||
457
deployment/docs/troubleshooting/initial-deployment-issues.md
Normal file
457
deployment/docs/troubleshooting/initial-deployment-issues.md
Normal 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
|
||||||
|
|
||||||
Reference in New Issue
Block a user