From 1b9cda6dd3bc9c0867dbaaedb0e759cc5e1ad74e Mon Sep 17 00:00:00 2001 From: Michael Schiemer Date: Sat, 8 Nov 2025 13:38:46 +0100 Subject: [PATCH] docs: Add CI image setup documentation --- deployment/docs/README.md | 45 +- deployment/docs/status/deployment-todo.md | 1 + .../gitea-traefik-cicd-setup.md | 633 ++++++++++++++++++ .../initial-deployment-issues.md | 457 +++++++++++++ 4 files changed, 1127 insertions(+), 9 deletions(-) create mode 100644 deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md create mode 100644 deployment/docs/troubleshooting/initial-deployment-issues.md diff --git a/deployment/docs/README.md b/deployment/docs/README.md index 2bdb416c..ebbd7c9b 100644 --- a/deployment/docs/README.md +++ b/deployment/docs/README.md @@ -1,7 +1,7 @@ # Deployment Dokumentation - Index -**Stand:** 2025-11-01 -**Status:** ? Vollst?ndige Dokumentation vorhanden +**Stand:** 2025-11-07 +**Status:** ✅ Vollständige Dokumentation vorhanden (inkl. CI/CD, Backup & Rollback) --- @@ -33,13 +33,36 @@ - Secrets Konfiguration - Schritt-f?r-Schritt Anleitung -4. **[deployment-commands.md](guides/deployment-commands.md)** - - Command-Referenz f?r Deployment +4. **[server-directory-structure.md](server-directory-structure.md)** + - 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 - Docker Compose 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 - Setup und Verwaltung - CI/CD Integration @@ -47,7 +70,7 @@ ### 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 - Was passiert bei jedem Deployment - Container-Neustart Details @@ -106,7 +129,7 @@ ### 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 ### 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 +### 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-results.md](tests/test-results.md)** - Test Ergebnisse @@ -200,5 +227,5 @@ --- -**Letzte Aktualisierung:** 2025-11-01 -**Status:** ? Dokumentation vollst?ndig +**Letzte Aktualisierung:** 2025-11-07 +**Status:** ✅ Dokumentation vollständig (inkl. CI/CD, Backup & Rollback) diff --git a/deployment/docs/status/deployment-todo.md b/deployment/docs/status/deployment-todo.md index eb677665..fb3d6f53 100644 --- a/deployment/docs/status/deployment-todo.md +++ b/deployment/docs/status/deployment-todo.md @@ -353,4 +353,5 @@ ansible-playbook -i inventory/production.yml playbooks/.yml **Test-Ressourcen:** - [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 +- [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 \ No newline at end of file diff --git a/deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md b/deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md new file mode 100644 index 00000000..f49d3b48 --- /dev/null +++ b/deployment/docs/troubleshooting/gitea-traefik-cicd-setup.md @@ -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= +``` + +**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:@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=` +- `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 + diff --git a/deployment/docs/troubleshooting/initial-deployment-issues.md b/deployment/docs/troubleshooting/initial-deployment-issues.md new file mode 100644 index 00000000..0b0eba4a --- /dev/null +++ b/deployment/docs/troubleshooting/initial-deployment-issues.md @@ -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 +