chore: remove test trigger file

This commit is contained in:
2025-10-31 04:14:18 +01:00
parent 403205315e
commit 6deca7838e
29 changed files with 4052 additions and 128 deletions

View File

@@ -0,0 +1,561 @@
# Application Stack Deployment Prozess
## Übersicht
Dieses Dokument erklärt, wie genau das Deployment in den Application Stack abläuft, wenn die CI/CD Pipeline ausgeführt wird.
**📖 Verwandte Dokumentation:**
- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Wie Codeänderungen gepusht werden
- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline
---
## Deployment-Flow
```
CI/CD Pipeline (Gitea Actions)
1. Tests & Build
2. Docker Image Build & Push zur Registry
3. Ansible Playbook ausführen
4. Application Stack aktualisieren
```
---
## Detaillierter Ablauf
### Phase 1: CI/CD Pipeline (`.gitea/workflows/production-deploy.yml`)
#### Job 1: Tests
```yaml
- PHP 8.3 Setup
- Composer Dependencies installieren
- Pest Tests ausführen
- PHPStan Code Quality Check
- Code Style Check
```
#### Job 2: Build & Push
```yaml
- Docker Image Build (Dockerfile.production)
- Image mit Tags pushen:
- git.michaelschiemer.de:5000/framework:latest
- git.michaelschiemer.de:5000/framework:<tag>
- git.michaelschiemer.de:5000/framework:git-<short-sha>
```
#### Job 3: Deploy (Ansible)
**Schritt 1: Code Checkout**
```bash
git clone <repository> /workspace/repo
```
**Schritt 2: SSH Setup**
```bash
# SSH Private Key aus Secret wird in ~/.ssh/production geschrieben
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/production
chmod 600 ~/.ssh/production
```
**Schritt 3: Ansible Installation**
```bash
apt-get install -y ansible
```
**Schritt 4: Ansible Playbook ausführen**
```bash
cd /workspace/repo/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/deploy-update.yml \
-e "image_tag=<generated-tag>" \
-e "git_commit_sha=<commit-sha>" \
-e "deployment_timestamp=<timestamp>" \
-e "docker_registry_username=${{ secrets.REGISTRY_USER }}" \
-e "docker_registry_password=${{ secrets.REGISTRY_PASSWORD }}"
```
---
### Phase 2: Ansible Deployment (`deploy-update.yml`)
Das Ansible Playbook führt folgende Schritte auf dem Production-Server aus:
#### Pre-Tasks
**1. Secrets laden (optional)**
```yaml
- Lädt Registry Credentials aus Ansible Vault (falls vorhanden)
- Oder verwendet Credentials aus Workflow-Variablen
```
**2. Docker Service prüfen**
```yaml
- Verifiziert, dass Docker läuft
- Startet Docker falls notwendig
```
**3. Verzeichnisse erstellen**
```yaml
- Application Stack Path: ~/deployment/stacks/application
- Backup Verzeichnis: ~/deployment/backups/<timestamp>/
```
#### Tasks
**1. Backup aktueller Deployment-Status**
```bash
# Speichert aktuellen Container-Status
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
ps --format json > backups/<timestamp>/current_containers.json
# Speichert aktuelle docker-compose.yml Konfiguration
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
config > backups/<timestamp>/docker-compose-config.yml
```
**2. Docker Registry Login**
```bash
# Login zur privaten Registry mit Credentials
docker login git.michaelschiemer.de:5000 \
-u <registry-username> \
-p <registry-password>
```
**3. Neues Image Pullen**
```bash
# Pullt das neue Image von der Registry
docker pull git.michaelschiemer.de:5000/framework:<tag>
# Beispiel:
# git.michaelschiemer.de:5000/framework:abc1234-1696234567
```
**4. docker-compose.yml aktualisieren**
**Wichtig:** Das Playbook aktualisiert die `docker-compose.yml` Datei direkt auf dem Server!
```yaml
# Vorher:
services:
app:
image: git.michaelschiemer.de:5000/framework:latest
# Nachher (wenn image_tag != 'latest'):
services:
app:
image: git.michaelschiemer.de:5000/framework:<tag>
```
**Regex-Replace:**
```yaml
regexp: '^(\s+image:\s+){{ app_image }}:.*$'
replace: '\1{{ app_image }}:{{ image_tag }}'
```
**Betroffene Services (werden alle aktualisiert):**
- `app` (PHP-FPM) - Zeile 6: `image: git.michaelschiemer.de:5000/framework:latest`
- `queue-worker` (Queue Worker) - Zeile 120: `image: git.michaelschiemer.de:5000/framework:latest`
- `scheduler` (Scheduler) - Zeile 165: `image: git.michaelschiemer.de:5000/framework:latest`
**Hinweis:**
- Alle drei Services verwenden das gleiche Image, daher werden alle mit dem neuen Tag aktualisiert
- Der Regex matched **alle Zeilen** die mit `image: git.michaelschiemer.de:5000/framework:` beginnen
- `nginx` und `redis` bleiben unverändert (verwenden andere Images)
**5. Application Stack neu starten**
```bash
docker compose -f ~/deployment/stacks/application/docker-compose.yml \
up -d \
--pull always \
--force-recreate \
--remove-orphans
```
**Was passiert dabei:**
- `--pull always`: Zieht Images immer neu (auch wenn schon vorhanden)
- `--force-recreate`: Erstellt Container immer neu, auch wenn Konfiguration unverändert ist
- `--remove-orphans`: Entfernt Container, die nicht mehr in der Compose-Datei definiert sind
- `-d`: Läuft im Hintergrund (detached)
**Container-Neustart-Reihenfolge:**
1. Abhängigkeiten werden gestoppt (app hängt von redis ab)
2. Container werden neu erstellt mit neuem Image
3. Container werden in korrekter Reihenfolge gestartet (redis → app → nginx)
4. Health-Checks werden ausgeführt
**6. Warten auf Health-Checks**
```yaml
# Wartet 60 Sekunden auf Container-Gesundheit
wait_for:
timeout: 60
```
**7. Health-Status prüfen**
```bash
# Prüft alle Container-Health-Status
docker compose ps --format json | \
jq -r '.[] | select(.Health != "healthy" and .Health != "") | "\(.Name): \(.Health)"'
```
**8. Deployment-Metadaten speichern**
```
~/deployment/backups/<timestamp>/deployment_metadata.txt
```
**Inhalt:**
```
Deployment Timestamp: 2025-10-31T02:35:04Z
Git Commit: abc1234...
Image Tag: abc1234-1696234567
Deployed Image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
Image Pull: SUCCESS
Stack Deploy: UPDATED
Health Status: All services healthy
```
**9. Alte Backups aufräumen**
```bash
# Behält nur die letzten X Backups (Standard: 5)
ls -dt */ | tail -n +6 | xargs -r rm -rf
```
---
### Phase 3: Application Stack Services
Der Application Stack besteht aus mehreren Services:
#### Services im Stack
**1. `app` (PHP-FPM Application)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>`
- Container: `app`
- Health Check: `php-fpm-healthcheck`
- Netzwerk: `app-internal`
- Abhängigkeiten: `redis` (condition: service_healthy)
**2. `nginx` (Web Server)**
- Image: `nginx:1.25-alpine`
- Container: `nginx`
- Health Check: `wget --spider http://localhost/health`
- Netzwerke: `traefik-public`, `app-internal`
- Abhängigkeiten: `app` (condition: service_healthy)
- Traefik Labels für Routing
**3. `redis` (Cache/Session/Queue)**
- Image: `redis:7-alpine`
- Container: `redis`
- Health Check: `redis-cli --raw incr ping`
- Netzwerk: `app-internal`
**4. `queue-worker` (Background Jobs)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>` (gleiches wie app)
- Container: `queue-worker`
- Health Check: `pgrep -f 'queue:work'`
- Netzwerk: `app-internal`
- Command: `php console.php queue:work`
- Abhängigkeiten: `app`, `redis`
**5. `scheduler` (Cron Jobs)**
- Image: `git.michaelschiemer.de:5000/framework:<tag>` (gleiches wie app)
- Container: `scheduler`
- Health Check: `pgrep -f 'scheduler:run'`
- Netzwerk: `app-internal`
- Command: `php console.php scheduler:run`
- Abhängigkeiten: `app`, `redis`
---
## Container-Neustart-Details
### Was passiert bei `docker compose up -d --force-recreate`?
**1. Container-Stop:**
- Laufende Container werden gestoppt
- Volumes bleiben erhalten (persistente Daten)
- Netzwerke bleiben erhalten
**2. Container-Entfernung:**
- Alte Container werden entfernt
- Neue Container werden mit neuem Image erstellt
**3. Container-Start:**
- Container starten in Abhängigkeits-Reihenfolge
- Health-Checks werden ausgeführt
- Falls Health-Check fehlschlägt, wird Container neu gestartet (restart: unless-stopped)
**4. Zero-Downtime?**
- **Teilweise:** Container werden nacheinander neu gestartet
- `nginx` wartet auf `app` Health-Check
- Während Neustart kann es zu kurzen Verbindungsfehlern kommen
- Kein echter Blue-Green-Deployment (das wäre möglich, aber nicht implementiert)
---
## Konfigurationsvariablen
### Inventory-Variablen (`inventory/production.yml`)
```yaml
app_image: "git.michaelschiemer.de:5000/framework"
docker_registry_url: "git.michaelschiemer.de:5000"
backups_path: "~/deployment/backups"
max_rollback_versions: 5
deploy_user_home: "~/deployment"
```
### Workflow-Variablen (aus CI/CD)
```yaml
image_tag: "abc1234-1696234567" # Generiert aus: <short-sha>-<timestamp>
git_commit_sha: "abc1234567890..."
deployment_timestamp: "2025-10-31T02:35:04Z"
docker_registry_username: "<from-secret>"
docker_registry_password: "<from-secret>"
```
---
## Beispiel-Deployment
### Schritt-für-Schritt Beispiel
**1. CI/CD Pipeline startet:**
```bash
# Commit: abc1234...
# Image Tag generiert: abc1234-1696234567
```
**2. Image wird gebaut und gepusht:**
```bash
docker buildx build \
--tag git.michaelschiemer.de:5000/framework:latest \
--tag git.michaelschiemer.de:5000/framework:abc1234-1696234567 \
--tag git.michaelschiemer.de:5000/framework:git-abc1234 \
--push \
.
```
**3. Ansible Playbook wird ausgeführt:**
```bash
ansible-playbook -i inventory/production.yml \
playbooks/deploy-update.yml \
-e "image_tag=abc1234-1696234567" \
-e "git_commit_sha=abc1234567890..." \
-e "deployment_timestamp=2025-10-31T02:35:04Z"
```
**4. Auf Production-Server:**
```bash
# 1. Backup erstellen
mkdir -p ~/deployment/backups/2025-10-31T02-35-04Z
# 2. Registry Login
docker login git.michaelschiemer.de:5000 -u admin -p <password>
# 3. Image Pullen
docker pull git.michaelschiemer.de:5000/framework:abc1234-1696234567
# 4. docker-compose.yml aktualisieren
# Vorher: image: git.michaelschiemer.de:5000/framework:latest
# Nachher: image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
# 5. Stack neu starten
cd ~/deployment/stacks/application
docker compose up -d --pull always --force-recreate --remove-orphans
# 6. Health-Checks warten
sleep 60
# 7. Status prüfen
docker compose ps
# app: healthy
# nginx: healthy
# redis: healthy
# queue-worker: healthy
# scheduler: healthy
```
**5. Deployment-Metadaten speichern:**
```bash
cat > ~/deployment/backups/2025-10-31T02-35-04Z/deployment_metadata.txt <<EOF
Deployment Timestamp: 2025-10-31T02:35:04Z
Git Commit: abc1234567890...
Image Tag: abc1234-1696234567
Deployed Image: git.michaelschiemer.de:5000/framework:abc1234-1696234567
Image Pull: SUCCESS
Stack Deploy: UPDATED
Health Status: All services healthy
EOF
```
---
## Wichtige Hinweise
### 1. Image-Tag-Update
**Wichtig:** Das Playbook aktualisiert die `docker-compose.yml` Datei **direkt auf dem Server**!
- Die Datei wird mit `replace` Modul geändert
- Alle Services mit dem Image `git.michaelschiemer.de:5000/framework:*` werden aktualisiert
- Das bedeutet: `app`, `queue-worker`, und `scheduler` bekommen alle das neue Image
### 2. Force-Recreate
**Achtung:** `--force-recreate` startet Container **immer** neu, auch wenn Konfiguration unverändert ist.
- Container-IDs ändern sich
- Kurze Downtime möglich
- Neue Container bekommen neue IPs im Docker-Netzwerk
### 3. Health-Checks
**Health-Checks sind kritisch:**
- Container müssen "healthy" werden, sonst starten abhängige Container nicht
- `nginx` wartet auf `app` Health-Check
- `app` wartet auf `redis` Health-Check
**Health-Check Timeouts:**
- `app`: 40s start_period, dann 30s interval
- `redis`: 10s start_period, dann 30s interval
- `nginx`: 10s start_period, dann 30s interval
### 4. Backups
**Backup-Strategie:**
- Jedes Deployment erstellt ein Backup-Verzeichnis
- Enthält: Container-Status, docker-compose.yml, Deployment-Metadaten
- Alte Backups werden automatisch gelöscht (Standard: Behält 5 Backups)
**Backup-Pfad:**
```
~/deployment/backups/
├── 2025-10-31T02-35-04Z/
│ ├── current_containers.json
│ ├── docker-compose-config.yml
│ └── deployment_metadata.txt
├── 2025-10-31T01-20-15Z/
│ └── ...
└── ...
```
---
## Rollback-Prozess
Falls das Deployment fehlschlägt:
### Automatischer Rollback (via Workflow)
```yaml
# Im Workflow: Rollback on failure
if: failure() && steps.health.outcome == 'failure'
run: |
ansible-playbook -i inventory/production.yml \
playbooks/rollback.yml
```
### Manueller Rollback
```bash
cd ~/deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/rollback.yml \
-e "rollback_timestamp=2025-10-31T01-20-15Z"
```
**Was passiert beim Rollback:**
1. Liest vorheriges Backup
2. Pullt altes Image
3. Aktualisiert `docker-compose.yml` mit altem Image-Tag
4. Startet Stack neu mit altem Image
---
## Verbesserungsmöglichkeiten
### 1. Blue-Green Deployment
- Zwei parallele Stacks (blue/green)
- Traffic-Switching via Traefik
- Zero-Downtime möglich
### 2. Rolling Updates
- Container werden nacheinander aktualisiert
- Reduzierte Downtime
### 3. Database-Migration vor Deployment
- Migrationen werden aktuell nach Deployment ausgeführt
- Besser: Migrationen vor Deployment testen
### 4. Canary Deployment
- Neue Version zuerst auf Subset der Traffic
- Graduelle Rollout
---
## Troubleshooting
### Container starten nicht
```bash
# Logs prüfen
docker compose -f ~/deployment/stacks/application/docker-compose.yml logs
# Health-Check-Status prüfen
docker compose ps
```
### Image wird nicht gepullt
```bash
# Registry-Login testen
docker login git.michaelschiemer.de:5000 -u admin -p <password>
# Image manuell pullen
docker pull git.michaelschiemer.de:5000/framework:<tag>
```
### docker-compose.yml wurde nicht aktualisiert
```bash
# Prüfe ob Regex korrekt ist
grep -E "image:\s+git.michaelschiemer.de:5000/framework" \
~/deployment/stacks/application/docker-compose.yml
# Prüfe Backup für vorherige Version
ls -la ~/deployment/backups/
```
---
## Zusammenfassung
**Deployment-Ablauf:**
1. CI/CD Pipeline baut Image und pusht es zur Registry
2. Ansible Playbook wird auf Production-Server ausgeführt
3. Neues Image wird gepullt
4. `docker-compose.yml` wird mit neuem Image-Tag aktualisiert
5. Application Stack wird mit `--force-recreate` neu gestartet
6. Health-Checks werden ausgeführt
7. Deployment-Metadaten werden gespeichert
8. Workflow führt abschließenden Health-Check aus
**Alle Services erhalten das neue Image:**
- `app` (PHP-FPM)
- `queue-worker` (Queue Worker)
- `scheduler` (Scheduler)
**Zero-Downtime:** Nein, Container werden neu gestartet (kurze Downtime möglich)
**Rollback:** Automatisch via Workflow oder manuell via Rollback-Playbook