chore: remove test trigger file
This commit is contained in:
561
deployment/APPLICATION_STACK_DEPLOYMENT.md
Normal file
561
deployment/APPLICATION_STACK_DEPLOYMENT.md
Normal 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
|
||||
272
deployment/CI_CD_STATUS.md
Normal file
272
deployment/CI_CD_STATUS.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# CI/CD Pipeline Status
|
||||
|
||||
**Stand:** ✅ CI/CD Pipeline ist vollständig konfiguriert und bereit zum Testen!
|
||||
|
||||
## 📖 Dokumentation
|
||||
|
||||
- **[Code Changes Workflow](CODE_CHANGE_WORKFLOW.md)** - Anleitung: Wie Codeänderungen gepusht und deployed werden
|
||||
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Detaillierte Erklärung des Deployment-Prozesses
|
||||
|
||||
## ✅ Was bereits vorhanden ist
|
||||
|
||||
### 1. Workflow-Dateien
|
||||
- ✅ `.gitea/workflows/production-deploy.yml` - Haupt-Deployment-Pipeline
|
||||
- ✅ `.gitea/workflows/update-production-secrets.yml` - Secrets-Deployment
|
||||
- ✅ `.gitea/workflows/security-scan.yml` - Security-Vulnerability-Scan
|
||||
|
||||
### 2. Gitea Runner Setup
|
||||
- ✅ `deployment/gitea-runner/docker-compose.yml` - Runner-Konfiguration
|
||||
- ✅ `deployment/gitea-runner/config.yaml` - Runner-Konfiguration
|
||||
- ✅ `deployment/gitea-runner/register.sh` - Registration-Script
|
||||
- ✅ `deployment/gitea-runner/.env.example` - Environment-Template
|
||||
|
||||
### 3. Deployment-Scripts
|
||||
- ✅ `deployment/ansible/playbooks/deploy-update.yml` - Ansible-Deployment
|
||||
- ✅ `deployment/ansible/playbooks/rollback.yml` - Rollback-Playbook
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Was noch fehlt
|
||||
|
||||
### 1. Gitea Repository Secrets konfigurieren
|
||||
|
||||
**Status**: ✅ Secrets sind bereits konfiguriert
|
||||
|
||||
**Konfigurierte Secrets:**
|
||||
- ✅ `REGISTRY_USER` - Benutzername für Docker Registry
|
||||
- ✅ `REGISTRY_PASSWORD` - Passwort für Docker Registry
|
||||
- ✅ `SSH_PRIVATE_KEY` - SSH Private Key für Production-Server-Zugriff
|
||||
- ⚠️ `GITEA_TOKEN` - Optional: Für automatische Issue-Erstellung bei Security-Scans
|
||||
|
||||
**Verifiziert:** Alle kritischen Secrets sind bereits in Gitea konfiguriert.
|
||||
|
||||
**Hinweis:** Falls `GITEA_TOKEN` noch nicht konfiguriert ist, kann die automatische Issue-Erstellung bei Security-Scans optional später hinzugefügt werden.
|
||||
|
||||
**REGISTRY_USER:**
|
||||
```
|
||||
admin
|
||||
```
|
||||
|
||||
**REGISTRY_PASSWORD:**
|
||||
```
|
||||
<Das Passwort für git.michaelschiemer.de:5000>
|
||||
```
|
||||
*Zu finden in: `deployment/stacks/registry/auth/htpasswd` oder manuell gesetzt*
|
||||
|
||||
**SSH_PRIVATE_KEY:**
|
||||
```bash
|
||||
# SSH Key Inhalt anzeigen
|
||||
cat ~/.ssh/production
|
||||
```
|
||||
*Kompletter Inhalt der Datei inkl. `-----BEGIN OPENSSH PRIVATE KEY-----` und `-----END OPENSSH PRIVATE KEY-----`*
|
||||
|
||||
**GITEA_TOKEN (optional):**
|
||||
```
|
||||
<Gitea Personal Access Token mit repo-Berechtigung>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Gitea Runner registrieren und starten
|
||||
|
||||
**Status**: ✅ Runner läuft bereits
|
||||
|
||||
**Verifiziert:**
|
||||
- ✅ Runner ist registriert (`data/.runner` existiert)
|
||||
- ✅ Runner Container läuft (`gitea-runner` ist "Up")
|
||||
- ✅ Docker-in-Docker Container läuft (`gitea-runner-dind` ist "Up")
|
||||
- ✅ Runner hat bereits Tasks ausgeführt (task 1-6 in Logs)
|
||||
- ✅ Runner Name: `dev-runner-01`
|
||||
- ✅ Runner Labels: `ubuntu-latest`, `ubuntu-22.04`, `debian-latest`
|
||||
|
||||
**Hinweis:**
|
||||
Die Logs zeigen einige Verbindungsfehler (connection refused, 502 Bad Gateway), was normal sein kann wenn Gitea temporär nicht erreichbar ist. Der Runner funktioniert grundsätzlich und hat bereits Tasks erfolgreich ausgeführt.
|
||||
|
||||
**Verifizierung in Gitea:**
|
||||
- Prüfe Runner-Status in Gitea: `https://git.michaelschiemer.de/admin/actions/runners`
|
||||
- Runner sollte als "Idle" oder "Active" angezeigt werden
|
||||
|
||||
---
|
||||
|
||||
### 3. Ansible Vault Password Handling
|
||||
|
||||
**Status**: ⚠️ Workflow nutzt Vault, aber kein Secret dafür
|
||||
|
||||
**Problem:**
|
||||
- Der Workflow `update-production-secrets.yml` benötigt ein Vault-Passwort
|
||||
- Aktuell wird es als Workflow-Input eingegeben (manuell)
|
||||
- Für automatisiertes Deployment sollte es als Secret vorliegen
|
||||
|
||||
**Lösung:**
|
||||
- [ ] Optional: `ANSIBLE_VAULT_PASSWORD` als Secret hinzufügen (nur wenn automatisiert)
|
||||
- [ ] Oder: Manuelles Eingeben beim Workflow-Trigger ist ausreichend
|
||||
|
||||
---
|
||||
|
||||
### 4. Pipeline End-to-End testen
|
||||
|
||||
**Status**: ⚠️ Pipeline ist definiert, aber noch nicht getestet
|
||||
|
||||
**Was fehlt:**
|
||||
- [ ] Test-Commit pushen und Pipeline-Lauf beobachten
|
||||
- [ ] Fehler beheben falls notwendig
|
||||
- [ ] Verifizieren dass Deployment funktioniert
|
||||
|
||||
**Test-Schritte:**
|
||||
1. Stelle sicher dass alle Secrets konfiguriert sind
|
||||
2. Stelle sicher dass Runner läuft
|
||||
3. Test-Commit erstellen:
|
||||
```bash
|
||||
git checkout -b test/cicd-pipeline
|
||||
# Kleine Änderung machen
|
||||
echo "# Test CI/CD" >> README.md
|
||||
git add README.md
|
||||
git commit -m "test: CI/CD pipeline"
|
||||
git push origin test/cicd-pipeline
|
||||
```
|
||||
4. Oder: Workflow manuell triggern:
|
||||
- Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
|
||||
- Wähle "Production Deployment Pipeline"
|
||||
- Klicke "Run workflow"
|
||||
5. Beobachte Logs und prüfe jeden Schritt:
|
||||
- ✅ Tests laufen erfolgreich
|
||||
- ✅ Docker Image wird gebaut
|
||||
- ✅ Image wird zur Registry gepusht
|
||||
- ✅ Ansible-Deployment läuft
|
||||
- ✅ Health-Check schlägt erfolgreich durch
|
||||
|
||||
---
|
||||
|
||||
## 📋 Checkliste für CI/CD Completion
|
||||
|
||||
### Vorbereitung
|
||||
- [x] Gitea Repository Secrets konfiguriert: ✅
|
||||
- [x] `REGISTRY_USER` ✅
|
||||
- [x] `REGISTRY_PASSWORD` ✅
|
||||
- [x] `SSH_PRIVATE_KEY` ✅
|
||||
- [ ] `GITEA_TOKEN` (optional)
|
||||
|
||||
### Gitea Runner
|
||||
- [x] Runner Registration Token von Gitea abgerufen ✅
|
||||
- [x] `deployment/gitea-runner/.env` erstellt und konfiguriert ✅
|
||||
- [x] Runner registriert ✅
|
||||
- [x] Runner läuft (`docker compose up -d`) ✅
|
||||
- [ ] Runner sichtbar in Gitea UI als "Idle" oder "Active" (bitte manuell prüfen)
|
||||
|
||||
### Pipeline-Test
|
||||
- [ ] Test-Commit gepusht oder Workflow manuell getriggert
|
||||
- [ ] Alle Jobs erfolgreich:
|
||||
- [ ] Tests
|
||||
- [ ] Build
|
||||
- [ ] Deploy
|
||||
- [ ] Deployment erfolgreich auf Production
|
||||
- [ ] Health-Check erfolgreich
|
||||
- [ ] Application läuft korrekt
|
||||
|
||||
### Dokumentation
|
||||
- [ ] Secrets-Setup dokumentiert
|
||||
- [ ] Runner-Setup dokumentiert
|
||||
- [ ] Bekannte Probleme/Workarounds dokumentiert
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Priorisierte Reihenfolge
|
||||
|
||||
### Phase 1: Secrets Setup
|
||||
**Status:** ✅ Abgeschlossen
|
||||
- Alle kritischen Secrets sind konfiguriert
|
||||
|
||||
### Phase 2: Gitea Runner
|
||||
**Status:** ✅ Abgeschlossen
|
||||
- Runner läuft und ist registriert
|
||||
|
||||
### Phase 3: Pipeline-Test (NÄCHSTER SCHRITT)
|
||||
**Status:** ⚠️ Ausstehend
|
||||
1. Test-Workflow ausführen
|
||||
2. Fehler beheben falls notwendig
|
||||
3. Production-Deployment verifizieren
|
||||
|
||||
---
|
||||
|
||||
## 📝 Quick Reference
|
||||
|
||||
### Secrets Setup
|
||||
```bash
|
||||
# Secrets in Gitea konfigurieren:
|
||||
https://git.michaelschiemer.de/michael/michaelschiemer/settings/secrets/actions
|
||||
```
|
||||
|
||||
### Runner Setup
|
||||
```bash
|
||||
cd deployment/gitea-runner
|
||||
cp .env.example .env
|
||||
# Token von https://git.michaelschiemer.de/admin/actions/runners eintragen
|
||||
./register.sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Pipeline manuell triggern (NÄCHSTER SCHRITT)
|
||||
```
|
||||
1. Gehe zu: https://git.michaelschiemer.de/michael/michaelschiemer/actions
|
||||
2. Wähle: "Production Deployment Pipeline"
|
||||
3. Klicke: "Run workflow"
|
||||
4. Wähle Branch: main
|
||||
5. Optionale Einstellungen:
|
||||
- skip_tests: false (Tests sollen laufen)
|
||||
6. Klicke: "Run workflow"
|
||||
7. Beobachte Logs und prüfe jeden Schritt
|
||||
```
|
||||
|
||||
### Runner-Status prüfen
|
||||
```
|
||||
https://git.michaelschiemer.de/admin/actions/runners
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## ✅ Aktueller Status
|
||||
|
||||
**CI/CD Pipeline ist vollständig konfiguriert!**
|
||||
|
||||
- ✅ Secrets konfiguriert
|
||||
- ✅ Runner läuft und ist registriert
|
||||
- ✅ Workflows sind vorhanden
|
||||
- ⚠️ **Nächster Schritt:** Pipeline testen!
|
||||
|
||||
**Ready to Deploy:** Die Pipeline kann jetzt getestet werden. Alle Voraussetzungen sind erfüllt.
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Runner erscheint nicht in Gitea
|
||||
- Prüfe Runner-Logs: `docker compose logs gitea-runner`
|
||||
- Prüfe Registration-Token in `.env`
|
||||
- Re-registrieren: `./unregister.sh && ./register.sh`
|
||||
|
||||
### Workflow startet nicht
|
||||
- Prüfe ob Runner läuft: `docker compose ps`
|
||||
- Prüfe Runner-Status in Gitea UI
|
||||
- Prüfe ob Workflow-Datei korrekt ist
|
||||
|
||||
### Secrets werden nicht erkannt
|
||||
- Prüfe Secret-Namen (müssen exakt übereinstimmen)
|
||||
- Prüfe ob Secrets in korrektem Repository/Organisation sind
|
||||
- Prüfe ob Secrets nicht abgelaufen sind
|
||||
|
||||
### Registry-Login fehlschlägt
|
||||
- Prüfe `REGISTRY_USER` und `REGISTRY_PASSWORD` Secrets
|
||||
- Teste Registry-Login manuell:
|
||||
```bash
|
||||
docker login git.michaelschiemer.de:5000 -u admin -p <password>
|
||||
```
|
||||
|
||||
### SSH-Verbindung fehlschlägt
|
||||
- Prüfe `SSH_PRIVATE_KEY` Secret (kompletter Inhalt)
|
||||
- Prüfe ob Public Key auf Production-Server ist
|
||||
- Teste SSH-Verbindung manuell:
|
||||
```bash
|
||||
ssh -i ~/.ssh/production deploy@94.16.110.151
|
||||
```
|
||||
551
deployment/CODE_CHANGE_WORKFLOW.md
Normal file
551
deployment/CODE_CHANGE_WORKFLOW.md
Normal file
@@ -0,0 +1,551 @@
|
||||
# Codeänderungen pushen und deployen
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Dokument erklärt, wie Codeänderungen gepusht werden und automatisch in Production deployed werden.
|
||||
|
||||
**Quick Start:**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: Add new feature"
|
||||
git push origin main
|
||||
```
|
||||
→ Automatisches Deployment startet (~8-15 Minuten)
|
||||
|
||||
**📖 Verwandte Dokumentation:**
|
||||
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - Wie das Deployment genau funktioniert
|
||||
- **[CI/CD Status](CI_CD_STATUS.md)** - Aktueller Status der Pipeline
|
||||
|
||||
---
|
||||
|
||||
## Normaler Workflow: Codeänderungen deployen
|
||||
|
||||
### Schritt 1: Code lokal ändern
|
||||
|
||||
```bash
|
||||
# Änderungen in deinem lokalen Repository machen
|
||||
# z.B. Datei bearbeiten: src/App/Controller/HomeController.php
|
||||
|
||||
# Änderungen anzeigen
|
||||
git status
|
||||
|
||||
# Änderungen anschauen
|
||||
git diff
|
||||
```
|
||||
|
||||
### Schritt 2: Änderungen committen
|
||||
|
||||
```bash
|
||||
# Änderungen zum Staging hinzufügen
|
||||
git add .
|
||||
|
||||
# Oder nur spezifische Dateien
|
||||
git add src/App/Controller/HomeController.php
|
||||
|
||||
# Commit erstellen
|
||||
git commit -m "feat: Add new feature to home controller"
|
||||
```
|
||||
|
||||
**Commit-Message Konventionen:**
|
||||
- `feat:` - Neue Feature
|
||||
- `fix:` - Bug-Fix
|
||||
- `refactor:` - Code-Refactoring
|
||||
- `docs:` - Dokumentation
|
||||
- `test:` - Tests
|
||||
- `chore:` - Wartungsaufgaben
|
||||
|
||||
### Schritt 3: Code zu Gitea pushen
|
||||
|
||||
```bash
|
||||
# Zu main branch pushen (triggert automatisches Deployment)
|
||||
git push origin main
|
||||
|
||||
# Oder zu anderem Branch pushen (kein Auto-Deploy)
|
||||
git push origin feature/new-feature
|
||||
```
|
||||
|
||||
### Schritt 4: Automatisches Deployment
|
||||
|
||||
**Was passiert automatisch:**
|
||||
|
||||
1. **Git Push** → Gitea erhält den Push
|
||||
2. **Workflow wird getriggert** (bei Push zu `main` Branch)
|
||||
3. **CI/CD Pipeline startet:**
|
||||
- Tests laufen
|
||||
- Docker Image wird gebaut
|
||||
- Image wird zur Registry gepusht
|
||||
- Ansible Deployment wird ausgeführt
|
||||
- Application Stack wird aktualisiert
|
||||
|
||||
**Zeitdauer:** ~8-15 Minuten für komplettes Deployment
|
||||
|
||||
---
|
||||
|
||||
## Deployment-Trigger
|
||||
|
||||
### Automatisches Deployment (bei Push zu `main`)
|
||||
|
||||
**Workflow:** `.gitea/workflows/production-deploy.yml`
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
```
|
||||
|
||||
**Bedeutung:**
|
||||
- ✅ Push zu `main` Branch → Deployment startet automatisch
|
||||
- ❌ Push zu anderen Branches → Kein Deployment
|
||||
- ❌ Push nur von Markdown-Dateien → Kein Deployment (wegen `paths-ignore`)
|
||||
|
||||
**Beispiel:**
|
||||
```bash
|
||||
# Triggert Deployment
|
||||
git push origin main
|
||||
|
||||
# Triggert KEIN Deployment (nur Markdown)
|
||||
git commit -m "docs: Update README" --only README.md
|
||||
git push origin又问
|
||||
|
||||
# Triggert KEIN Deployment (anderer Branch)
|
||||
git checkout -b feature/new-feature
|
||||
git push origin feature/new-feature
|
||||
```
|
||||
|
||||
### Manuelles Deployment (Workflow-Dispatch)
|
||||
|
||||
**Workflow kann manuell gestartet werden:**
|
||||
|
||||
1. Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
|
||||
2. Wähle: "Production Deployment Pipeline"
|
||||
3. Klicke: "Run workflow"
|
||||
4. Wähle Branch (z.B. `main` oder anderer Branch)
|
||||
5. Optionale Einstellungen:
|
||||
- `skip_tests`: `true` (nur in Notfällen!)
|
||||
6. Klicke: "Run workflow"
|
||||
|
||||
**Verwendung:**
|
||||
- Deployment von anderem Branch (z.B. `develop`, `staging`)
|
||||
- Deployment ohne Code-Änderung (z.B. nach Config-Änderung)
|
||||
- Notfall-Deployment mit `skip_tests: true`
|
||||
|
||||
---
|
||||
|
||||
## Workflow-Details
|
||||
|
||||
### Was passiert bei jedem Push zu `main`?
|
||||
|
||||
#### Job 1: Tests (ca. 2-5 Minuten)
|
||||
|
||||
```yaml
|
||||
- PHP 8.3 Setup
|
||||
- Composer Dependencies installieren
|
||||
- Pest Tests ausführen
|
||||
- PHPStan Code Quality Check
|
||||
- Code Style Check (composer cs)
|
||||
```
|
||||
|
||||
**Bei Fehler:** Pipeline stoppt, kein Deployment
|
||||
|
||||
#### Job 2: Build (ca. 3-5 Minuten)
|
||||
|
||||
```yaml
|
||||
- Docker Buildx Setup
|
||||
- Image Metadata generieren (Tag: <short-sha>-<timestamp>)
|
||||
- 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 (ca. 2-4 Minuten)
|
||||
|
||||
```yaml
|
||||
- SSH Setup (mit Secret)
|
||||
- Ansible Installation
|
||||
- Ansible Playbook ausführen:
|
||||
- Image Pull
|
||||
- docker-compose.yml Update
|
||||
- Stack Neustart
|
||||
- Health-Check (10x versuche)
|
||||
- Rollback bei Fehler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Branching-Strategie
|
||||
|
||||
### Empfohlener Workflow
|
||||
|
||||
```
|
||||
main (Production)
|
||||
↓
|
||||
develop (Entwicklung/Testing)
|
||||
↓
|
||||
feature/* (Feature Branches)
|
||||
```
|
||||
|
||||
### Workflow-Beispiele
|
||||
|
||||
#### 1. Feature entwickeln
|
||||
|
||||
```bash
|
||||
# Feature Branch erstellen
|
||||
git checkout -b feature/user-authentication
|
||||
|
||||
# Änderungen machen
|
||||
# ... Code schreiben ...
|
||||
|
||||
# Committen
|
||||
git add .
|
||||
git commit -m "feat: Add user authentication"
|
||||
|
||||
# Zu Gitea pushen (kein Auto-Deploy)
|
||||
git push origin feature/user-authentication
|
||||
|
||||
# Pull Request erstellen in Gitea
|
||||
# → Code Review
|
||||
# → Merge zu develop (oder main)
|
||||
```
|
||||
|
||||
#### 2. Direkt zu Production deployen
|
||||
|
||||
```bash
|
||||
# Änderungen lokal
|
||||
git checkout main
|
||||
# ... Änderungen machen ...
|
||||
git add .
|
||||
git commit -m "fix: Critical bug fix"
|
||||
|
||||
# Pushen → Triggert automatisches Deployment
|
||||
git push origin main
|
||||
|
||||
# Pipeline läuft automatisch:
|
||||
# ✅ Tests
|
||||
# ✅ Build
|
||||
# ✅ Deploy
|
||||
```
|
||||
|
||||
#### 3. Hotfix (Notfall)
|
||||
|
||||
```bash
|
||||
# Hotfix Branch von main
|
||||
git checkout -b hotfix/critical-security-fix main
|
||||
|
||||
# Fix implementieren
|
||||
# ... Code schreiben ...
|
||||
git add .
|
||||
git commit -m "fix: Critical security vulnerability"
|
||||
|
||||
# Direkt zu main mergen
|
||||
git checkout main
|
||||
git merge hotfix/critical-security-fix Ker
|
||||
|
||||
# Pushen → Auto-Deploy
|
||||
git push origin main
|
||||
|
||||
# Optional: Manuelles Deployment mit skip_tests
|
||||
# (nur wenn Tests lokal bereits erfolgreich)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment-Status prüfen
|
||||
|
||||
### 1. Pipeline-Status in Gitea
|
||||
|
||||
```
|
||||
https://git.michaelschiemer.de/michael/michaelschiemer/actions
|
||||
```
|
||||
|
||||
**Status-Anzeigen:**
|
||||
- 🟢 Grüner Haken = Erfolgreich
|
||||
- 🔴 Roter Haken = Fehlgeschlagen
|
||||
- 🟡 Gelber Kreis = Läuft gerade
|
||||
|
||||
### 2. Logs ansehen
|
||||
|
||||
1. Gehe zu Actions
|
||||
2. Klicke auf den Workflow-Run
|
||||
3. Klicke auf Job (z.B. "Deploy to Production Server")
|
||||
4. Klicke auf Step (z.B. "Deploy via Ansible")
|
||||
5. Logs ansehen
|
||||
|
||||
### 3. Application-Status prüfen
|
||||
|
||||
```bash
|
||||
# SSH zum Production-Server
|
||||
ssh deploy@94.16.110.151
|
||||
|
||||
# Container-Status prüfen
|
||||
cd ~/deployment/stacks/application
|
||||
docker compose ps
|
||||
|
||||
# Logs ansehen
|
||||
docker compose logs -f app
|
||||
|
||||
# Health-Check manuell
|
||||
curl https://michaelschiemer.de/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment verhindern
|
||||
|
||||
### Temporäres Deployment verhindern
|
||||
|
||||
**Option 1: Push zu anderem Branch**
|
||||
```bash
|
||||
# Entwickle auf Feature-Branch
|
||||
git checkout -b feature/my-feature
|
||||
git push origin feature/my-feature
|
||||
# → Kein Auto-Deploy
|
||||
```
|
||||
|
||||
**Option CT 2: [skip ci] in Commit-Message**
|
||||
```bash
|
||||
# Workflow wird übersprungen
|
||||
git commit -m "docs: Update documentation [skip ci]"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Hinweis:** `[skip ci]` wird aktuell **nicht** unterstützt, da kein entsprechender Filter im Workflow ist.
|
||||
|
||||
### Deployment-Trigger deaktivieren
|
||||
|
||||
**Temporär (Workflow anpassen):**
|
||||
```yaml
|
||||
# In .gitea/workflows/production-deploy.yml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# workflow_dispatch: # Kommentiere aus für temporäres Deaktivieren
|
||||
```
|
||||
|
||||
**Besser:** Nutze Feature-Branches für Entwicklung ohne Auto-Deploy.
|
||||
|
||||
---
|
||||
|
||||
## Häufige Szenarien
|
||||
|
||||
### Szenario 1: Kleine Bug-Fix
|
||||
|
||||
```bash
|
||||
# 1. Bug-Fix lokal implementieren
|
||||
git checkout main
|
||||
# ... Fix implementieren ...
|
||||
git add .
|
||||
git commit -m "fix: Resolve login issue"
|
||||
|
||||
# 2. Pushen → Auto-Deploy
|
||||
git push origin main
|
||||
|
||||
# 3. Pipeline beobachten
|
||||
# → Tests ✅
|
||||
# → Build ✅
|
||||
# → Deploy ✅
|
||||
|
||||
# 4. Verifizieren
|
||||
curl https://michaels chasing.de/health
|
||||
```
|
||||
|
||||
### Szenario 2: Große Feature-Entwicklung
|
||||
|
||||
```bash
|
||||
# 1. Feature-Branch erstellen
|
||||
git checkout -b feature/new-dashboard
|
||||
|
||||
# 2. Feature entwickeln
|
||||
# ... viele Commits ...
|
||||
|
||||
# 3. Regelmäßig pushen (kein Auto-Deploy)
|
||||
git push origin feature/new-dashboard
|
||||
|
||||
# 4. Pull Request erstellen in Gitea
|
||||
# → Code Review
|
||||
# → Diskussion
|
||||
|
||||
# 5. Merge zu main (triggert Auto-Deploy)
|
||||
# → Via Gitea UI: "Merge Pull Request"
|
||||
# → Oder lokal:
|
||||
git checkout main
|
||||
git merge feature/new-dashboard
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Szenario 3: Config-Änderung ohne Code-Änderung
|
||||
|
||||
```bash
|
||||
# Beispiel: .env Variablen ändern
|
||||
# (wird über Ansible Template generiert, daher direkt auf Server ändern)
|
||||
|
||||
# Oder: docker-compose.yml anpassen
|
||||
# Änderungen machen
|
||||
git add .
|
||||
git commit -m "chore: Update docker-compose configuration"
|
||||
git push origin main
|
||||
|
||||
# → Pipeline läuft
|
||||
# → Build: Keine Code-Änderung, aber Image wird neu getaggt
|
||||
# → Deploy: docker-compose.yml wird aktualisiert
|
||||
```
|
||||
|
||||
### Szenario 4: Notfall-Rollback
|
||||
|
||||
```bash
|
||||
# Option 1: Rollback via Ansible Playbook
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml \
|
||||
playbooks/rollback.yml \
|
||||
-e "rollback_timestamp=2025-10-31T01-20-15Z"
|
||||
|
||||
# Option 2: Alten Commit pushen
|
||||
git log --oneline
|
||||
# Finde letzten funktionierenden Commit
|
||||
git checkout <commit-hash>
|
||||
git checkout -b rollback/previous-version
|
||||
git push origin rollback/previous-version
|
||||
|
||||
# Manuell zu main mergen oder direkt:
|
||||
git checkout main
|
||||
git reset --hard <commit-hash>
|
||||
git push origin main --force # ⚠️ Vorsicht!
|
||||
# → Triggert Auto-Deploy mit altem Code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Commits
|
||||
|
||||
- ✅ Klare, beschreibende Commit-Messages
|
||||
entweder
|
||||
- ✅ Atomic Commits (ein Feature = ein Commit)
|
||||
- ✅ Regelmäßig pushen (nicht alles auf einmal)
|
||||
|
||||
### 2. Testing
|
||||
|
||||
- ✅ Tests lokal ausführen vor Push:
|
||||
```bash
|
||||
composer cs # Code Style
|
||||
make phpstan # Static Analysis
|
||||
./vendor/bin/pest # Tests
|
||||
```
|
||||
|
||||
### 3. Deployment
|
||||
|
||||
- ✅ **Niemals** direkt zu `main` pushen ohne lokale Tests
|
||||
- ✅ Feature-Branches für größere Änderungen
|
||||
- ✅ Pull Requests für Code Review
|
||||
- ✅ Pipeline-Status beobachten nach Push
|
||||
|
||||
### 4. Rollback-Plan
|
||||
|
||||
- ✅ Immer Backup vor größeren Änderungen
|
||||
- ✅ Rollback-Playbook bereit halten
|
||||
- ✅ Deployment-Metadaten dokumentieren
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pipeline schlägt fehl
|
||||
|
||||
**Problem:** Tests fehlgeschlagen
|
||||
```bash
|
||||
# Tests lokal ausführen
|
||||
./vendor/bin/pest
|
||||
|
||||
# Fehler beheben
|
||||
# ... Code anpassen ...
|
||||
git add .
|
||||
git commit -m "fix: Fix failing tests"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Problem:** Build fehlgeschlagen
|
||||
```bash
|
||||
# Docker Build lokal testen
|
||||
docker build -f Dockerfile.production -t test-image .
|
||||
|
||||
# Fehler beheben
|
||||
# ... Dockerfile anpassen ...
|
||||
git add .
|
||||
git commit -m "fix: Fix Docker build"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Problem:** Deployment fehlgeschlagen
|
||||
```bash
|
||||
# SSH zum Server
|
||||
ssh deploy@94.16.110.151
|
||||
|
||||
# Logs prüfen
|
||||
cd ~/deployment/stacks/application
|
||||
docker compose logs app
|
||||
|
||||
# Manuell rollback
|
||||
cd ~/deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml playbooks/rollback.yml
|
||||
```
|
||||
|
||||
### Deployment läuft zu lange
|
||||
|
||||
**Pipeline hängt:**
|
||||
- Prüfe Runner-Status: `docker compose ps` in `deployment/gitea-runner`
|
||||
- Prüfe Runner-Logs: `docker compose logs gitea-runner`
|
||||
- Prüfe Workflow-Logs in Gitea UI
|
||||
|
||||
**Deployment hängt:**
|
||||
- Prüfe Server-Status: `ssh deploy@94.16.110.151 "docker ps"`
|
||||
- Prüfe Container-Logs: `docker compose logs`
|
||||
- Prüfe Disk-Space: `df -h`
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
### Normaler Workflow
|
||||
|
||||
1. **Code ändern** lokal
|
||||
2. **Committen** mit klarer Message
|
||||
3. **Push zu `main`** → Auto-Deploy startet
|
||||
4. **Pipeline beobachten** in Gitea Actions
|
||||
5. **Verifizieren** auf Production
|
||||
|
||||
### Wichtige Commands
|
||||
|
||||
```bash
|
||||
# Änderungen pushen (triggert Auto-Deploy)
|
||||
git push origin main
|
||||
|
||||
# Feature entwickeln (kein Auto-Deploy)
|
||||
git checkout -b feature/my-feature
|
||||
git push origin feature/my-feature
|
||||
|
||||
# Pipeline-Status prüfen
|
||||
# → https://git.michaelschiemer.de/michael/michaelschiemer/actions
|
||||
|
||||
# Application-Status prüfen
|
||||
ssh deploy@94.16.110.151 "cd ~/deployment/stacks/application && docker compose ps"
|
||||
```
|
||||
|
||||
### Deployment-Zeit
|
||||
|
||||
- **Gesamt:** ~8-15 Minuten
|
||||
- **Tests:** ~2-5 Minuten
|
||||
- **Build:** ~3-5 Minuten
|
||||
- **Deploy:** ~2-4 Minuten
|
||||
- **Health-Check:** ~1 Minute
|
||||
|
||||
---
|
||||
|
||||
**Ready to deploy!** 🚀
|
||||
@@ -1,33 +1,63 @@
|
||||
# Deployment Status - Gitea Actions Runner Setup
|
||||
|
||||
**Status**: 🚧 BLOCKED - Phase 1 Step 1.1
|
||||
**Last Updated**: 2025-10-30
|
||||
**Status**: ✅ Phase 3 Complete - Ready for Phase 1
|
||||
**Last Updated**: 2025-10-31
|
||||
**Target Server**: 94.16.110.151 (Netcup)
|
||||
|
||||
---
|
||||
|
||||
## Aktueller Status
|
||||
|
||||
### ✅ Abgeschlossen
|
||||
### ✅ Phase 0: Git Repository SSH Access Setup - COMPLETE
|
||||
|
||||
**Phase 1 - Teilschritte Erledigt**:
|
||||
1. ✅ Runner-Verzeichnisstruktur verifiziert: `/home/michael/dev/michaelschiemer/deployment/gitea-runner/`
|
||||
2. ✅ `.env.example` Template analysiert (23 Zeilen)
|
||||
3. ✅ `docker-compose.yml` Architektur verstanden (47 Zeilen, Docker-in-Docker)
|
||||
4. ✅ `.env` Datei erstellt via: `cp deployment/gitea-runner/.env.example deployment/gitea-runner/.env`
|
||||
1. ✅ Git SSH Key generiert (`~/.ssh/git_michaelschiemer`)
|
||||
2. ✅ SSH Config konfiguriert für `git.michaelschiemer.de`
|
||||
3. ✅ Public Key zu Gitea hinzugefügt
|
||||
4. ✅ Git Remote auf SSH umgestellt
|
||||
5. ✅ Push zu origin funktioniert ohne Credentials
|
||||
|
||||
### ⚠️ BLOCKER - Kritischer Fehler
|
||||
### ✅ Phase 3: Production Server Initial Setup - COMPLETE
|
||||
|
||||
**Problem**: Gitea Admin Panel nicht erreichbar
|
||||
**Infrastructure Stacks deployed via Ansible:**
|
||||
|
||||
**URL**: `https://git.michaelschiemer.de/admin/actions/runners`
|
||||
**Fehler**: `404 page not found`
|
||||
1. ✅ **Traefik** - Reverse Proxy & SSL (healthy)
|
||||
- HTTPS funktioniert, Let's Encrypt SSL aktiv
|
||||
- Dashboard: https://traefik.michaelschiemer.de
|
||||
|
||||
**Impact**:
|
||||
- ❌ Kann Registration Token nicht abrufen (Phase 1, Step 1.1)
|
||||
- ❌ Kann `.env` nicht komplettieren (Step 1.2)
|
||||
- ❌ Kann Runner nicht registrieren (Step 1.3)
|
||||
- ❌ Alle nachfolgenden Phasen (2-8) blockiert
|
||||
2. ✅ **PostgreSQL** - Database Stack (healthy)
|
||||
- Database läuft und ist bereit
|
||||
|
||||
3. ✅ **Docker Registry** - Private Registry (running, accessible)
|
||||
- Authentication konfiguriert
|
||||
- Zugriff erfolgreich getestet
|
||||
|
||||
4. ✅ **Gitea** - Git Server (healthy)
|
||||
- HTTPS erreichbar: https://git.michaelschiemer.de ✅
|
||||
- SSH Port 2222 aktiv
|
||||
- PostgreSQL Database verbunden
|
||||
|
||||
5. ✅ **Monitoring** - Monitoring Stack (deployed)
|
||||
- Grafana: https://grafana.michaelschiemer.de
|
||||
- Prometheus: https://prometheus.michaelschiemer.de
|
||||
- Portainer: https://portainer.michaelschiemer.de
|
||||
|
||||
**Deployment Method:** Ansible Playbook `setup-infrastructure.yml`
|
||||
**Deployment Date:** 2025-10-31
|
||||
|
||||
### ⏳ Phase 1: Gitea Runner Setup - READY TO START
|
||||
|
||||
**Prerequisites erfüllt:**
|
||||
- ✅ Gitea deployed und erreichbar
|
||||
- ✅ Runner-Verzeichnisstruktur vorhanden
|
||||
- ✅ `.env.example` Template analysiert
|
||||
- ✅ `.env` Datei erstellt
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. ⏳ Gitea Admin Panel öffnen: https://git.michaelschiemer.de/admin/actions/runners
|
||||
2. ⏳ Actions in Gitea aktivieren (falls noch nicht geschehen)
|
||||
3. ⏳ Registration Token abrufen
|
||||
4. ⏳ Token in `.env` eintragen
|
||||
5. ⏳ Runner registrieren und starten
|
||||
|
||||
---
|
||||
|
||||
|
||||
281
deployment/DEPLOYMENT-TODO.md
Normal file
281
deployment/DEPLOYMENT-TODO.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Deployment TODO - Komplette Implementierung
|
||||
|
||||
**Status**: 🔄 In Progress
|
||||
**Letzte Aktualisierung**: 2025-10-31
|
||||
**Ziel**: Komplettes Deployment-Setup im `deployment/` Ordner
|
||||
|
||||
---
|
||||
|
||||
## ✅ Bereits Fertig
|
||||
|
||||
### Infrastructure Stacks (deployed via Ansible)
|
||||
- ✅ **Traefik** - Reverse Proxy & SSL
|
||||
- ✅ **PostgreSQL** - Database Stack
|
||||
- ✅ **Docker Registry** - Private Registry
|
||||
- ✅ **Gitea** - Git Server + MySQL + Redis
|
||||
- ✅ **Monitoring** - Portainer + Grafana + Prometheus
|
||||
- ✅ **WireGuard VPN** - VPN Server
|
||||
|
||||
### Ansible Playbooks
|
||||
- ✅ `setup-infrastructure.yml` - Infrastructure Stacks Deployment
|
||||
- ✅ `setup-wireguard.yml` - WireGuard VPN Setup
|
||||
- ✅ `add-wireguard-client.yml` - WireGuard Client hinzufügen
|
||||
- ✅ `deploy-update.yml` - Application Update Deployment
|
||||
- ✅ `rollback.yml` - Rollback zu vorheriger Version
|
||||
- ✅ `setup-production-secrets.yml` - Secrets Deployment
|
||||
- ✅ `setup-ssl-certificates.yml` - SSL Certificate Setup
|
||||
- ✅ `sync-stacks.yml` - Stacks synchronisieren
|
||||
|
||||
### Dokumentation
|
||||
- ✅ `README.md` - Deployment Übersicht
|
||||
- ✅ `SETUP-GUIDE.md` - Komplette Setup-Anleitung
|
||||
- ✅ `DEPLOYMENT-STATUS.md` - Aktueller Status
|
||||
- ✅ `docs/WIREGUARD-SETUP.md` - WireGuard Dokumentation
|
||||
|
||||
---
|
||||
|
||||
## ⏳ Offene Aufgaben
|
||||
|
||||
### 1. Application Stack Integration
|
||||
|
||||
**Status**: ⚠️ Fehlt in `setup-infrastructure.yml`
|
||||
|
||||
**Was fehlt:**
|
||||
- [x] Application Stack zu `setup-infrastructure.yml` hinzufügen ✅
|
||||
- [x] `.env` Template für Application Stack erstellen (`application.env.j2`) ✅
|
||||
- [x] Ansible Playbook/Task für Application Stack Deployment ✅
|
||||
- [x] Database-Migration nach Application Deployment ✅
|
||||
- [x] Health-Check nach Application Deployment ✅
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/stacks/application/docker-compose.yml` ✅ Vorhanden
|
||||
- `deployment/stacks/application/.env.example` ✅ Vorhanden
|
||||
- `deployment/stacks/application/.env` ❌ Fehlt (muss generiert werden)
|
||||
- `deployment/ansible/templates/application.env.j2` ❌ Fehlt (Template für `.env`)
|
||||
- `deployment/ansible/playbooks/setup-infrastructure.yml` ⚠️ Application fehlt
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. Application Stack Deployment Task zu `setup-infrastructure.yml` hinzufügen
|
||||
2. `.env` Template erstellen (mit Passwörtern aus Vault)
|
||||
3. Database-Migration nach Application Start
|
||||
4. Health-Check Integration
|
||||
|
||||
---
|
||||
|
||||
### 2. Application Stack .env Konfiguration
|
||||
|
||||
**Status**: ✅ Erledigt
|
||||
|
||||
**Was erledigt:**
|
||||
- [x] Ansible Template für `.env` Datei erstellt (`application.env.j2`) ✅
|
||||
- [x] Passwörter aus Vault/PostgreSQL .env laden ✅
|
||||
- [x] Domain-Konfiguration aus Inventory ✅
|
||||
- [x] Environment-Variablen aus Vault/Template generieren ✅
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/stacks/application/.env.example` ✅ Vorhanden (angepasst für PostgreSQL)
|
||||
- `deployment/stacks/application/.env` ⚠️ Wird automatisch generiert
|
||||
- `deployment/ansible/templates/application.env.j2` ✅ Erstellt
|
||||
- `deployment/stacks/application/docker-compose.yml` ✅ Angepasst (PostgreSQL statt MySQL)
|
||||
|
||||
---
|
||||
|
||||
### 3. Gitea Runner Setup abschließen
|
||||
|
||||
**Status**: ⏳ Wartet auf Registration Token
|
||||
|
||||
**Was fehlt:**
|
||||
- [ ] Gitea Admin Panel öffnen: https://git.michaelschiemer.de/admin/actions/runners
|
||||
- [ ] Actions in Gitea aktivieren (falls noch nicht geschehen)
|
||||
- [ ] Registration Token abrufen
|
||||
- [ ] Token in `.env` eintragen
|
||||
- [ ] Runner registrieren und starten
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/gitea-runner/.env` ⚠️ Vorhanden, aber Token fehlt
|
||||
- `deployment/gitea-runner/.env.example` ✅ Vorhanden
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. Gitea Actions aktivieren (Admin Panel)
|
||||
2. Runner Registration Token generieren
|
||||
3. Token in `.env` eintragen
|
||||
4. Runner starten: `cd deployment/gitea-runner && docker compose up -d`
|
||||
|
||||
---
|
||||
|
||||
### 4. CI/CD Pipeline finalisieren
|
||||
|
||||
**Status**: ⚠️ Existiert, aber muss konfiguriert und getestet werden
|
||||
|
||||
**Was fehlt:**
|
||||
- [x] **Gitea Repository Secrets konfigurieren:** ✅
|
||||
- [x] `REGISTRY_USER` (Docker Registry Benutzername) ✅
|
||||
- [x] `REGISTRY_PASSWORD` (Docker Registry Passwort) ✅
|
||||
- [x] `SSH_PRIVATE_KEY` (SSH Private Key für Production-Server) ✅
|
||||
- [ ] `GITEA_TOKEN` (Optional: Für automatische Issue-Erstellung)
|
||||
- [x] **Gitea Runner registrieren:** ✅
|
||||
- [x] Registration Token von Gitea Admin Panel abgerufen ✅
|
||||
- [x] Token in `deployment/gitea-runner/.env` eingetragen ✅
|
||||
- [x] Runner registriert ✅
|
||||
- [x] Runner läuft ✅
|
||||
- [ ] **Pipeline End-to-End testen:**
|
||||
- [ ] Test-Commit pushen oder Workflow manuell triggern
|
||||
- [ ] Alle Jobs erfolgreich (Tests, Build, Deploy)
|
||||
- [ ] Deployment erfolgreich auf Production
|
||||
- [ ] Health-Check erfolgreich
|
||||
|
||||
**Dateien:**
|
||||
- `.gitea/workflows/production-deploy.yml` ✅ Vorhanden
|
||||
- `.gitea/workflows/update-production-secrets.yml` ✅ Vorhanden
|
||||
- `.gitea/workflows/security-scan.yml` ✅ Vorhanden
|
||||
- `deployment/CI_CD_STATUS.md` ✅ Neu erstellt (detaillierte Checkliste)
|
||||
|
||||
**Detaillierte Anleitung:**
|
||||
Siehe `deployment/CI_CD_STATUS.md` für komplette Checkliste und Setup-Anleitung.
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. **Secrets in Gitea konfigurieren:**
|
||||
- Gehe zu: `https://git.michaelschiemer.de/michael/michaelschiemer/settings/secrets/actions`
|
||||
- Füge Secrets hinzu: `REGISTRY_USER`, `REGISTRY_PASSWORD`, `SSH_PRIVATE_KEY`
|
||||
2. **Gitea Runner registrieren:**
|
||||
- Token von: `https://git.michaelschiemer.de/admin/actions/runners`
|
||||
- Konfiguriere `deployment/gitea-runner/.env`
|
||||
- Führe `./register.sh` aus
|
||||
3. **Pipeline testen:**
|
||||
- Workflow manuell triggern oder Test-Commit pushen
|
||||
- Logs beobachten und Fehler beheben
|
||||
|
||||
---
|
||||
|
||||
### 5. Backup & Rollback Scripts
|
||||
|
||||
**Status**: ⚠️ Teilweise vorhanden
|
||||
|
||||
**Was fehlt:**
|
||||
- [ ] Backup-Playbook für Application Stack
|
||||
- [ ] Rollback-Playbook testen und finalisieren
|
||||
- [ ] PostgreSQL Backup-Integration
|
||||
- [ ] Gitea Data Backup
|
||||
- [ ] Registry Images Backup
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/ansible/playbooks/rollback.yml` ✅ Vorhanden
|
||||
- `deployment/scripts/rollback.sh` ✅ Vorhanden
|
||||
- `deployment/stacks/postgresql/scripts/backup.sh` ✅ Vorhanden
|
||||
- `deployment/ansible/playbooks/backup.yml` ❌ Fehlt
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. Backup-Playbook erstellen
|
||||
2. Rollback-Playbook testen
|
||||
3. Backup-Scripte finalisieren
|
||||
4. Automatisierte Backups konfigurieren
|
||||
|
||||
---
|
||||
|
||||
### 6. Deployment Scripts finalisieren
|
||||
|
||||
**Status**: ⚠️ Vorhanden, aber muss angepasst werden
|
||||
|
||||
**Was fehlt:**
|
||||
- [ ] `deployment/scripts/deploy.sh` testen und anpassen
|
||||
- [ ] `deployment/scripts/rollback.sh` testen und anpassen
|
||||
- [ ] `deployment/scripts/setup-production.sh` finalisieren
|
||||
- [ ] Scripts für alle Stacks (nicht nur Application)
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/scripts/deploy.sh` ✅ Vorhanden (aber Docker Swarm statt Compose?)
|
||||
- `deployment/scripts/rollback.sh` ✅ Vorhanden
|
||||
- `deployment/scripts/setup-production.sh` ✅ Vorhanden
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. Scripts prüfen und anpassen (Docker Compose statt Swarm)
|
||||
2. Scripts testen
|
||||
3. Integration mit Ansible Playbooks
|
||||
|
||||
---
|
||||
|
||||
### 7. Dokumentation vervollständigen
|
||||
|
||||
**Status**: ⚠️ Gut, aber einige Updates nötig
|
||||
|
||||
**Was fehlt:**
|
||||
- [ ] `DEPLOYMENT-STATUS.md` aktualisieren (Application Stack Status)
|
||||
- [ ] `README.md` aktualisieren (Application Stack Deployment)
|
||||
- [ ] `SETUP-GUIDE.md` aktualisieren (Application Stack Phase)
|
||||
- [ ] Troubleshooting Guide für Application Stack
|
||||
|
||||
**Dateien:**
|
||||
- `deployment/README.md` ⚠️ Muss aktualisiert werden
|
||||
- `deployment/SETUP-GUIDE.md` ⚠️ Muss aktualisiert werden
|
||||
- `deployment/DEPLOYMENT-STATUS.md` ⚠️ Muss aktualisiert werden
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Priorisierte Reihenfolge
|
||||
|
||||
### Phase 1: Application Stack Deployment (KRITISCH)
|
||||
|
||||
1. **Application Stack zu setup-infrastructure.yml hinzufügen**
|
||||
- Task für Application Stack Deployment
|
||||
- `.env` Template erstellen
|
||||
- Database-Migration nach Deployment
|
||||
|
||||
2. **Application .env Konfiguration**
|
||||
- Template `application.env.j2` erstellen
|
||||
- Passwörter aus Vault laden
|
||||
- Template in Playbook integrieren
|
||||
|
||||
### Phase 2: CI/CD Setup
|
||||
|
||||
3. **Gitea Runner Setup abschließen**
|
||||
- Token abrufen und konfigurieren
|
||||
- Runner starten
|
||||
|
||||
4. **CI/CD Pipeline finalisieren**
|
||||
- Secrets in Gitea konfigurieren
|
||||
- Pipeline testen
|
||||
|
||||
### Phase 3: Backup & Scripts
|
||||
|
||||
5. **Backup & Rollback Scripts**
|
||||
- Backup-Playbook erstellen
|
||||
- Rollback testen
|
||||
|
||||
6. **Deployment Scripts finalisieren**
|
||||
- Scripts testen und anpassen
|
||||
|
||||
### Phase 4: Dokumentation
|
||||
|
||||
7. **Dokumentation aktualisieren**
|
||||
- README aktualisieren
|
||||
- Status-Dokumente aktualisieren
|
||||
|
||||
---
|
||||
|
||||
## 📋 Quick Checklist
|
||||
|
||||
### Application Stack
|
||||
- [ ] Application Stack in `setup-infrastructure.yml` hinzufügen
|
||||
- [ ] `.env` Template (`application.env.j2`) erstellen
|
||||
- [ ] Database-Migration Task hinzufügen
|
||||
- [ ] Health-Check nach Deployment
|
||||
|
||||
### CI/CD
|
||||
- [ ] Gitea Runner Token konfigurieren
|
||||
- [ ] Runner starten
|
||||
- [ ] Secrets in Gitea konfigurieren
|
||||
- [ ] Pipeline testen
|
||||
|
||||
### Scripts & Backup
|
||||
- [ ] Backup-Playbook erstellen
|
||||
- [ ] Rollback testen
|
||||
- [ ] Deployment-Scripts finalisieren
|
||||
|
||||
### Dokumentation
|
||||
- [ ] README aktualisieren
|
||||
- [ ] SETUP-GUIDE aktualisieren
|
||||
- [ ] DEPLOYMENT-STATUS aktualisieren
|
||||
|
||||
---
|
||||
|
||||
**Nächster Schritt**: Application Stack zu `setup-infrastructure.yml` hinzufügen und `.env` Template erstellen
|
||||
155
deployment/NATIVE-WORKFLOW-README.md
Normal file
155
deployment/NATIVE-WORKFLOW-README.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Native Workflow ohne GitHub Actions
|
||||
|
||||
## Problem
|
||||
|
||||
Der aktuelle Workflow (`production-deploy.yml`) verwendet GitHub Actions wie:
|
||||
- `actions/checkout@v4`
|
||||
- `shivammathur/setup-php@v2`
|
||||
- `actions/cache@v3`
|
||||
- `docker/setup-buildx-action@v3`
|
||||
- `docker/build-push-action@v5`
|
||||
|
||||
Diese Actions müssen von GitHub geladen werden, was zu Abbrüchen führen kann wenn:
|
||||
- GitHub nicht erreichbar ist
|
||||
- Actions nicht geladen werden können
|
||||
- Timeouts auftreten
|
||||
|
||||
## Lösung: Native Workflow
|
||||
|
||||
Die Datei `.gitea/workflows/production-deploy-native.yml` verwendet **nur Shell-Commands** und keine GitHub Actions:
|
||||
|
||||
### Vorteile
|
||||
|
||||
1. **Keine GitHub-Abhängigkeit**: Funktioniert komplett offline
|
||||
2. **Schneller**: Keine Action-Downloads
|
||||
3. **Weniger Fehlerquellen**: Direkte Shell-Commands statt Actions
|
||||
4. **Einfacher zu debuggen**: Standard-Bash-Scripts
|
||||
|
||||
### Änderungen
|
||||
|
||||
#### 1. Checkout
|
||||
**Vorher:**
|
||||
```yaml
|
||||
- uses: actions/checkout@v4
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```bash
|
||||
git clone --depth 1 --branch "$REF_NAME" \
|
||||
"https://git.michaelschiemer.de/${{ github.repository }}.git" \
|
||||
/workspace/repo
|
||||
```
|
||||
|
||||
#### 2. PHP Setup
|
||||
**Vorher:**
|
||||
```yaml
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```bash
|
||||
apt-get update
|
||||
apt-get install -y php8.3 php8.3-cli php8.3-mbstring ...
|
||||
```
|
||||
|
||||
#### 3. Cache
|
||||
**Vorher:**
|
||||
```yaml
|
||||
- uses: actions/cache@v3
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```bash
|
||||
# Einfaches Datei-basiertes Caching
|
||||
if [ -d "/tmp/composer-cache/vendor" ]; then
|
||||
cp -r /tmp/composer-cache/vendor /workspace/repo/vendor
|
||||
fi
|
||||
```
|
||||
|
||||
#### 4. Docker Buildx
|
||||
**Vorher:**
|
||||
```yaml
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```bash
|
||||
docker buildx create --name builder --use || docker buildx use builder
|
||||
docker buildx inspect --bootstrap
|
||||
```
|
||||
|
||||
#### 5. Docker Build/Push
|
||||
**Vorher:**
|
||||
```yaml
|
||||
- uses: docker/build-push-action@v5
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```bash
|
||||
docker buildx build \
|
||||
--file ./Dockerfile.production \
|
||||
--tag $REGISTRY/$IMAGE_NAME:latest \
|
||||
--push \
|
||||
.
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Option 1: Native Workflow aktivieren
|
||||
|
||||
1. **Benenne um:**
|
||||
```bash
|
||||
mv .gitea/workflows/production-deploy.yml .gitea/workflows/production-deploy-with-actions.yml.bak
|
||||
mv .gitea/workflows/production-deploy-native.yml .gitea/workflows/production-deploy.yml
|
||||
```
|
||||
|
||||
2. **Commite und pushe:**
|
||||
```bash
|
||||
git add .gitea/workflows/production-deploy.yml
|
||||
git commit -m "chore: switch to native workflow without GitHub Actions"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Option 2: Beide parallel testen
|
||||
|
||||
Lass beide Workflows parallel laufen:
|
||||
- `production-deploy.yml` - Mit Actions (aktuell)
|
||||
- `production-deploy-native.yml` - Native (neue Version)
|
||||
|
||||
## Gitea Actions Konfiguration
|
||||
|
||||
**Wichtig:** Wenn wir die native Version verwenden, brauchen wir `DEFAULT_ACTIONS_URL` **nicht mehr** in der Gitea-Konfiguration.
|
||||
|
||||
Aber es schadet auch nicht, es drin zu lassen für zukünftige Workflows.
|
||||
|
||||
## Debugging
|
||||
|
||||
Wenn der native Workflow nicht funktioniert:
|
||||
|
||||
1. **Prüfe Git Clone:**
|
||||
```bash
|
||||
# Im Runner Container
|
||||
git clone --depth 1 https://git.michaelschiemer.de/michael/michaelschiemer.git /tmp/test
|
||||
```
|
||||
|
||||
2. **Prüfe Docker Buildx:**
|
||||
```bash
|
||||
docker buildx version
|
||||
docker buildx ls
|
||||
```
|
||||
|
||||
3. **Prüfe PHP Installation:**
|
||||
```bash
|
||||
php --version
|
||||
php -m # Zeigt installierte Module
|
||||
```
|
||||
|
||||
## Empfehlung
|
||||
|
||||
**Für Stabilität:** Verwende die native Version (`production-deploy-native.yml`)
|
||||
|
||||
**Für Kompatibilität:** Bleib bei der Actions-Version (`production-deploy.yml`)
|
||||
|
||||
Die native Version sollte stabiler sein, da sie keine externen Dependencies benötigt.
|
||||
@@ -129,7 +129,43 @@ docker compose up -d
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
The CI/CD pipeline is defined in `.gitea/workflows/deploy.yml` and runs on push to main branch:
|
||||
The CI/CD pipeline is defined in `.gitea/workflows/production-deploy.yml` and runs automatically on push to `main` branch.
|
||||
|
||||
### Quick Start: Deploy Code Changes
|
||||
|
||||
```bash
|
||||
# 1. Make changes locally
|
||||
# ... edit files ...
|
||||
|
||||
# 2. Commit changes
|
||||
git add .
|
||||
git commit -m "feat: Add new feature"
|
||||
|
||||
# 3. Push to main → Automatic deployment starts
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**What happens automatically:**
|
||||
- ✅ Tests run (~2-5 min)
|
||||
- ✅ Docker image is built (~3-5 min)
|
||||
- ✅ Image is pushed to registry (~1-2 min)
|
||||
- ✅ Ansible deployment runs (~2-4 min)
|
||||
- ✅ Application stack is updated
|
||||
|
||||
**Total time:** ~8-15 minutes
|
||||
|
||||
**Status check:**
|
||||
- Pipeline status: `https://git.michaelschiemer.de/michael/michaelschiemer/actions`
|
||||
- Application status: `ssh deploy@94.16.110.151 "cd ~/deployment/stacks/application && docker compose ps"`
|
||||
|
||||
**📖 Detailed Documentation:**
|
||||
- **[Code Change Workflow](CODE_CHANGE_WORKFLOW.md)** - Complete guide for pushing code changes
|
||||
- **[Application Stack Deployment](APPLICATION_STACK_DEPLOYMENT.md)** - How deployment works in detail
|
||||
- **[CI/CD Status](CI_CD_STATUS.md)** - Current CI/CD pipeline status
|
||||
|
||||
### Pipeline Details
|
||||
|
||||
The CI/CD pipeline runs on push to main branch:
|
||||
|
||||
1. **Build Stage**: Build Docker image
|
||||
2. **Push Stage**: Push to private registry
|
||||
|
||||
@@ -22,7 +22,8 @@ This guide walks through the complete setup of production deployment from scratc
|
||||
**Development Machine:**
|
||||
- ✅ Docker & Docker Compose installed
|
||||
- ✅ Ansible installed (`pip install ansible`)
|
||||
- ✅ SSH key for production server
|
||||
- ✅ SSH key for production server (`~/.ssh/production`)
|
||||
- ✅ Git SSH key configured (see Phase 0)
|
||||
- ✅ Access to Gitea admin panel
|
||||
|
||||
**Production Server (94.16.110.151):**
|
||||
@@ -33,6 +34,83 @@ This guide walks through the complete setup of production deployment from scratc
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Git Repository SSH Access Setup (Development Machine)
|
||||
|
||||
### Step 0.1: Generate Git SSH Key
|
||||
|
||||
Create a separate SSH key specifically for Git operations (different from the production server SSH key):
|
||||
|
||||
```bash
|
||||
# Generate SSH key for Git
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/git_michaelschiemer -C "git@michaelschiemer.de" -N ""
|
||||
|
||||
# Set correct permissions
|
||||
chmod 600 ~/.ssh/git_michaelschiemer
|
||||
chmod 644 ~/.ssh/git_michaelschiemer.pub
|
||||
```
|
||||
|
||||
### Step 0.2: Configure SSH Config
|
||||
|
||||
Add Git SSH configuration to `~/.ssh/config`:
|
||||
|
||||
```bash
|
||||
# Edit SSH config
|
||||
nano ~/.ssh/config
|
||||
```
|
||||
|
||||
Add the following configuration:
|
||||
|
||||
```
|
||||
Host git.michaelschiemer.de
|
||||
HostName git.michaelschiemer.de
|
||||
Port 2222
|
||||
User git
|
||||
IdentityFile ~/.ssh/git_michaelschiemer
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
```
|
||||
|
||||
### Step 0.3: Add Public Key to Gitea
|
||||
|
||||
1. Display your public key:
|
||||
```bash
|
||||
cat ~/.ssh/git_michaelschiemer.pub
|
||||
```
|
||||
|
||||
2. Copy the output (starts with `ssh-ed25519 ...`)
|
||||
|
||||
3. In Gitea:
|
||||
- Go to **Settings** → **SSH / GPG Keys**
|
||||
- Click **Add Key**
|
||||
- Paste the public key
|
||||
- Click **Add Key**
|
||||
|
||||
4. Verify the connection:
|
||||
```bash
|
||||
ssh -T git@git.michaelschiemer.de
|
||||
```
|
||||
|
||||
Expected output: `Hi there! You've successfully authenticated...`
|
||||
|
||||
### Step 0.4: Update Git Remote (if needed)
|
||||
|
||||
If your `origin` remote uses HTTPS, switch it to SSH:
|
||||
|
||||
```bash
|
||||
# Check current remote URL
|
||||
git remote -v
|
||||
|
||||
# Update to SSH
|
||||
git remote set-url origin git@git.michaelschiemer.de:michael/michaelschiemer.git
|
||||
|
||||
# Test push (should work without password prompt)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Note**: This SSH key is separate from the production server SSH key (`~/.ssh/production`). The production key is used for Ansible/server access, while the Git key is only for repository operations.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Gitea Runner Setup (Development Machine)
|
||||
|
||||
### Step 1.1: Get Gitea Registration Token
|
||||
@@ -233,99 +311,107 @@ ansible-vault view production.vault.yml \
|
||||
|
||||
## Phase 3: Production Server Initial Setup
|
||||
|
||||
### Step 3.1: Deploy Infrastructure Stacks
|
||||
### Prerequisites
|
||||
|
||||
**On Production Server (SSH as deploy user):**
|
||||
Before running Phase 3, ensure:
|
||||
- ✅ SSH access to production server configured (`~/.ssh/production`)
|
||||
- ✅ Repository cloned on production server at `~/deployment/stacks` (or adjust `stacks_base_path` in playbook)
|
||||
- ✅ Ansible installed on your development machine: `pip install ansible`
|
||||
- ✅ Ansible collections installed: `ansible-galaxy collection install community.docker`
|
||||
|
||||
### Step 3.1: Clone Repository on Production Server (if not already done)
|
||||
|
||||
**On Production Server:**
|
||||
|
||||
```bash
|
||||
# SSH to production server
|
||||
ssh deploy@94.16.110.151
|
||||
|
||||
# Navigate to stacks directory
|
||||
cd ~/deployment/stacks
|
||||
|
||||
# Deploy stacks in order
|
||||
|
||||
# 1. Traefik (Reverse Proxy & SSL)
|
||||
cd traefik
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
# Wait for "Configuration loaded" message
|
||||
# Ctrl+C to exit logs
|
||||
|
||||
# 2. PostgreSQL (Database)
|
||||
cd ../postgresql
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
# Wait for "database system is ready to accept connections"
|
||||
# Ctrl+C to exit logs
|
||||
|
||||
# 3. Docker Registry (Private Registry)
|
||||
cd ../registry
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
# Wait for "listening on [::]:5000"
|
||||
# Ctrl+C to exit logs
|
||||
|
||||
# 4. Gitea (Git Server + MySQL + Redis)
|
||||
cd ../gitea
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
# Wait for "Listen: http://0.0.0.0:3000"
|
||||
# Ctrl+C to exit logs
|
||||
|
||||
# 5. Monitoring (Portainer + Grafana + Prometheus)
|
||||
cd ../monitoring
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
# Wait for all services to start
|
||||
# Ctrl+C to exit logs
|
||||
|
||||
# Verify all stacks are running
|
||||
docker ps
|
||||
# Clone repository (if not already present)
|
||||
mkdir -p ~/deployment
|
||||
cd ~/deployment
|
||||
git clone git@git.michaelschiemer.de:michael/michaelschiemer.git . || git clone https://git.michaelschiemer.de/michael/michaelschiemer.git .
|
||||
```
|
||||
|
||||
### Step 3.2: Configure Gitea
|
||||
|
||||
1. Access Gitea: https://git.michaelschiemer.de
|
||||
2. Complete initial setup wizard:
|
||||
- Database: Use MySQL from stack
|
||||
- Admin account: Create admin user
|
||||
- Repository root: `/data/git/repositories`
|
||||
- Enable Actions in admin settings
|
||||
|
||||
### Step 3.3: Create Docker Registry User
|
||||
|
||||
```bash
|
||||
# SSH to production server
|
||||
ssh deploy@94.16.110.151
|
||||
|
||||
# Create registry htpasswd entry
|
||||
cd ~/deployment/stacks/registry
|
||||
docker compose exec registry htpasswd -Bbn admin your-registry-password >> auth/htpasswd
|
||||
|
||||
# Test login
|
||||
docker login git.michaelschiemer.de:5000
|
||||
# Username: admin
|
||||
# Password: your-registry-password
|
||||
```
|
||||
|
||||
### Step 3.4: Setup SSH Keys for Ansible
|
||||
### Step 3.2: Deploy Infrastructure Stacks with Ansible
|
||||
|
||||
**On Development Machine:**
|
||||
|
||||
```bash
|
||||
# Generate SSH key if not exists
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/production -C "ansible-deploy"
|
||||
# Navigate to Ansible directory
|
||||
cd deployment/ansible
|
||||
|
||||
# Copy public key to production server
|
||||
ssh-copy-id -i ~/.ssh/production.pub deploy@94.16.110.151
|
||||
# Run infrastructure deployment playbook
|
||||
ansible-playbook playbooks/setup-infrastructure.yml \
|
||||
-i inventory/production.yml
|
||||
|
||||
# Test SSH connection
|
||||
ssh -i ~/.ssh/production deploy@94.16.110.151 "echo 'SSH works!'"
|
||||
# The playbook will:
|
||||
# 1. Create required Docker networks (traefik-public, app-internal)
|
||||
# 2. Deploy Traefik (Reverse Proxy & SSL)
|
||||
# 3. Deploy PostgreSQL (Database)
|
||||
# 4. Deploy Docker Registry (Private Registry)
|
||||
# 5. Deploy Gitea (Git Server + PostgreSQL)
|
||||
# 6. Deploy Monitoring (Portainer + Grafana + Prometheus)
|
||||
# 7. Wait for all services to be healthy
|
||||
# 8. Verify accessibility
|
||||
```
|
||||
|
||||
**✅ Checkpoint**: All infrastructure stacks running, SSH access configured
|
||||
**Expected output:**
|
||||
- ✅ All stacks deployed successfully
|
||||
- ✅ All services healthy
|
||||
- ✅ Gitea accessible at https://git.michaelschiemer.de
|
||||
|
||||
**Note:** If monitoring passwords need to be stored in Vault (recommended for production), add them to `secrets/production.vault.yml`:
|
||||
- `vault_grafana_admin_password`
|
||||
- `vault_prometheus_password`
|
||||
|
||||
Then run the playbook with vault:
|
||||
```bash
|
||||
ansible-playbook playbooks/setup-infrastructure.yml \
|
||||
-i inventory/production.yml \
|
||||
--vault-password-file secrets/.vault_pass
|
||||
```
|
||||
|
||||
### Step 3.3: Configure Gitea (Manual Step)
|
||||
|
||||
1. Access Gitea: https://git.michaelschiemer.de
|
||||
2. Complete initial setup wizard (first-time only):
|
||||
- **Database Type**: PostgreSQL
|
||||
- **Database Host**: `postgres:5432`
|
||||
- **Database User**: `gitea`
|
||||
- **Database Password**: `gitea_password` (or check `deployment/stacks/gitea/docker-compose.yml`)
|
||||
- **Database Name**: `gitea`
|
||||
- **Admin Account**: Create your admin user
|
||||
- **Repository Root**: `/data/git/repositories` (default)
|
||||
3. **Enable Actions** (required for Phase 1):
|
||||
- Go to **Site Administration** → **Actions**
|
||||
- Enable **Enable Actions** checkbox
|
||||
- Save settings
|
||||
|
||||
### Step 3.4: Verify Docker Registry
|
||||
|
||||
The Ansible playbook automatically creates registry authentication. To retrieve credentials:
|
||||
|
||||
```bash
|
||||
# SSH to production server
|
||||
ssh deploy@94.16.110.151
|
||||
|
||||
# View registry htpasswd (contains username:password hash)
|
||||
cat ~/deployment/stacks/registry/auth/htpasswd
|
||||
|
||||
# The default username is 'admin'
|
||||
# Password hash can be used to login, or create new user:
|
||||
cd ~/deployment/stacks/registry
|
||||
docker compose exec registry htpasswd -Bbn <username> <password> >> auth/htpasswd
|
||||
docker compose restart registry
|
||||
|
||||
# Test login
|
||||
docker login registry.michaelschiemer.de
|
||||
# Or if using port:
|
||||
docker login git.michaelschiemer.de:5000
|
||||
```
|
||||
|
||||
**✅ Checkpoint**: All infrastructure stacks running, Gitea accessible, Actions enabled
|
||||
|
||||
---
|
||||
|
||||
|
||||
185
deployment/WORKFLOW-TROUBLESHOOTING.md
Normal file
185
deployment/WORKFLOW-TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Workflow Troubleshooting Guide
|
||||
|
||||
## Problem: Workflows brechen zwischendurch ab
|
||||
|
||||
### Mögliche Ursachen
|
||||
|
||||
#### 1. Actions werden nicht geladen
|
||||
|
||||
**Symptom:** Workflow startet, aber Actions wie `actions/checkout@v4` schlagen fehl
|
||||
|
||||
**Lösung:** Prüfe Gitea Konfiguration:
|
||||
```bash
|
||||
docker exec gitea cat /data/gitea/conf/app.ini | grep -A 3 '[actions]'
|
||||
```
|
||||
|
||||
Sollte enthalten:
|
||||
```ini
|
||||
[actions]
|
||||
ENABLED = true
|
||||
DEFAULT_ACTIONS_URL = https://github.com
|
||||
```
|
||||
|
||||
#### 2. Timeouts bei langen Steps
|
||||
|
||||
**Symptom:** Workflow läuft eine Zeit, dann Timeout
|
||||
|
||||
**Lösung:** Timeout in Runner Config erhöhen:
|
||||
```yaml
|
||||
# deployment/gitea-runner/config.yaml
|
||||
runner:
|
||||
timeout: 6h # Erhöhe von 3h auf 6h
|
||||
```
|
||||
|
||||
Dann Runner neu starten:
|
||||
```bash
|
||||
cd deployment/gitea-runner
|
||||
docker compose restart gitea-runner
|
||||
```
|
||||
|
||||
#### 3. Docker Buildx Probleme
|
||||
|
||||
**Symptom:** Build Step schlägt fehl oder bricht ab
|
||||
|
||||
**Lösung:** Prüfe, ob Buildx richtig läuft. Alternativ: Direktes Docker Build verwenden.
|
||||
|
||||
#### 4. GitHub-Variablen in Gitea
|
||||
|
||||
**Symptom:** `${{ github.sha }}` ist leer oder falsch
|
||||
|
||||
**Lösung:** Gitea Actions sollte `github.*` Variablen unterstützen, aber manchmal funktioniert `gitea.*` besser.
|
||||
|
||||
**Test:** Prüfe in Workflow-Logs, welche Variablen verfügbar sind:
|
||||
```yaml
|
||||
- name: Debug variables
|
||||
run: |
|
||||
echo "GITHUB_SHA: ${{ github.sha }}"
|
||||
echo "GITEA_SHA: ${{ gitea.sha }}"
|
||||
echo "RUNNER_OS: ${{ runner.os }}"
|
||||
```
|
||||
|
||||
#### 5. Secrets fehlen oder sind falsch
|
||||
|
||||
**Symptom:** Registry Login oder SSH schlägt fehl
|
||||
|
||||
**Lösung:** Prüfe Secrets in Gitea:
|
||||
- Repository → Settings → Secrets
|
||||
- Alle benötigten Secrets sollten vorhanden sein:
|
||||
- `REGISTRY_USER`
|
||||
- `REGISTRY_PASSWORD`
|
||||
- `SSH_PRIVATE_KEY`
|
||||
- `ANSIBLE_VAULT_PASSWORD` (falls verwendet)
|
||||
|
||||
### Debugging-Schritte
|
||||
|
||||
#### 1. Workflow-Logs analysieren
|
||||
|
||||
In Gitea UI:
|
||||
1. Gehe zu Actions → Fehlgeschlagener Workflow
|
||||
2. Klicke auf fehlgeschlagene Step
|
||||
3. Prüfe Logs für Fehlermeldungen
|
||||
4. Suche nach:
|
||||
- `error`
|
||||
- `timeout`
|
||||
- `failed`
|
||||
- `exit code`
|
||||
|
||||
#### 2. Runner-Logs prüfen
|
||||
|
||||
```bash
|
||||
cd deployment/gitea-runner
|
||||
docker compose logs gitea-runner --tail=100 | grep -E "(error|failed|timeout)"
|
||||
```
|
||||
|
||||
#### 3. Runner Status prüfen
|
||||
|
||||
In Gitea: https://git.michaelschiemer.de/admin/actions/runners
|
||||
|
||||
Prüfe:
|
||||
- Status sollte "Idle" oder "Running" sein
|
||||
- Letzte Aktivität sollte kürzlich sein
|
||||
- Keine Fehler-Meldungen
|
||||
|
||||
### Häufige Fehler und Fixes
|
||||
|
||||
#### Problem: "Action not found"
|
||||
|
||||
**Fehler:** `Error: Action 'actions/checkout@v4' not found`
|
||||
|
||||
**Fix:**
|
||||
1. Prüfe `DEFAULT_ACTIONS_URL` in Gitea config
|
||||
2. Stelle sicher, dass Internet-Zugriff vom Runner vorhanden ist
|
||||
3. Gitea neu starten: `docker compose restart gitea`
|
||||
|
||||
#### Problem: "Timeout"
|
||||
|
||||
**Fehler:** `timeout: job exceeded maximum duration`
|
||||
|
||||
**Fix:**
|
||||
1. Erhöhe Timeout in `config.yaml`
|
||||
2. Oder teile Workflow in kleinere Jobs auf
|
||||
|
||||
#### Problem: "Docker build failed"
|
||||
|
||||
**Fehler:** Docker Build schlägt fehl
|
||||
|
||||
**Fix:**
|
||||
1. Prüfe `docker-dind` Container läuft
|
||||
2. Prüfe Registry-Zugriff
|
||||
3. Prüfe Registry-Credentials
|
||||
|
||||
#### Problem: "SSH connection failed"
|
||||
|
||||
**Fehler:** Ansible Deployment kann nicht zum Server verbinden
|
||||
|
||||
**Fix:**
|
||||
1. Prüfe `SSH_PRIVATE_KEY` Secret ist korrekt
|
||||
2. Prüfe SSH-Key hat richtige Berechtigungen
|
||||
3. Prüfe Firewall erlaubt Verbindung
|
||||
|
||||
### Workflow optimieren
|
||||
|
||||
#### Reduziere Workflow-Zeit
|
||||
|
||||
1. **Cache verwenden:**
|
||||
```yaml
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: vendor
|
||||
key: composer-${{ hashFiles('composer.lock') }}
|
||||
```
|
||||
|
||||
2. **Parallel Jobs:**
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
# ...
|
||||
build:
|
||||
# ...
|
||||
# Beide können parallel laufen
|
||||
```
|
||||
|
||||
3. **Conditional Steps:**
|
||||
```yaml
|
||||
- name: Skip on docs change
|
||||
if: contains(github.event.head_commit.message, '[skip ci]')
|
||||
run: exit 0
|
||||
```
|
||||
|
||||
### Nächste Schritte
|
||||
|
||||
1. **Identifiziere genaue Abbruch-Stelle:**
|
||||
- In welchem Step bricht es ab?
|
||||
- Welche Fehlermeldung erscheint?
|
||||
|
||||
2. **Prüfe Logs:**
|
||||
- Workflow-Logs in Gitea UI
|
||||
- Runner-Logs: `docker compose logs gitea-runner`
|
||||
|
||||
3. **Teste einzelne Steps:**
|
||||
- Führe Steps manuell aus
|
||||
- Isoliere das Problem
|
||||
|
||||
4. **Workflow vereinfachen:**
|
||||
- Reduziere auf minimalen Test-Workflow
|
||||
- Füge Steps schrittweise hinzu
|
||||
@@ -12,7 +12,10 @@ deployment/ansible/
|
||||
├── playbooks/
|
||||
│ ├── setup-production-secrets.yml # Deploy secrets
|
||||
│ ├── deploy-update.yml # Deploy application updates
|
||||
│ └── rollback.yml # Rollback deployments
|
||||
│ ├── rollback.yml # Rollback deployments
|
||||
│ ├── setup-wireguard.yml # Setup WireGuard VPN server
|
||||
│ ├── add-wireguard-client.yml # Add WireGuard client
|
||||
│ └── README-WIREGUARD.md # WireGuard documentation
|
||||
├── secrets/
|
||||
│ ├── .gitignore # Prevent committing secrets
|
||||
│ └── production.vault.yml.example # Example vault file
|
||||
@@ -112,6 +115,23 @@ ansible-playbook playbooks/rollback.yml \
|
||||
-e "rollback_to_version=2025-01-28T15-30-00"
|
||||
```
|
||||
|
||||
### Setup WireGuard VPN
|
||||
|
||||
**First-time setup** - Install WireGuard VPN server:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
|
||||
```
|
||||
|
||||
**Add a client**:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
|
||||
-e "client_name=myclient"
|
||||
```
|
||||
|
||||
Siehe [playbooks/README-WIREGUARD.md](playbooks/README-WIREGUARD.md) für detaillierte Anleitung.
|
||||
|
||||
## Ansible Vault Operations
|
||||
|
||||
### View Encrypted File
|
||||
|
||||
284
deployment/ansible/playbooks/README-WIREGUARD.md
Normal file
284
deployment/ansible/playbooks/README-WIREGUARD.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# WireGuard VPN Setup
|
||||
|
||||
WireGuard VPN-Server Installation und Konfiguration via Ansible.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Ansible Setup installiert und konfiguriert einen WireGuard VPN-Server auf dem Production-Server, um sicheren Zugriff auf interne Services zu ermöglichen.
|
||||
|
||||
## Playbooks
|
||||
|
||||
### 1. setup-wireguard.yml
|
||||
|
||||
Installiert und konfiguriert den WireGuard VPN-Server.
|
||||
|
||||
**Features:**
|
||||
- Installiert WireGuard und Tools
|
||||
- Generiert Server-Keys (falls nicht vorhanden)
|
||||
- Konfiguriert WireGuard-Server
|
||||
- Aktiviert IP Forwarding
|
||||
- Konfiguriert NAT (Masquerading)
|
||||
- Öffnet Firewall-Port (51820/udp)
|
||||
- Startet WireGuard-Service
|
||||
|
||||
**Verwendung:**
|
||||
```bash
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml
|
||||
```
|
||||
|
||||
**Variablen:**
|
||||
- `wireguard_port`: Port für WireGuard (Standard: 51820)
|
||||
- `wireguard_network`: VPN-Netzwerk (Standard: 10.8.0.0/24)
|
||||
- `wireguard_server_ip`: Server-IP im VPN (Standard: 10.8.0.1)
|
||||
|
||||
**Beispiel mit Custom-Parametern:**
|
||||
```bash
|
||||
ansible-playbook -i inventory/production.yml playbooks/setup-wireguard.yml \
|
||||
-e "wireguard_port=51820" \
|
||||
-e "wireguard_network=10.8.0.0/24" \
|
||||
-e "wireguard_server_ip=10.8.0.1"
|
||||
```
|
||||
|
||||
### 2. add-wireguard-client.yml
|
||||
|
||||
Fügt einen neuen Client zum WireGuard-Server hinzu.
|
||||
|
||||
**Features:**
|
||||
- Generiert Client-Keys
|
||||
- Fügt Client zur Server-Config hinzu
|
||||
- Erstellt Client-Konfigurationsdatei
|
||||
- Generiert QR-Code (falls qrencode installiert)
|
||||
- Restartet WireGuard-Service
|
||||
|
||||
**Verwendung:**
|
||||
```bash
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
|
||||
-e "client_name=myclient"
|
||||
```
|
||||
|
||||
**Optionale Parameter:**
|
||||
- `client_ip`: Spezifische Client-IP (Standard: automatisch berechnet)
|
||||
- `allowed_ips`: Erlaubte IP-Ranges (Standard: gesamtes VPN-Netzwerk)
|
||||
|
||||
**Beispiel mit spezifischer IP:**
|
||||
```bash
|
||||
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
|
||||
-e "client_name=myclient" \
|
||||
-e "client_ip=10.8.0.2"
|
||||
```
|
||||
|
||||
## Wichtige Sicherheitshinweise
|
||||
|
||||
### SSH-Zugriff bleibt verfügbar
|
||||
|
||||
**WICHTIG**: Die WireGuard-Konfiguration ändert NICHT die SSH-Zugriffsmöglichkeiten:
|
||||
|
||||
- ✅ SSH über die normale Server-IP bleibt vollständig funktionsfähig
|
||||
- ✅ WireGuard routet standardmäßig nur das VPN-Netzwerk (10.8.0.0/24)
|
||||
- ✅ Normale Internet-Routen werden nicht geändert
|
||||
- ✅ Firewall-Regeln für SSH (Port 22) werden NICHT entfernt oder blockiert
|
||||
|
||||
Die Client-Konfiguration verwendet standardmäßig `AllowedIPs = 10.8.0.0/24`, was bedeutet, dass nur Traffic für das VPN-Netzwerk über WireGuard geroutet wird. Alle anderen Verbindungen (inkl. SSH) nutzen weiterhin die normale Internet-Verbindung.
|
||||
|
||||
**Um SSH komplett über VPN zu routen** (nicht empfohlen für die erste Installation):
|
||||
```bash
|
||||
ansible-playbook ... -e "allowed_ips=0.0.0.0/0"
|
||||
```
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
Nach der Installation:
|
||||
|
||||
```
|
||||
/etc/wireguard/
|
||||
├── wg0.conf # Server-Konfiguration
|
||||
├── wg0_private.key # Server-Private-Key (600)
|
||||
├── wg0_public.key # Server-Public-Key (644)
|
||||
└── clients/ # Client-Konfigurationen
|
||||
├── client1.conf # Client 1 Config
|
||||
└── client2.conf # Client 2 Config
|
||||
```
|
||||
|
||||
## Client-Konfiguration verwenden
|
||||
|
||||
### 1. Config-Datei auf Client kopieren
|
||||
|
||||
```bash
|
||||
# Von Ansible Control Machine
|
||||
scp -i ~/.ssh/production \
|
||||
deploy@94.16.110.151:/etc/wireguard/clients/myclient.conf \
|
||||
~/myclient.conf
|
||||
```
|
||||
|
||||
### 2. WireGuard auf Client installieren
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt install wireguard wireguard-tools # Ubuntu/Debian
|
||||
# oder
|
||||
sudo yum install wireguard-tools # CentOS/RHEL
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install wireguard-tools
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
Download von https://www.wireguard.com/install/
|
||||
|
||||
### 3. VPN verbinden
|
||||
|
||||
**Linux/macOS:**
|
||||
```bash
|
||||
sudo wg-quick up ~/myclient.conf
|
||||
# oder
|
||||
sudo wg-quick up myclient
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
Importiere die `.conf`-Datei in die WireGuard-App.
|
||||
|
||||
### 4. Verbindung testen
|
||||
|
||||
```bash
|
||||
# Ping zum Server
|
||||
ping 10.8.0.1
|
||||
|
||||
# Status prüfen
|
||||
sudo wg show
|
||||
|
||||
# VPN trennen
|
||||
sudo wg-quick down myclient
|
||||
```
|
||||
|
||||
## QR-Code für Mobile Client
|
||||
|
||||
Falls `qrencode` installiert ist, wird beim Hinzufügen eines Clients automatisch ein QR-Code angezeigt:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml \
|
||||
-e "client_name=myphone"
|
||||
```
|
||||
|
||||
Der QR-Code kann mit der WireGuard Mobile App (iOS/Android) gescannt werden.
|
||||
|
||||
## Firewall-Konfiguration
|
||||
|
||||
Das Playbook öffnet automatisch den WireGuard-Port (51820/udp) in UFW, falls installiert.
|
||||
|
||||
**Manuelle Firewall-Regeln:**
|
||||
|
||||
```bash
|
||||
# UFW
|
||||
sudo ufw allow 51820/udp comment 'WireGuard VPN'
|
||||
|
||||
# iptables direkt
|
||||
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WireGuard startet nicht
|
||||
|
||||
```bash
|
||||
# Status prüfen
|
||||
sudo systemctl status wg-quick@wg0
|
||||
|
||||
# Logs anzeigen
|
||||
sudo journalctl -u wg-quick@wg0 -f
|
||||
|
||||
# Manuell starten
|
||||
sudo wg-quick up wg0
|
||||
```
|
||||
|
||||
### Client kann nicht verbinden
|
||||
|
||||
1. **Firewall prüfen:**
|
||||
```bash
|
||||
sudo ufw status
|
||||
sudo iptables -L -n | grep 51820
|
||||
```
|
||||
|
||||
2. **Server-Logs prüfen:**
|
||||
```bash
|
||||
sudo journalctl -u wg-quick@wg0 -f
|
||||
```
|
||||
|
||||
3. **Server-Status prüfen:**
|
||||
```bash
|
||||
sudo wg show
|
||||
```
|
||||
|
||||
4. **Routing prüfen:**
|
||||
```bash
|
||||
sudo ip route show
|
||||
```
|
||||
|
||||
### IP Forwarding nicht aktiv
|
||||
|
||||
```bash
|
||||
# Manuell aktivieren
|
||||
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
# Permanent machen
|
||||
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
## Client entfernen
|
||||
|
||||
Um einen Client zu entfernen:
|
||||
|
||||
```bash
|
||||
# Auf dem Server
|
||||
sudo nano /etc/wireguard/wg0.conf
|
||||
# Entferne den [Peer] Block für den Client
|
||||
|
||||
sudo wg-quick down wg0
|
||||
sudo wg-quick up wg0
|
||||
|
||||
# Optional: Client-Config löschen
|
||||
sudo rm /etc/wireguard/clients/clientname.conf
|
||||
```
|
||||
|
||||
## Server-Public-Key abrufen
|
||||
|
||||
```bash
|
||||
# Auf dem Server
|
||||
cat /etc/wireguard/wg0_public.key
|
||||
# oder
|
||||
sudo cat /etc/wireguard/wg0_private.key | wg pubkey
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Backup der Keys**: Speichere Server-Keys sicher:
|
||||
```bash
|
||||
sudo tar czf wireguard-backup.tar.gz /etc/wireguard/
|
||||
```
|
||||
|
||||
2. **Regelmäßige Updates:**
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade wireguard wireguard-tools
|
||||
```
|
||||
|
||||
3. **Monitoring**: Überwache VPN-Verbindungen:
|
||||
```bash
|
||||
sudo wg show
|
||||
```
|
||||
|
||||
4. **Sicherheit**:
|
||||
- Verwalte Client-Keys sicher
|
||||
- Entferne nicht genutzte Clients
|
||||
- Nutze starke Passwörter für Server-Zugriff
|
||||
|
||||
## Support
|
||||
|
||||
Bei Problemen:
|
||||
1. Prüfe Logs: `sudo journalctl -u wg-quick@wg0`
|
||||
2. Prüfe Status: `sudo wg show`
|
||||
3. Prüfe Firewall: `sudo ufw status`
|
||||
4. Teste Connectivity: `ping 10.8.0.1` (vom Client)
|
||||
169
deployment/ansible/playbooks/add-wireguard-client.yml
Executable file
169
deployment/ansible/playbooks/add-wireguard-client.yml
Executable file
@@ -0,0 +1,169 @@
|
||||
---
|
||||
- name: Add WireGuard Client
|
||||
hosts: production
|
||||
become: yes
|
||||
gather_facts: yes
|
||||
|
||||
vars:
|
||||
wireguard_interface: "wg0"
|
||||
wireguard_config_path: "/etc/wireguard"
|
||||
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
|
||||
wireguard_client_configs_path: "/etc/wireguard/clients"
|
||||
|
||||
pre_tasks:
|
||||
- name: Set WireGuard network
|
||||
set_fact:
|
||||
wireguard_network: "{{ wireguard_network | default('10.8.0.0/24') }}"
|
||||
|
||||
- name: Set WireGuard other variables with defaults
|
||||
set_fact:
|
||||
wireguard_port: "{{ wireguard_port | default(51820) }}"
|
||||
client_ip: "{{ client_ip | default('') }}"
|
||||
# IMPORTANT: Default to VPN network only (not 0.0.0.0/0)
|
||||
# This ensures SSH access via normal IP remains available
|
||||
allowed_ips: "{{ allowed_ips | default(wireguard_network) }}"
|
||||
|
||||
tasks:
|
||||
- name: Validate client name
|
||||
fail:
|
||||
msg: "client_name is required. Usage: ansible-playbook ... -e 'client_name=myclient'"
|
||||
when: client_name is not defined or client_name == ""
|
||||
|
||||
- name: Get server external IP address
|
||||
uri:
|
||||
url: https://api.ipify.org
|
||||
return_content: yes
|
||||
register: server_external_ip
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Set server external IP
|
||||
set_fact:
|
||||
server_external_ip_content: "{{ ansible_host | default(server_external_ip.content | default('')) }}"
|
||||
|
||||
- name: Check if WireGuard config exists
|
||||
stat:
|
||||
path: "{{ wireguard_config_file }}"
|
||||
register: wireguard_config_exists
|
||||
|
||||
- name: Fail if WireGuard not configured
|
||||
fail:
|
||||
msg: "WireGuard server not configured. Please run setup-wireguard.yml first."
|
||||
when: not wireguard_config_exists.stat.exists
|
||||
|
||||
- name: Read WireGuard server config
|
||||
slurp:
|
||||
src: "{{ wireguard_config_file }}"
|
||||
register: wireguard_server_config_read
|
||||
|
||||
- name: Extract server IP from config
|
||||
set_fact:
|
||||
server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)', '\\1')) | first | default('10.8.0.1') }}"
|
||||
|
||||
- name: Count existing clients in config
|
||||
set_fact:
|
||||
existing_clients_count: "{{ (wireguard_server_config_read.content | b64decode | regex_findall('\\[Peer\\]') | length) }}"
|
||||
when: client_ip == ""
|
||||
|
||||
- name: Calculate client IP if not provided
|
||||
set_fact:
|
||||
client_ip: "{{ server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)\\d+$', '\\1') }}{{ (server_vpn_ip | regex_replace('^(\\d+\\.\\d+\\.\\d+\\.)(\\d+)', '\\2') | int) + (existing_clients_count | default(1)) | int }}"
|
||||
when: client_ip == ""
|
||||
|
||||
- name: Generate client private key
|
||||
command: "wg genkey"
|
||||
register: client_private_key
|
||||
changed_when: true
|
||||
no_log: yes
|
||||
|
||||
- name: Generate client public key
|
||||
command: "wg pubkey"
|
||||
args:
|
||||
stdin: "{{ client_private_key.stdout }}"
|
||||
register: client_public_key
|
||||
changed_when: false
|
||||
no_log: yes
|
||||
|
||||
- name: Check if client already exists in config
|
||||
shell: "grep -q '{{ client_name }}' {{ wireguard_config_file }} || echo 'not found'"
|
||||
register: client_exists_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Add client to WireGuard server config
|
||||
blockinfile:
|
||||
path: "{{ wireguard_config_file }}"
|
||||
block: |
|
||||
# Client: {{ client_name }}
|
||||
[Peer]
|
||||
PublicKey = {{ client_public_key.stdout }}
|
||||
AllowedIPs = {{ client_ip }}/32
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK - Client: {{ client_name }}"
|
||||
when: "'not found' in client_exists_check.stdout"
|
||||
|
||||
- name: Ensure client configs directory exists
|
||||
file:
|
||||
path: "{{ wireguard_client_configs_path }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Get server public key
|
||||
shell: "cat {{ wireguard_config_path }}/{{ wireguard_interface }}_private.key | wg pubkey"
|
||||
register: server_public_key_cmd
|
||||
changed_when: false
|
||||
no_log: yes
|
||||
failed_when: false
|
||||
|
||||
- name: Create client configuration file
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/wireguard-client.conf.j2"
|
||||
dest: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Read WireGuard server config to find server IP
|
||||
slurp:
|
||||
src: "{{ wireguard_config_file }}"
|
||||
register: wireguard_server_config_read
|
||||
|
||||
- name: Restart WireGuard service
|
||||
systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
state: restarted
|
||||
when: "'not found' in client_exists_check.stdout"
|
||||
|
||||
- name: Display client configuration
|
||||
debug:
|
||||
msg: |
|
||||
========================================
|
||||
WireGuard Client Added: {{ client_name }}
|
||||
========================================
|
||||
|
||||
Client Configuration File:
|
||||
{{ wireguard_client_configs_path }}/{{ client_name }}.conf
|
||||
|
||||
Client IP: {{ client_ip }}
|
||||
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
|
||||
|
||||
To use this configuration:
|
||||
1. Copy the config file to your client machine
|
||||
2. Install WireGuard client
|
||||
3. Run: sudo wg-quick up {{ client_name }}
|
||||
|
||||
Or scan the QR code (if qrencode installed):
|
||||
qrencode -t ansiutf8 < {{ wireguard_client_configs_path }}/{{ client_name }}.conf
|
||||
========================================
|
||||
|
||||
- name: Generate QR code for client config
|
||||
command: "qrencode -t ansiutf8 -r {{ wireguard_client_configs_path }}/{{ client_name }}.conf"
|
||||
register: qr_code
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display QR code
|
||||
debug:
|
||||
msg: "{{ qr_code.stdout }}"
|
||||
when: qr_code.rc == 0
|
||||
@@ -118,7 +118,7 @@
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
?? - name: Get deployed image information
|
||||
- name: Get deployed image information
|
||||
shell: |
|
||||
docker compose -f {{ app_stack_path }}/docker-compose.yml config | grep -E "^\s+image:" | head -1 | awk '{print $2}' || echo "unknown"
|
||||
args:
|
||||
|
||||
@@ -241,6 +241,138 @@
|
||||
debug:
|
||||
msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}"
|
||||
|
||||
# 6. Deploy Application Stack
|
||||
- name: Optionally load application secrets from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Check if PostgreSQL .env exists
|
||||
stat:
|
||||
path: "{{ stacks_base_path }}/postgresql/.env"
|
||||
register: postgres_env_file
|
||||
changed_when: false
|
||||
|
||||
- name: Extract PostgreSQL password from .env file
|
||||
shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''"
|
||||
register: postgres_password_from_file
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: postgres_env_file.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Set application database password (from file, vault, or generate)
|
||||
set_fact:
|
||||
app_db_password: "{{ postgres_password_from_file.stdout if (postgres_env_file.stat.exists and postgres_password_from_file.stdout != '') else (vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Set application redis password from vault or generate
|
||||
set_fact:
|
||||
app_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
|
||||
|
||||
- name: Ensure application stack directory exists
|
||||
file:
|
||||
path: "{{ stacks_base_path }}/application"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create application stack .env file
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/application.env.j2"
|
||||
dest: "{{ stacks_base_path }}/application/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
vars:
|
||||
db_password: "{{ app_db_password }}"
|
||||
db_user: "{{ db_user | default('postgres') }}"
|
||||
db_name: "{{ db_name | default('michaelschiemer') }}"
|
||||
redis_password: "{{ app_redis_password }}"
|
||||
app_domain: "{{ app_domain | default('michaelschiemer.de') }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Deploy Application stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/application"
|
||||
state: present
|
||||
pull: always
|
||||
register: application_output
|
||||
|
||||
- name: Wait for Application to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: application_output.changed
|
||||
|
||||
- name: Wait for application containers to be healthy
|
||||
pause:
|
||||
seconds: 30
|
||||
when: application_output.changed
|
||||
|
||||
- name: Check application container health status
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "" and .Health != "starting") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: app_health_status
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display application health status
|
||||
debug:
|
||||
msg: "Application health: {{ app_health_status.stdout if app_health_status.stdout != '' else 'All services healthy or starting' }}"
|
||||
|
||||
- name: Wait for app container to be ready before migration
|
||||
wait_for:
|
||||
timeout: 60
|
||||
when: application_output.changed
|
||||
|
||||
- name: Check if app container is running
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps app | grep -q "Up" || exit 1
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: app_container_running
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: application_output.changed
|
||||
|
||||
- name: Run database migrations
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml exec -T app php console.php db:migrate
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: migration_result
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
ignore_errors: yes
|
||||
when: application_output.changed and app_container_running.rc == 0
|
||||
|
||||
- name: Display migration result
|
||||
debug:
|
||||
msg: |
|
||||
Migration Result:
|
||||
{{ migration_result.stdout if migration_result.rc == 0 else 'Migration may have failed - check logs with: docker compose -f ' + stacks_base_path + '/application/docker-compose.yml logs app' }}
|
||||
when: application_output.changed
|
||||
|
||||
- name: Verify application accessibility via HTTPS
|
||||
uri:
|
||||
url: "https://{{ app_domain | default('michaelschiemer.de') }}/health"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
status_code: [200, 404, 502, 503]
|
||||
timeout: 10
|
||||
register: app_health_check
|
||||
ignore_errors: yes
|
||||
when: application_output.changed
|
||||
|
||||
- name: Display application accessibility status
|
||||
debug:
|
||||
msg: "Application health check: {{ 'SUCCESS (HTTP ' + (app_health_check.status|string) + ')' if app_health_check.status == 200 else 'FAILED or not ready yet (HTTP ' + (app_health_check.status|string) + ')' }}"
|
||||
when: application_output.changed
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
@@ -250,9 +382,11 @@
|
||||
- "Docker Registry: {{ 'Deployed' if registry_output.changed else 'Already running' }}"
|
||||
- "Gitea: {{ 'Deployed' if gitea_output.changed else 'Already running' }}"
|
||||
- "Monitoring: {{ 'Deployed' if monitoring_output.changed else 'Already running' }}"
|
||||
- "Application: {{ 'Deployed' if application_output.changed else 'Already running' }}"
|
||||
- ""
|
||||
- "Next Steps:"
|
||||
- "1. Access Gitea at: https://git.michaelschiemer.de"
|
||||
- "2. Complete Gitea setup wizard if first-time deployment"
|
||||
- "3. Navigate to Admin > Actions > Runners to get registration token"
|
||||
- "4. Continue with Phase 1 - Gitea Runner Setup"
|
||||
- "5. Access Application at: https://{{ app_domain | default('michaelschiemer.de') }}"
|
||||
|
||||
294
deployment/ansible/playbooks/setup-wireguard.yml
Executable file
294
deployment/ansible/playbooks/setup-wireguard.yml
Executable file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
- name: Setup WireGuard VPN Server
|
||||
hosts: production
|
||||
become: yes
|
||||
gather_facts: yes
|
||||
|
||||
vars:
|
||||
wireguard_interface: "wg0"
|
||||
wireguard_config_path: "/etc/wireguard"
|
||||
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
|
||||
wireguard_private_key_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}_private.key"
|
||||
wireguard_public_key_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}_public.key"
|
||||
wireguard_client_configs_path: "{{ wireguard_config_path }}/clients"
|
||||
wireguard_enable_ip_forwarding: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Set WireGuard variables with defaults
|
||||
set_fact:
|
||||
wireguard_port: "{{ wireguard_port | default(51820) }}"
|
||||
wireguard_network: "{{ wireguard_network | default('10.8.0.0/24') }}"
|
||||
wireguard_server_ip: "{{ wireguard_server_ip | default('10.8.0.1') }}"
|
||||
|
||||
- name: Optionally load wireguard secrets from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Check if WireGuard is already installed
|
||||
command: which wg
|
||||
register: wireguard_installed
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Update package cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600
|
||||
when: not wireguard_installed.rc == 0
|
||||
|
||||
- name: Install WireGuard
|
||||
apt:
|
||||
name:
|
||||
- wireguard
|
||||
- wireguard-tools
|
||||
- qrencode
|
||||
state: present
|
||||
when: not wireguard_installed.rc == 0
|
||||
notify: restart wireguard
|
||||
|
||||
- name: Ensure WireGuard config directory exists
|
||||
file:
|
||||
path: "{{ wireguard_config_path }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Ensure WireGuard client configs directory exists
|
||||
file:
|
||||
path: "{{ wireguard_client_configs_path }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Check if WireGuard server keys exist
|
||||
stat:
|
||||
path: "{{ wireguard_private_key_file }}"
|
||||
register: server_private_key_exists
|
||||
|
||||
- name: Generate WireGuard server private key
|
||||
command: "wg genkey"
|
||||
register: server_private_key
|
||||
changed_when: true
|
||||
when: not server_private_key_exists.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Save WireGuard server private key
|
||||
copy:
|
||||
content: "{{ server_private_key.stdout }}"
|
||||
dest: "{{ wireguard_private_key_file }}"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
when: not server_private_key_exists.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Read WireGuard server private key
|
||||
slurp:
|
||||
src: "{{ wireguard_private_key_file }}"
|
||||
register: server_private_key_content
|
||||
when: server_private_key_exists.stat.exists
|
||||
|
||||
- name: Generate WireGuard server public key
|
||||
command: "wg pubkey"
|
||||
args:
|
||||
stdin: "{{ server_private_key.stdout if not server_private_key_exists.stat.exists else server_private_key_content.content | b64decode | trim }}"
|
||||
register: server_public_key
|
||||
changed_when: false
|
||||
when: not server_private_key_exists.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Get existing server public key
|
||||
shell: "cat {{ wireguard_private_key_file }} | wg pubkey"
|
||||
register: existing_server_public_key
|
||||
changed_when: false
|
||||
when: server_private_key_exists.stat.exists
|
||||
no_log: yes
|
||||
failed_when: false
|
||||
|
||||
- name: Set server public key fact
|
||||
set_fact:
|
||||
server_public_key_value: "{{ server_public_key.stdout if not server_private_key_exists.stat.exists else existing_server_public_key.stdout }}"
|
||||
|
||||
- name: Save WireGuard server public key
|
||||
copy:
|
||||
content: "{{ server_public_key_value }}"
|
||||
dest: "{{ wireguard_public_key_file }}"
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Enable IP forwarding
|
||||
sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
state: present
|
||||
sysctl_set: yes
|
||||
reload: yes
|
||||
when: wireguard_enable_ip_forwarding
|
||||
|
||||
- name: Make IP forwarding persistent
|
||||
lineinfile:
|
||||
path: /etc/sysctl.conf
|
||||
regexp: '^net\.ipv4\.ip_forward'
|
||||
line: 'net.ipv4.ip_forward=1'
|
||||
state: present
|
||||
when: wireguard_enable_ip_forwarding
|
||||
|
||||
- name: Get server external IP address
|
||||
uri:
|
||||
url: https://api.ipify.org
|
||||
return_content: yes
|
||||
register: server_external_ip
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Set server external IP from inventory if API fails
|
||||
set_fact:
|
||||
server_external_ip_content: "{{ ansible_host | default(server_external_ip.content | default('')) }}"
|
||||
when: server_external_ip.content is defined
|
||||
|
||||
- name: Get server external IP from ansible_host
|
||||
set_fact:
|
||||
server_external_ip_content: "{{ ansible_host }}"
|
||||
when: server_external_ip.content is not defined
|
||||
|
||||
- name: Read server private key for config
|
||||
slurp:
|
||||
src: "{{ wireguard_private_key_file }}"
|
||||
register: server_private_key_file_content
|
||||
when: server_private_key_exists.stat.exists
|
||||
|
||||
- name: Set server private key for template (new key)
|
||||
set_fact:
|
||||
server_private_key_for_config: "{{ server_private_key.stdout }}"
|
||||
when: not server_private_key_exists.stat.exists
|
||||
|
||||
- name: Set server private key for template (existing key)
|
||||
set_fact:
|
||||
server_private_key_for_config: "{{ server_private_key_file_content.content | b64decode | trim }}"
|
||||
when: server_private_key_exists.stat.exists
|
||||
|
||||
- name: Get network interface name
|
||||
shell: "ip route | grep default | awk '{print $5}' | head -1"
|
||||
register: default_interface
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Set default interface
|
||||
set_fact:
|
||||
wireguard_interface_name: "{{ default_interface.stdout | default('eth0') }}"
|
||||
|
||||
- name: Check if WireGuard config exists
|
||||
stat:
|
||||
path: "{{ wireguard_config_file }}"
|
||||
register: wireguard_config_exists
|
||||
|
||||
- name: Create WireGuard server configuration
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/wireguard-server.conf.j2"
|
||||
dest: "{{ wireguard_config_file }}"
|
||||
mode: '0600'
|
||||
owner: root
|
||||
group: root
|
||||
notify: restart wireguard
|
||||
|
||||
- name: Check if WireGuard service is enabled
|
||||
systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
register: wireguard_service_status
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Enable WireGuard service
|
||||
systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
when: not wireguard_service_status.status.ActiveState is defined or wireguard_service_status.status.ActiveState != 'active'
|
||||
|
||||
- name: Start WireGuard service
|
||||
systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
state: started
|
||||
notify: restart wireguard
|
||||
|
||||
- name: Check if UFW firewall is installed
|
||||
command: which ufw
|
||||
register: ufw_installed
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Verify SSH access is allowed in UFW
|
||||
command: "ufw status | grep -q '22/tcp' || echo 'SSH not found'"
|
||||
register: ssh_ufw_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: ufw_installed.rc == 0
|
||||
|
||||
- name: Warn if SSH is not explicitly allowed
|
||||
debug:
|
||||
msg: |
|
||||
?? WARNING: SSH (port 22) might not be explicitly allowed in UFW!
|
||||
Please ensure SSH access is configured before proceeding.
|
||||
Run: sudo ufw allow 22/tcp
|
||||
when: ufw_installed.rc == 0 and 'SSH not found' in ssh_ufw_check.stdout
|
||||
|
||||
- name: Allow WireGuard port in UFW firewall
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ wireguard_port }}"
|
||||
proto: udp
|
||||
comment: "WireGuard VPN"
|
||||
when: ufw_installed.rc == 0
|
||||
|
||||
- name: Allow WireGuard port in UFW firewall (alternative)
|
||||
shell: "ufw allow {{ wireguard_port }}/udp comment 'WireGuard VPN'"
|
||||
when: ufw_installed.rc == 0
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Check WireGuard status
|
||||
command: "wg show {{ wireguard_interface }}"
|
||||
register: wireguard_status
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display WireGuard status
|
||||
debug:
|
||||
msg: |
|
||||
WireGuard Status:
|
||||
{{ wireguard_status.stdout if wireguard_status.rc == 0 else 'WireGuard interface not active' }}
|
||||
|
||||
- name: Display server public key
|
||||
debug:
|
||||
msg: |
|
||||
========================================
|
||||
WireGuard Server Setup Complete!
|
||||
========================================
|
||||
|
||||
Server Public Key:
|
||||
{{ server_public_key_value }}
|
||||
|
||||
Server IP: {{ wireguard_server_ip }}
|
||||
Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }}
|
||||
Network: {{ wireguard_network }}
|
||||
|
||||
To add a client, run:
|
||||
ansible-playbook -i inventory/production.yml playbooks/add-wireguard-client.yml -e "client_name=myclient"
|
||||
|
||||
Client configs are stored in:
|
||||
{{ wireguard_client_configs_path }}/
|
||||
========================================
|
||||
|
||||
handlers:
|
||||
- name: restart wireguard
|
||||
systemd:
|
||||
name: "wg-quick@{{ wireguard_interface }}"
|
||||
state: restarted
|
||||
@@ -7,7 +7,7 @@
|
||||
vault_db_password: "change-me-secure-db-password"
|
||||
vault_db_root_password: "change-me-secure-root-password"
|
||||
|
||||
# Redis Credentials
|
||||
# Application Stack Credentials
|
||||
vault_redis_password: "change-me-secure-redis-password"
|
||||
|
||||
# Application Secrets
|
||||
|
||||
40
deployment/ansible/templates/application.env.j2
Normal file
40
deployment/ansible/templates/application.env.j2
Normal file
@@ -0,0 +1,40 @@
|
||||
# Application Stack Environment Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
|
||||
# Timezone
|
||||
TZ={{ timezone | default('Europe/Berlin') }}
|
||||
|
||||
# Application Domain
|
||||
APP_DOMAIN={{ app_domain | default('michaelschiemer.de') }}
|
||||
|
||||
# Application Settings
|
||||
APP_ENV={{ app_env | default('production') }}
|
||||
APP_DEBUG={{ app_debug | default('false') }}
|
||||
APP_URL=https://{{ app_domain | default('michaelschiemer.de') }}
|
||||
|
||||
# Database Configuration
|
||||
# Using PostgreSQL from postgres stack
|
||||
DB_HOST=postgres
|
||||
DB_PORT={{ db_port | default('5432') }}
|
||||
DB_NAME={{ db_name | default('michaelschiemer') }}
|
||||
DB_USER={{ db_user | default('postgres') }}
|
||||
DB_PASS={{ db_password }}
|
||||
|
||||
# Redis Configuration
|
||||
# Redis runs in this stack
|
||||
REDIS_PASSWORD={{ redis_password }}
|
||||
|
||||
# Cache Configuration
|
||||
CACHE_DRIVER={{ cache_driver | default('redis') }}
|
||||
CACHE_PREFIX={{ cache_prefix | default('app') }}
|
||||
|
||||
# Session Configuration
|
||||
SESSION_DRIVER={{ session_driver | default('redis') }}
|
||||
SESSION_LIFETIME={{ session_lifetime | default('120') }}
|
||||
|
||||
# Queue Worker Configuration
|
||||
QUEUE_DRIVER={{ queue_driver | default('redis') }}
|
||||
QUEUE_CONNECTION={{ queue_connection | default('default') }}
|
||||
QUEUE_WORKER_SLEEP={{ queue_worker_sleep | default('3') }}
|
||||
QUEUE_WORKER_TRIES={{ queue_worker_tries | default('3') }}
|
||||
QUEUE_WORKER_TIMEOUT={{ queue_worker_timeout | default('60') }}
|
||||
@@ -17,4 +17,5 @@ GRAFANA_PLUGINS={{ grafana_plugins | default('') }}
|
||||
|
||||
# Prometheus BasicAuth
|
||||
# Format: username:hashed_password
|
||||
PROMETHEUS_AUTH={{ prometheus_auth }}
|
||||
# Note: Dollar signs are escaped for Docker Compose ($$ becomes $)
|
||||
PROMETHEUS_AUTH={{ prometheus_auth | replace('$', '$$') }}
|
||||
27
deployment/ansible/templates/wireguard-client.conf.j2
Normal file
27
deployment/ansible/templates/wireguard-client.conf.j2
Normal file
@@ -0,0 +1,27 @@
|
||||
# WireGuard Client Configuration for {{ client_name }}
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
|
||||
[Interface]
|
||||
# Client private key
|
||||
PrivateKey = {{ client_private_key.stdout }}
|
||||
|
||||
# Client IP address in VPN network
|
||||
Address = {{ client_ip }}/24
|
||||
|
||||
# DNS server (optional)
|
||||
DNS = 1.1.1.1, 8.8.8.8
|
||||
|
||||
[Peer]
|
||||
# Server public key
|
||||
PublicKey = {{ server_public_key_cmd.stdout }}
|
||||
|
||||
# Server endpoint
|
||||
Endpoint = {{ server_external_ip_content }}:{{ wireguard_port }}
|
||||
|
||||
# Allowed IPs (routes through VPN)
|
||||
# IMPORTANT: Only VPN network is routed through VPN by default
|
||||
# SSH access via normal IP ({{ server_external_ip_content }}) remains available
|
||||
AllowedIPs = {{ allowed_ips }}
|
||||
|
||||
# Keep connection alive
|
||||
PersistentKeepalive = 25
|
||||
22
deployment/ansible/templates/wireguard-server.conf.j2
Normal file
22
deployment/ansible/templates/wireguard-server.conf.j2
Normal file
@@ -0,0 +1,22 @@
|
||||
# WireGuard Server Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
|
||||
[Interface]
|
||||
# Server private key
|
||||
PrivateKey = {{ server_private_key_for_config }}
|
||||
|
||||
# Server IP address in VPN network
|
||||
Address = {{ wireguard_server_ip }}/24
|
||||
|
||||
# Port to listen on
|
||||
ListenPort = {{ wireguard_port }}
|
||||
|
||||
# Enable NAT for VPN clients to access internet
|
||||
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o {{ wireguard_interface_name }} -j MASQUERADE
|
||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o {{ wireguard_interface_name }} -j MASQUERADE
|
||||
|
||||
# Clients will be added here by the add-wireguard-client playbook
|
||||
# Example:
|
||||
# [Peer]
|
||||
# PublicKey = <client_public_key>
|
||||
# AllowedIPs = 10.8.0.2/32
|
||||
@@ -11,8 +11,8 @@ runner:
|
||||
# Maximum number of concurrent jobs
|
||||
capacity: 1
|
||||
|
||||
# Timeout for a single job
|
||||
timeout: 3h
|
||||
# Timeout for a single job (increased for long-running deployments)
|
||||
timeout: 6h
|
||||
|
||||
# Whether to enable debug mode (skip SSL verification for setup)
|
||||
insecure: true
|
||||
@@ -55,8 +55,8 @@ container:
|
||||
# Default image if not specified in workflow
|
||||
default_image: node:16-bullseye
|
||||
|
||||
# Docker host
|
||||
docker_host: ""
|
||||
# Docker host (use docker-dind without TLS since it's in isolated network)
|
||||
docker_host: tcp://docker-dind:2375
|
||||
|
||||
# Valid volume paths that can be mounted
|
||||
valid_volumes:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
gitea-runner:
|
||||
image: gitea/act_runner:latest
|
||||
@@ -25,14 +23,11 @@ services:
|
||||
container_name: gitea-runner-dind
|
||||
restart: unless-stopped
|
||||
privileged: true
|
||||
environment:
|
||||
- DOCKER_TLS_CERTDIR=/certs
|
||||
volumes:
|
||||
- docker-certs:/certs
|
||||
- docker-data:/var/lib/docker
|
||||
networks:
|
||||
- gitea-runner
|
||||
command: ["dockerd", "--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2376", "--tlsverify"]
|
||||
command: ["dockerd", "--host=unix:///var/run/docker.sock", "--host=tcp://0.0.0.0:2375"]
|
||||
|
||||
networks:
|
||||
gitea-runner:
|
||||
@@ -40,7 +35,5 @@ networks:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
docker-certs:
|
||||
name: gitea-runner-certs
|
||||
docker-data:
|
||||
name: gitea-runner-docker-data
|
||||
|
||||
@@ -13,9 +13,9 @@ APP_DEBUG=false
|
||||
APP_URL=https://michaelschiemer.de
|
||||
|
||||
# Database Configuration
|
||||
# Note: MySQL runs in Stack 5 (PostgreSQL) or external server
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
# Note: Using PostgreSQL from postgres stack
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=michaelschiemer
|
||||
DB_USER=appuser
|
||||
DB_PASS=<generate-with-openssl-rand-base64-32>
|
||||
|
||||
@@ -14,8 +14,8 @@ services:
|
||||
- APP_DEBUG=${APP_DEBUG:-false}
|
||||
- APP_URL=${APP_URL:-https://michaelschiemer.de}
|
||||
# Database
|
||||
- DB_HOST=${DB_HOST:-mysql}
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_NAME=${DB_NAME}
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASS=${DB_PASS}
|
||||
@@ -127,8 +127,8 @@ services:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- APP_DEBUG=${APP_DEBUG:-false}
|
||||
# Database
|
||||
- DB_HOST=${DB_HOST:-mysql}
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_NAME=${DB_NAME}
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASS=${DB_PASS}
|
||||
@@ -172,8 +172,8 @@ services:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- APP_DEBUG=${APP_DEBUG:-false}
|
||||
# Database
|
||||
- DB_HOST=${DB_HOST:-mysql}
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
- DB_HOST=${DB_HOST:-postgres}
|
||||
- DB_PORT=${DB_PORT:-5432}
|
||||
- DB_NAME=${DB_NAME}
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASS=${DB_PASS}
|
||||
|
||||
@@ -15,12 +15,6 @@ services:
|
||||
- "traefik.http.routers.portainer.tls=true"
|
||||
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/api/system/status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
|
||||
Reference in New Issue
Block a user