chore: complete update

This commit is contained in:
2025-07-17 16:38:55 +02:00
parent 64a7051137
commit ec5526e2b2
46 changed files with 3139 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
# .gitignore für Netcup Deployment
*.retry
.ansible/
*.log
.env.local
secrets.yml

View File

@@ -0,0 +1,136 @@
# Test Makefile für rsync debugging (Fixed)
.PHONY: test-rsync debug-sync upload restart quick-deploy
# Teste manuellen rsync
test-rsync:
@echo "🔍 Testing manual rsync..."
@SERVER_IP=$$(grep ansible_host inventory/hosts.yml | awk '{print $$2}'); \
echo "Server IP: $$SERVER_IP"; \
APP_PATH=$$(grep local_app_path inventory/hosts.yml | awk '{print $$2}' | tr -d '"'); \
echo "Local path: $$APP_PATH"; \
echo ""; \
echo "=== Testing dry-run rsync ==="; \
rsync -av --dry-run \
--exclude='ansible' \
--exclude='.git' \
--exclude='vendor' \
--exclude='node_modules' \
--exclude='storage/logs' \
--exclude='cache' \
--exclude='logs' \
--exclude='dist' \
--exclude='.archive' \
$$APP_PATH/ root@$$SERVER_IP:/opt/myapp/; \
echo ""; \
echo "If this shows files, then rsync should work"
# Debug was in lokalen Dateien ist
debug-local:
@echo "📁 Local files debug:"
@APP_PATH=$$(grep local_app_path inventory/hosts.yml | awk '{print $$2}' | tr -d '"'); \
echo "Path: $$APP_PATH"; \
echo ""; \
if [ -z "$$APP_PATH" ]; then \
echo "❌ APP_PATH is empty!"; \
echo "Raw line from hosts.yml:"; \
grep local_app_path inventory/hosts.yml; \
exit 1; \
fi; \
echo "=== Root files ==="; \
ls -la "$$APP_PATH" | head -10; \
echo ""; \
echo "=== Public files ==="; \
ls -la "$$APP_PATH/public" | head -10; \
echo ""; \
echo "=== Does index.php exist locally? ==="; \
if [ -f "$$APP_PATH/public/index.php" ]; then \
echo "✅ index.php exists locally"; \
echo "Size: $$(wc -c < $$APP_PATH/public/index.php) bytes"; \
echo "Content preview:"; \
head -5 "$$APP_PATH/public/index.php"; \
else \
echo "❌ index.php NOT found locally!"; \
echo "Checking if public folder exists:"; \
if [ -d "$$APP_PATH/public" ]; then \
echo "Public folder exists, contents:"; \
ls -la "$$APP_PATH/public/"; \
else \
echo "Public folder does not exist!"; \
fi; \
fi
# Test direkt mit absoluten Pfaden
debug-direct:
@echo "📁 Direct path test:"
@echo "=== Current directory ==="
pwd
@echo ""
@echo "=== Going to project root ==="
cd ../.. && pwd
@echo ""
@echo "=== Files in project root ==="
cd ../.. && ls -la | head -10
@echo ""
@echo "=== Public folder ==="
cd ../.. && ls -la public/ | head -10
@echo ""
@echo "=== Index.php check ==="
cd ../.. && if [ -f "public/index.php" ]; then \
echo "✅ index.php found!"; \
echo "Size: $$(wc -c < public/index.php) bytes"; \
else \
echo "❌ index.php not found"; \
fi
# Test Ansible synchronize mit debug
debug-sync:
@echo "🔍 Testing Ansible synchronize with debug..."
ansible-playbook -i inventory/hosts.yml debug-sync.yml -v
# Upload files only (no infrastructure setup)
upload:
@echo "📤 Uploading files only..."
ansible-playbook -i inventory/hosts.yml upload-only.yml
# Restart application after upload
restart:
@echo "🔄 Restarting application..."
ansible-playbook -i inventory/hosts.yml restart-app.yml
# Quick upload and restart
quick-deploy:
@echo "⚡ Quick deploy: upload + restart..."
ansible-playbook -i inventory/hosts.yml upload-only.yml
ansible-playbook -i inventory/hosts.yml restart-app.yml
# Alle Standard-Befehle
deploy:
@echo "🚀 Deploying project to Netcup..."
chmod +x deploy.sh
./deploy.sh
check:
@echo "🔍 Testing configuration..."
ansible all -m ping
logs:
@echo "📋 Showing container logs..."
ansible all -m shell -a "cd /opt/myapp && (docker compose logs --tail 100 || docker-compose logs --tail 100)"
help:
@echo "📖 Debug commands:"
@echo " make debug-local - Check local files"
@echo " make debug-direct - Check with direct paths"
@echo " make test-rsync - Test manual rsync"
@echo " make debug-sync - Test Ansible sync"
@echo ""
@echo "📖 Deploy commands:"
@echo " make deploy - Full deployment (infrastructure + app)"
@echo " make upload - Upload files only (no infrastructure)"
@echo " make restart - Restart application containers"
@echo " make quick-deploy - Upload files + restart (fastest)"
@echo ""
@echo "📖 Utility commands:"
@echo " make logs - Show container logs"
@echo " make check - Test connection"

View File

@@ -0,0 +1,128 @@
# Netcup Quick Setup Guide
## 1. Server vorbereiten
### Netcup VPS bestellen
- **Mindestens:** VPS 200 G8 (2 CPU, 4GB RAM)
- **OS:** Ubuntu 22.04 LTS
- **Netzwerk:** IPv4 + IPv6
### SSH-Key installieren
```bash
# SSH-Key generieren (falls noch nicht vorhanden)
ssh-keygen -t ed25519 -C "netcup-deploy"
# Key zum Server kopieren
ssh-copy-id root@DEINE-SERVER-IP
```
## 2. Konfiguration
### Basis-Einstellungen
```bash
# Server-Details eintragen
vim inventory/hosts.yml
```
**Wichtig ändern:**
- `ansible_host: 85.123.456.789` → deine Netcup IP
- `domain: "example.com"` → deine Domain
- `ssl_email: "admin@example.com"` → deine E-Mail
- `git_repo: "https://github.com/user/repo.git"` → dein Git Repository
### DNS konfigurieren
Stelle sicher dass deine Domain zur Netcup IP zeigt:
```bash
# A-Record setzen
example.com. IN A DEINE-NETCUP-IP
```
## 3. App-Anforderungen
Deine App braucht:
- **Dockerfile** im Repository-Root
- **Port 3000** (oder ändere `app_port` in hosts.yml)
- **Health-Check** Endpoint `/health` (oder ändere `health_check_url`)
### Beispiel Dockerfile (Node.js)
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```
### Beispiel Health-Check (Express.js)
```javascript
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
```
## 4. Deployment
```bash
# Einfach deployen
make deploy
# Oder manuell
./deploy.sh
```
## 5. Troubleshooting
### Server nicht erreichbar?
```bash
# Ping testen
ping DEINE-SERVER-IP
# SSH testen
ssh root@DEINE-SERVER-IP
# Firewall prüfen (auf dem Server)
ufw status
```
### SSL-Probleme?
```bash
# DNS prüfen
nslookup DEINE-DOMAIN
# Certbot manuell
ssh root@DEINE-SERVER-IP
certbot certificates
```
### App startet nicht?
```bash
# Logs anschauen
make logs
# Container status
ansible all -m shell -a "docker ps -a"
# Ins Container einsteigen
ansible all -m shell -a "docker exec -it CONTAINER_NAME sh"
```
## 6. Nach dem Deployment
-**App testen:** https://deine-domain.com
-**Health-Check:** https://deine-domain.com/health
-**SSL prüfen:** https://www.ssllabs.com/ssltest/
-**Performance:** https://pagespeed.web.dev/
## 7. Updates
```bash
# App updaten (git pull + rebuild)
make update
# Logs nach Update prüfen
make logs
```
Das war's! Deine App läuft jetzt auf Netcup mit SSL! 🎉

View File

@@ -0,0 +1,40 @@
# Netcup Simple Deploy
Ultra-einfaches Ansible-Setup für Netcup VPS Deployment.
## Quick Start
1. **Server-Info eintragen:**
```bash
vim inventory/hosts.yml
# Deine Netcup-Server IP und Domain eintragen
```
2. **App-Einstellungen:**
```bash
vim inventory/group_vars.yml
# Domain, Repo, etc. anpassen
```
3. **Deployen:**
```bash
ansible-playbook deploy.yml
```
## Was wird installiert
✅ Docker & Docker Compose
✅ Nginx Reverse Proxy
✅ SSL mit Let's Encrypt
✅ Deine App aus Git
✅ Automatische Updates
## Features
- 🚀 **Ein Kommando** deployment
- 🔒 **Automatisches SSL**
- 🐳 **Docker-basiert**
- 📱 **Health Checks**
- 🔄 **Zero-Downtime Updates**
Perfekt für einfache Web-Apps auf Netcup VPS!

View File

@@ -0,0 +1,161 @@
# Projekt Setup für Netcup (nutzt deine docker-compose.yml)
## Projektstruktur
Das Deployment nutzt deine bestehende Docker-Konfiguration:
```
dein-projekt/ # Hauptordner
├── ansible/ # Hier sind wir jetzt
│ └── netcup-simple-deploy/
├── docker-compose.yml # ← DEINE Compose-Datei (wird verwendet!)
├── docker/ # Docker-Konfiguration
│ ├── Dockerfile
│ └── docker-compose.yml # ← Alternative hier
├── src/ # PHP Framework/Library Dateien
├── public/ # Web-Root
└── ...
```
## Was das Deployment macht:
**Nutzt deine bestehende docker-compose.yml**
**Startet ALLE deine Services** (DB, Redis, etc.)
**Überträgt komplettes Projekt**
**Nginx als Reverse Proxy** für SSL
## Quick Setup
### 1. Konfiguration
```bash
cd ansible/netcup-simple-deploy
vim inventory/hosts.yml
```
**Wichtig ändern:**
```yaml
ansible_host: DEINE-NETCUP-IP
domain: "deine-domain.com"
app_port: 8080 # Port deiner App aus docker-compose.yml
```
### 2. Port prüfen
Schaue in deine `docker-compose.yml` welchen Port deine App exponiertrt:
```yaml
services:
myapp:
ports:
- "8080:80" # ← Dann ist app_port: 8080
```
### 3. Deployment
```bash
make deploy
```
## Beispiel docker-compose.yml Strukturen
### Einfache PHP App
```yaml
version: '3.8'
services:
web:
build: .
ports:
- "8080:80"
volumes:
- ./src:/var/www/src
- ./public:/var/www/html
```
### Mit Datenbank
```yaml
version: '3.8'
services:
web:
build: .
ports:
- "8080:80"
depends_on:
- db
environment:
- DATABASE_URL=mysql://user:pass@db:3306/myapp
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=myapp
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
```
### Mit Redis + Database
```yaml
version: '3.8'
services:
web:
build: .
ports:
- "8080:80"
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=secret
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
```
## Nach dem Deployment
**Alle Services verwalten:**
```bash
make services # Zeige alle Services
make logs-service # Logs für bestimmten Service
make status # Status aller Container
make shell # In Container einsteigen
```
**Updates:**
```bash
# Nach Änderungen an Code oder docker-compose.yml
make deploy
# Nur Container neu bauen
make rebuild
```
**Monitoring:**
```bash
make logs # Alle Logs
make tail-logs # Live logs
make show-env # Environment variables
```
## Vorteile dieser Lösung
**Deine bestehende Konfiguration** wird verwendet
**Alle Services** (DB, Redis, etc.) funktionieren
**Keine Code-Änderungen** nötig
**SSL-Termination** durch nginx
**Einfache Updates** mit make deploy
Das Deployment ist jetzt vollständig auf deine bestehende Docker-Infrastruktur ausgerichtet! 🎉

View File

@@ -0,0 +1,172 @@
# Netcup Setup ohne Git
## 1. App-Struktur vorbereiten
### Option A: Bestehende App
Falls du bereits eine App hast, stelle sicher dass sie diese Struktur hat:
```
deine-app/
├── package.json # Node.js Abhängigkeiten
├── server.js # Hauptdatei
├── Dockerfile # Docker-Konfiguration
└── ... weitere Dateien
```
### Option B: Neue App erstellen
```bash
# Erstelle App-Verzeichnis
mkdir -p ~/meine-app
# Beispiel Node.js App
cd ~/meine-app
# package.json
cat > package.json << 'EOF'
{
"name": "meine-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
EOF
# server.js
cat > server.js << 'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello World!',
timestamp: new Date().toISOString()
});
});
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on port ${port}`);
});
EOF
# Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF
```
## 2. Ansible konfigurieren
```bash
# Ins Deployment-Verzeichnis
cd ansible/netcup-simple-deploy
# Konfiguration anpassen
vim inventory/hosts.yml
```
**Wichtige Änderungen:**
```yaml
ansible_host: DEINE-NETCUP-IP # ← Server IP
domain: "deine-domain.com" # ← Domain
ssl_email: "deine@email.com" # ← E-Mail
local_app_path: "~/meine-app" # ← Pfad zu deiner App
```
## 3. Deployment
```bash
# SSH-Key zum Server (falls noch nicht gemacht)
ssh-copy-id root@DEINE-NETCUP-IP
# App deployen
make deploy
```
## 4. App updaten
Nach Änderungen an deiner App:
```bash
# Einfach erneut deployen
make deploy
```
Die Dateien werden automatisch zum Server übertragen und die App neu gebaut.
## 5. Verschiedene App-Typen
### PHP App
```dockerfile
FROM php:8.1-apache
COPY . /var/www/html/
EXPOSE 80
```
### Python Flask
```dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 3000
CMD ["python", "app.py"]
```
### Static HTML
```dockerfile
FROM nginx:alpine
COPY . /usr/share/nginx/html
EXPOSE 80
```
## 6. Ordnerstruktur
```
netcup-simple-deploy/
├── inventory/
│ └── hosts.yml # ← Hier konfigurieren
├── deploy.sh # ← Deployment starten
└── Makefile # ← Einfache Befehle
~/meine-app/ # ← Deine App-Dateien
├── Dockerfile
├── package.json
└── server.js
```
## 7. Troubleshooting
### App startet nicht?
```bash
# Logs anschauen
make logs
# Container status prüfen
ansible all -m shell -a "docker ps -a"
```
### Dateien werden nicht übertragen?
```bash
# Pfad prüfen
ls -la ~/meine-app
# Manuell testen
ansible all -m shell -a "ls -la /opt/myapp/src/"
```
Das war's! Keine Git-Kenntnisse nötig - einfach deine Dateien bearbeiten und deployen! 🎉

View File

@@ -0,0 +1,165 @@
# PHP Projekt Setup für Netcup
## Projektstruktur
Das Deployment erwartet diese Struktur in deinem Hauptprojekt:
```
dein-projekt/ # Hauptordner
├── ansible/ # Hier sind wir jetzt
│ └── netcup-simple-deploy/
├── docker/ # Docker-Konfiguration
│ ├── Dockerfile # (optional, wird sonst automatisch erstellt)
│ └── docker-compose.yml # (optional)
├── src/ # PHP Framework/Library Dateien
│ ├── classes/
│ ├── includes/
│ └── ...
├── public/ # Web-Root (öffentlich zugänglich)
│ ├── index.php # Haupteinstiegspunkt
│ ├── css/
│ ├── js/
│ ├── images/
│ └── ...
├── storage/ # (optional) Logs, Cache, etc.
├── cache/ # (optional) Cache-Dateien
├── logs/ # (optional) Log-Dateien
└── .env # (optional) Umgebungsvariablen
```
## Quick Setup
### 1. Konfiguration
```bash
cd ansible/netcup-simple-deploy
vim inventory/hosts.yml
```
**Ändere diese Werte:**
```yaml
ansible_host: DEINE-NETCUP-IP
domain: "deine-domain.com"
ssl_email: "deine@email.com"
local_app_path: "../.." # Zeigt auf dein Hauptprojekt
php_version: "8.2" # PHP Version
```
### 2. Deployment
```bash
make deploy
```
Das war's! Deine PHP-App läuft unter `https://deine-domain.com`
## Was passiert beim Deployment?
1. **Dateien übertragen:** `public/`, `src/`, `docker/` → Server
2. **Dockerfile erstellen:** Falls keins in `docker/` vorhanden
3. **Docker Container bauen:** PHP + Apache + deine App
4. **Nginx Proxy:** SSL-Termination und Weiterleitung
5. **SSL-Zertifikat:** Automatisch mit Let's Encrypt
## Für verschiedene PHP-Setups
### Eigenes Dockerfile verwenden
Lege dein Dockerfile in `docker/Dockerfile`:
```dockerfile
FROM php:8.2-apache
# Deine spezifischen PHP Extensions
RUN docker-php-ext-install pdo pdo_mysql
# Custom Apache Config
COPY docker/apache.conf /etc/apache2/sites-available/000-default.conf
# App Dateien
COPY public/ /var/www/html/
COPY src/ /var/www/src/
EXPOSE 80
```
### Mit Composer Dependencies
```dockerfile
FROM php:8.2-apache
# Composer installieren
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Dependencies installieren
COPY composer.json composer.lock /var/www/
WORKDIR /var/www
RUN composer install --no-dev --optimize-autoloader
# App kopieren
COPY public/ /var/www/html/
COPY src/ /var/www/src/
```
### Mit Database
Erweitere `inventory/hosts.yml`:
```yaml
app_env:
APP_ENV: "production"
DATABASE_HOST: "your-db-host"
DATABASE_NAME: "your-db-name"
DATABASE_USER: "your-db-user"
DATABASE_PASS: "your-db-password"
```
## Nützliche Befehle
```bash
# Logs anschauen
make logs
make error-logs
# Cache löschen
make clear-cache
# Permissions reparieren
make fix-permissions
# Composer auf Server ausführen
make composer-install
# Live logs verfolgen
make tail-logs
# SSH auf Server
make ssh
```
## Troubleshooting
### App lädt nicht?
```bash
# Apache Fehler-Logs prüfen
make error-logs
# Allgemeine Logs
make logs
# Container Status
make status
```
### Permissions-Probleme?
```bash
# Permissions reparieren
make fix-permissions
```
### Nach Code-Änderungen?
```bash
# Einfach neu deployen
make deploy
```
### Database-Verbindung?
```bash
# Umgebungsvariablen prüfen
ansible all -m shell -a "docker exec \$(docker ps -q | head -1) env | grep DATABASE"
```
Das Setup ist optimiert für deine bestehende Projektstruktur - keine Änderungen an deinem Code nötig! 🎉

View File

@@ -0,0 +1,8 @@
[defaults]
inventory = inventory/hosts.yml
host_key_checking = False
timeout = 30
[privilege_escalation]
become = True
become_method = sudo

View File

@@ -0,0 +1,105 @@
---
# Fallback Deployment für Debian (mit allen Variablen)
- name: Deploy App to Netcup VPS (Debian Fallback)
hosts: all
become: yes
vars_files:
- inventory/group_vars.yml
tasks:
- name: Update system
apt:
update_cache: yes
upgrade: dist
- name: Install packages from Debian repos
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- git
- curl
- rsync
- docker.io
- docker-compose
state: present
- name: Start and enable Docker
systemd:
name: docker
state: started
enabled: yes
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Deploy webapp
include_role:
name: webapp
- name: Configure Nginx reverse proxy
template:
src: roles/webapp/templates/nginx-site.conf.j2
dest: /etc/nginx/sites-available/{{ domain }}
backup: yes
notify: reload nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/{{ domain }}
dest: /etc/nginx/sites-enabled/{{ domain }}
state: link
notify: reload nginx
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: reload nginx
- name: Generate SSL certificate
command: >
certbot --nginx -d {{ domain }}
--non-interactive --agree-tos
--email {{ ssl_email }}
args:
creates: "/etc/letsencrypt/live/{{ domain }}/fullchain.pem"
- name: Setup SSL renewal
cron:
name: "Renew SSL"
minute: "0"
hour: "3"
job: "certbot renew --quiet"
- name: Start nginx
systemd:
name: nginx
state: started
enabled: yes
- name: Wait for app to be ready
wait_for:
port: 80
delay: 10
timeout: 60
- name: Health check
uri:
url: "https://{{ domain }}"
method: GET
status_code: [200, 301, 302]
retries: 5
delay: 10
ignore_errors: yes
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded

View File

@@ -0,0 +1,119 @@
#!/bin/bash
# PHP Projekt Deployment Script für Netcup (nutzt bestehende docker-compose.yml)
set -e
echo "🚀 Projekt Deployment zu Netcup (nutzt deine docker-compose.yml)"
echo ""
# Prüfe ob Konfiguration angepasst wurde
if grep -q "85.123.456.789" inventory/hosts.yml; then
echo "❌ Bitte erst die Konfiguration anpassen!"
echo ""
echo "1. vim inventory/hosts.yml"
echo " - Server IP ändern"
echo " - Domain ändern"
echo " - app_port prüfen (Port deiner App)"
echo ""
echo "2. Dann nochmal: ./deploy.sh"
exit 1
fi
LOCAL_APP_PATH=$(grep "local_app_path:" inventory/hosts.yml | awk '{print $2}' | tr -d '"')
# Prüfe Projektstruktur
echo "📁 Prüfe Projektstruktur..."
FULL_PATH="$LOCAL_APP_PATH"
if [ ! -d "$FULL_PATH" ]; then
echo "❌ Projekt-Verzeichnis nicht gefunden: $FULL_PATH"
exit 1
fi
echo "✅ Projektstruktur OK:"
echo " 📂 Projekt: $FULL_PATH"
# Prüfe docker-compose.yml
if [ -f "$FULL_PATH/docker-compose.yml" ]; then
echo " ✅ docker-compose.yml gefunden im Root"
elif [ -f "$FULL_PATH/docker/docker-compose.yml" ]; then
echo " ✅ docker-compose.yml gefunden in docker/"
else
echo " Keine docker-compose.yml gefunden - wird automatisch erstellt"
fi
# Zeige docker-compose.yml Inhalt falls vorhanden
if [ -f "$FULL_PATH/docker-compose.yml" ]; then
echo ""
echo "📋 Deine docker-compose.yml (erste 10 Zeilen):"
head -10 "$FULL_PATH/docker-compose.yml" | sed 's/^/ /'
elif [ -f "$FULL_PATH/docker/docker-compose.yml" ]; then
echo ""
echo "📋 Deine docker-compose.yml aus docker/ (erste 10 Zeilen):"
head -10 "$FULL_PATH/docker/docker-compose.yml" | sed 's/^/ /'
fi
# Ping test
echo ""
echo "🔍 Teste Verbindung zum Server..."
if ! ansible all -m ping; then
echo "❌ Server nicht erreichbar. Prüfe:"
echo " - IP-Adresse korrekt?"
echo " - SSH-Key installiert? (ssh-copy-id root@deine-ip)"
echo " - Server läuft?"
exit 1
fi
echo "✅ Server erreichbar!"
echo ""
# Wähle Deployment-Methode
echo "🔧 Deployment-Optionen:"
echo "1. Standard: Saubere Docker-Installation (empfohlen)"
echo "2. Fallback: Debian Standard-Pakete (falls Probleme auftreten)"
echo ""
read -p "Wähle Option (1/2): " -n 1 -r
echo
if [[ $REPLY == "2" ]]; then
PLAYBOOK="deploy-debian-fallback.yml"
echo "📦 Verwende Debian Standard-Pakete"
else
PLAYBOOK="deploy.yml"
echo "🐳 Verwende saubere Docker-Installation"
fi
# Deployment confirmation
read -p "🚀 Projekt deployen? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Deployment abgebrochen."
exit 0
fi
echo "🔧 Starte Deployment mit $PLAYBOOK..."
echo "💡 Das Deployment nutzt deine bestehende docker-compose.yml!"
echo ""
ansible-playbook "$PLAYBOOK"
echo ""
echo "🎉 Deployment abgeschlossen!"
echo ""
# Zeige Ergebnisse
DOMAIN=$(grep "domain:" inventory/hosts.yml | awk '{print $2}' | tr -d '"')
echo "🌐 Dein Projekt ist verfügbar unter:"
echo " https://$DOMAIN"
echo ""
echo "📊 Status prüfen:"
echo " curl -I https://$DOMAIN"
echo ""
echo "🔧 Container-Status anschauen:"
echo " make status"
echo ""
echo "🔧 Logs anschauen:"
echo " make logs"
echo ""
echo "🔄 Nach Änderungen:"
echo " make deploy"

View File

@@ -0,0 +1,163 @@
---
# Ultra-einfaches Netcup Deployment (Port-Konflikt behoben)
- name: Deploy App to Netcup VPS (Debian Clean)
hosts: all
become: yes
vars_files:
- inventory/group_vars.yml
tasks:
- name: Clean up any existing Docker repositories
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/docker.list
- /etc/apt/sources.list.d/download_docker_com_linux_debian.list
- /etc/apt/keyrings/docker.gpg
- /etc/apt/keyrings/docker.asc
ignore_errors: yes
- name: Remove any Docker GPG keys from apt-key
shell: apt-key del 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 || true
ignore_errors: yes
- name: Update apt cache after cleanup
apt:
update_cache: yes
- name: Install basic packages first
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- git
- curl
- rsync
- ca-certificates
- gnupg
- lsb-release
state: present
- name: Create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Add Docker GPG key (new method)
shell: |
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
args:
creates: /etc/apt/keyrings/docker.gpg
- name: Add Docker repository (new method)
shell: |
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
args:
creates: /etc/apt/sources.list.d/docker.list
- name: Update apt cache
apt:
update_cache: yes
- name: Install Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
- name: Start and enable Docker
systemd:
name: docker
state: started
enabled: yes
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Stop nginx temporarily (to avoid port conflicts)
systemd:
name: nginx
state: stopped
ignore_errors: yes
- name: Deploy webapp
include_role:
name: webapp
- name: Configure Nginx reverse proxy
template:
src: roles/webapp/templates/nginx-site.conf.j2
dest: /etc/nginx/sites-available/{{ domain }}
backup: yes
notify: reload nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/{{ domain }}
dest: /etc/nginx/sites-enabled/{{ domain }}
state: link
notify: reload nginx
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: reload nginx
- name: Test nginx configuration
command: nginx -t
register: nginx_test
- name: Start nginx
systemd:
name: nginx
state: started
enabled: yes
- name: Generate SSL certificate
command: >
certbot --nginx -d {{ domain }}
--non-interactive --agree-tos
--email {{ ssl_email }}
args:
creates: "/etc/letsencrypt/live/{{ domain }}/fullchain.pem"
- name: Setup SSL renewal
cron:
name: "Renew SSL"
minute: "0"
hour: "3"
job: "certbot renew --quiet"
- name: Wait for app to be ready
wait_for:
port: 80
delay: 10
timeout: 60
- name: Health check
uri:
url: "https://{{ domain }}"
method: GET
status_code: [200, 301, 302]
retries: 5
delay: 10
ignore_errors: yes
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded

View File

@@ -0,0 +1,19 @@
---
# Globale Einstellungen
# Docker-Einstellungen
docker_compose_version: "2.24.0"
# Nginx-Einstellungen
nginx_client_max_body_size: "50M"
nginx_worker_connections: 1024
# SSL-Einstellungen
ssl_protocols: "TLSv1.2 TLSv1.3"
# App-Verzeichnis auf dem Server
app_directory: "/opt/{{ app_name }}"
# Health Check
health_check_url: "/health"
health_check_timeout: 30

View File

@@ -0,0 +1,26 @@
---
# Netcup Inventar für PHP-Projekt (Fixed paths)
all:
hosts:
netcup-server:
ansible_host: 94.16.110.151
ansible_user: deploy
ansible_ssh_private_key_file: /home/michael/.ssh/staging
# Server-Details
domain: "test.michaelschiemer.de"
ssl_email: "kontakt@michaelschiemer.de"
# App-Konfiguration
app_name: "michaelschiemer"
app_port: 8000
# Pfad zu deinem Projekt (ABSOLUT!)
local_app_path: "/home/michael/dev/michaelschiemer" # Absoluter Pfad zu deinem Hauptprojekt
# Umgebungsvariablen für deine App (wird in .env geschrieben)
app_env:
APP_ENV: "production"
DATABASE_URL: "sqlite:///app/data/app.db"
# Füge hier weitere ENV-Variablen hinzu die deine App braucht

View File

@@ -0,0 +1,91 @@
---
# Restart application containers after file upload
- name: Restart Application Containers
hosts: all
become: yes
vars_files:
- inventory/group_vars.yml
tasks:
- name: Check if app directory exists
stat:
path: "{{ app_directory }}"
register: app_dir_exists
- name: Fail if app directory doesn't exist
fail:
msg: "App directory {{ app_directory }} not found. Please deploy first with deploy.yml"
when: not app_dir_exists.stat.exists
- name: Check which docker compose command is available
shell: |
if docker compose version >/dev/null 2>&1; then
echo "docker compose"
elif docker-compose --version >/dev/null 2>&1; then
echo "docker-compose"
else
echo "none"
fi
register: docker_compose_cmd
changed_when: false
- name: Fail if docker compose not available
fail:
msg: "Neither 'docker compose' nor 'docker-compose' is available"
when: docker_compose_cmd.stdout == "none"
- name: Show current container status
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} ps"
register: container_status_before
ignore_errors: yes
changed_when: false
- name: Stop existing containers
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} down"
register: stop_result
- name: Start containers with updated files
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} up -d --build"
register: start_result
- name: Wait for application to start
wait_for:
port: "{{ app_port }}"
host: "127.0.0.1"
delay: 5
timeout: 60
- name: Test if app is accessible
uri:
url: "http://127.0.0.1:{{ app_port }}/"
method: GET
status_code: [200, 301, 302]
register: app_test
ignore_errors: yes
- name: Show final container status
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} ps"
register: container_status_after
changed_when: false
- name: Show restart result
debug:
msg: |
🔄 Application restart completed!
📂 Directory: {{ app_directory }}
🐳 Docker Compose: {{ docker_compose_cmd.stdout }}
🚀 Restart status: {{ 'Success' if start_result.rc == 0 else 'Failed' }}
{% if app_test.status is defined and (app_test.status == 200 or app_test.status == 301 or app_test.status == 302) %}
✅ App is responding (HTTP {{ app_test.status }})
🌐 Available at: https://{{ domain }}
{% else %}
⚠️ App health check failed - please check logs
🔍 Check logs with: cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} logs
{% endif %}
📊 Container Status:
{{ container_status_after.stdout }}

View File

@@ -0,0 +1,24 @@
---
# Default variables for webapp role (Port-Konflikt behoben)
# App directory on server
app_directory: "/opt/{{ app_name }}"
# PHP settings
php_version: "8.4"
# Health check
health_check_url: "/health"
health_check_timeout: 30
# Default app settings if not defined in inventory
app_name: "myapp"
app_port: 8000 # PHP läuft auf Port 8080, nginx auf Port 80
domain: "test.michaelschiemer.de"
ssl_email: "kontakt@michaelschiemer.de"
# Default environment variables
app_env:
APP_ENV: "production"
PHP_MEMORY_LIMIT: "256M"
PHP_UPLOAD_MAX_FILESIZE: "50M"

View File

@@ -0,0 +1,272 @@
---
# PHP Webapp Deployment (Handle missing PHP config files)
- name: Create app directory
file:
path: "{{ app_directory }}"
state: directory
mode: '0755'
- name: Check if docker-compose.yml exists locally first
local_action:
module: stat
path: "{{ local_app_path }}/docker-compose.yml"
register: local_compose_exists
become: no
- name: Show local docker-compose.yml status
debug:
msg: |
🔍 Local docker-compose.yml check:
- Path: {{ local_app_path }}/docker-compose.yml
- Exists: {{ local_compose_exists.stat.exists }}
- name: Fail if docker-compose.yml doesn't exist locally
fail:
msg: |
❌ docker-compose.yml nicht im lokalen Projekt gefunden!
Geprüft: {{ local_app_path }}/docker-compose.yml
Bitte stelle sicher, dass eine docker-compose.yml in deinem Projekt-Root existiert.
when: not local_compose_exists.stat.exists
- name: Upload project files with working synchronize
synchronize:
src: "{{ local_app_path }}/"
dest: "{{ app_directory }}/"
delete: no
archive: yes
checksum: yes
rsync_opts:
- "--exclude=ansible"
- "--exclude=.git"
- "--exclude=vendor"
- "--exclude=node_modules"
- "--exclude=storage/logs"
- "--exclude=cache"
- "--exclude=logs"
- "--exclude=dist"
- "--exclude=.archive"
- "--exclude=x_ansible"
- "--verbose"
register: sync_result
- name: Check if required PHP config files exist
stat:
path: "{{ app_directory }}/docker/php/php.production.ini"
register: php_prod_config
- name: Create missing PHP production config if needed
copy:
content: |
; PHP Production Configuration
memory_limit = 256M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 300
max_input_vars = 3000
; Error reporting for production
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; Opcache settings
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 4000
opcache.revalidate_freq = 2
opcache.validate_timestamps = 0
dest: "{{ app_directory }}/docker/php/php.production.ini"
mode: '0644'
when: not php_prod_config.stat.exists
- name: Check if common PHP config exists
stat:
path: "{{ app_directory }}/docker/php/php.common.ini"
register: php_common_config
- name: Create missing PHP common config if needed
copy:
content: |
; PHP Common Configuration
date.timezone = Europe/Berlin
short_open_tag = Off
expose_php = Off
; Security
allow_url_fopen = On
allow_url_include = Off
dest: "{{ app_directory }}/docker/php/php.common.ini"
mode: '0644'
when: not php_common_config.stat.exists
- name: Ensure PHP config directory exists
file:
path: "{{ app_directory }}/docker/php"
state: directory
mode: '0755'
- name: Show what files were synced
debug:
msg: |
📂 Sync Result:
- Changed: {{ sync_result.changed }}
- name: Ensure proper permissions for directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
recurse: yes
loop:
- "{{ app_directory }}/public"
- "{{ app_directory }}/src"
- "{{ app_directory }}/docker"
ignore_errors: yes
- name: Create storage directories if they don't exist
file:
path: "{{ app_directory }}/{{ item }}"
state: directory
mode: '0777'
loop:
- "storage/logs"
- "storage/cache"
- "cache"
- "logs"
ignore_errors: yes
- name: Check if docker-compose.yml exists in project root
stat:
path: "{{ app_directory }}/docker-compose.yml"
register: compose_exists
- name: Check if docker-compose.yml exists in docker folder
stat:
path: "{{ app_directory }}/docker/docker-compose.yml"
register: compose_docker_exists
- name: Use docker-compose.yml from docker folder if available
copy:
src: "{{ app_directory }}/docker/docker-compose.yml"
dest: "{{ app_directory }}/docker-compose.yml"
remote_src: yes
when: compose_docker_exists.stat.exists and not compose_exists.stat.exists
- name: Manually copy docker-compose.yml if sync failed
copy:
src: "{{ local_app_path }}/docker-compose.yml"
dest: "{{ app_directory }}/docker-compose.yml"
mode: '0644'
when: local_compose_exists.stat.exists and not compose_exists.stat.exists
- name: Show which docker-compose.yml we found
debug:
msg: |
📋 Docker Compose Status:
- Root compose exists: {{ compose_exists.stat.exists }}
- Docker folder compose exists: {{ compose_docker_exists.stat.exists }}
- name: Fail if no docker-compose.yml found
fail:
msg: |
❌ Keine docker-compose.yml gefunden!
Erwartet in:
- {{ app_directory }}/docker-compose.yml
- {{ app_directory }}/docker/docker-compose.yml
Bitte erstelle eine docker-compose.yml in deinem Projekt.
when: not compose_exists.stat.exists and not compose_docker_exists.stat.exists
- name: Check if public/index.php exists after sync
stat:
path: "{{ app_directory }}/public/index.php"
register: index_exists
- name: Fail if index.php not found
fail:
msg: |
❌ index.php nicht gefunden!
Geprüft: {{ app_directory }}/public/index.php
Die Dateien wurden nicht korrekt übertragen.
when: not index_exists.stat.exists
- name: Create environment file
template:
src: app.env.j2
dest: "{{ app_directory }}/.env"
register: env_result
- name: Check which docker compose command is available
shell: |
if docker compose version >/dev/null 2>&1; then
echo "docker compose"
elif docker-compose --version >/dev/null 2>&1; then
echo "docker-compose"
else
echo "none"
fi
register: docker_compose_cmd
changed_when: false
- name: Stop existing containers (if any)
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} down"
when: docker_compose_cmd.stdout != "none"
ignore_errors: yes
- name: Start containers with your docker-compose.yml
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} up -d --build"
register: start_result
when: docker_compose_cmd.stdout != "none"
- name: Wait for application to start
wait_for:
port: "{{ app_port }}"
host: "127.0.0.1"
delay: 5
timeout: 60
- name: Test if app is accessible
uri:
url: "http://127.0.0.1:{{ app_port }}/"
method: GET
status_code: [200, 301, 302]
register: app_test
ignore_errors: yes
- name: Show container status
shell: "cd {{ app_directory }} && {{ docker_compose_cmd.stdout }} ps"
register: container_status
when: docker_compose_cmd.stdout != "none"
ignore_errors: yes
- name: Show deployment result
debug:
msg: |
🎉 Projekt {{ app_name }} erfolgreich deployed!
📂 Projektdateien synchronisiert von: {{ local_app_path }}
🐳 Verwendete docker-compose.yml:
{% if compose_exists.stat.exists %}
✅ Aus Projekt-Root: {{ app_directory }}/docker-compose.yml
{% elif compose_docker_exists.stat.exists %}
✅ Aus docker/ Ordner: {{ app_directory }}/docker/docker-compose.yml
{% endif %}
🌐 Erreichbar unter: https://{{ domain }}
⚙️ Docker Compose: {{ docker_compose_cmd.stdout }}
📁 index.php Status: {{ 'Gefunden ✅' if index_exists.stat.exists else 'Nicht gefunden ❌' }}
🐳 Container Status:
{{ container_status.stdout if container_status.stdout is defined else 'Status konnte nicht abgerufen werden' }}
{% if app_test.status is defined and (app_test.status == 200 or app_test.status == 301 or app_test.status == 302) %}
✅ App reagiert erfolgreich (HTTP {{ app_test.status }})
{% else %}
⚠️ App-Test fehlgeschlagen - prüfe Logs mit 'make logs'
{% endif %}

View File

@@ -0,0 +1,12 @@
# Environment variables for {{ app_name }}
{% for key, value in app_env.items() %}
{{ key }}={{ value }}
{% endfor %}
# Deployment info
DEPLOYED_AT={{ ansible_date_time.iso8601 }}
DEPLOYED_BY=ansible
SERVER_NAME={{ inventory_hostname }}
APP_PORT=8000

View File

@@ -0,0 +1,53 @@
server {
listen 80;
server_name {{ domain }};
# HTTP to HTTPS redirect (wird von Certbot hinzugefügt)
location / {
proxy_pass http://127.0.0.1:{{ app_port }}; # Weiterleitung zu PHP-Container
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# PHP-spezifische Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# File upload für PHP
client_max_body_size {{ nginx_client_max_body_size }};
}
# Assets (CSS, JS, Bilder) direkt servieren falls gewünscht
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_cache_valid 200 1d;
expires 1d;
add_header Cache-Control "public, immutable";
}
# PHP-Admin Tools (falls vorhanden) schützen
location ~ /(admin|phpmyadmin|adminer) {
proxy_pass http://127.0.0.1:{{ app_port }};
# Basis Auth hier hinzufügen falls gewünscht
# auth_basic "Admin Area";
# auth_basic_user_file /etc/nginx/.htpasswd;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}

View File

@@ -0,0 +1,128 @@
---
# Nur Dateien-Upload ohne Infrastruktur-Setup
- name: Upload Files Only to Netcup VPS
hosts: all
become: yes
vars_files:
- inventory/group_vars.yml
tasks:
- name: Check if app directory exists
stat:
path: "{{ app_directory }}"
register: app_dir_exists
- name: Create app directory if it doesn't exist
file:
path: "{{ app_directory }}"
state: directory
mode: '0755'
when: not app_dir_exists.stat.exists
- name: Check if docker-compose.yml exists locally
local_action:
module: stat
path: "{{ local_app_path }}/docker-compose.yml"
register: local_compose_exists
become: no
- name: Show upload information
debug:
msg: |
📤 Uploading files...
- From: {{ local_app_path }}
- To: {{ app_directory }}
- Docker Compose available: {{ local_compose_exists.stat.exists }}
- name: Upload project files
synchronize:
src: "{{ local_app_path }}/"
dest: "{{ app_directory }}/"
delete: no
archive: yes
checksum: yes
rsync_opts:
- "--exclude=ansible"
- "--exclude=.git"
- "--exclude=vendor"
- "--exclude=node_modules"
- "--exclude=storage/logs"
- "--exclude=cache"
- "--exclude=logs"
- "--exclude=dist"
- "--exclude=.archive"
- "--exclude=x_ansible"
- "--exclude=.env"
- "--verbose"
register: sync_result
- name: Ensure proper permissions for directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
recurse: yes
loop:
- "{{ app_directory }}/public"
- "{{ app_directory }}/src"
- "{{ app_directory }}/docker"
ignore_errors: yes
- name: Create storage directories if they don't exist
file:
path: "{{ app_directory }}/{{ item }}"
state: directory
mode: '0777'
loop:
- "storage/logs"
- "storage/cache"
- "cache"
- "logs"
ignore_errors: yes
- name: Check if .env exists on server
stat:
path: "{{ app_directory }}/.env"
register: server_env_exists
- name: Create environment file if it doesn't exist
template:
src: roles/webapp/templates/app.env.j2
dest: "{{ app_directory }}/.env"
when: not server_env_exists.stat.exists
register: env_created
- name: Show what was uploaded
debug:
msg: |
📂 Upload completed!
📁 Files synced: {{ 'Yes' if sync_result.changed else 'No changes detected' }}
📄 Environment file: {{ 'Created' if env_created.changed else 'Already exists' }}
📍 Files are now at: {{ app_directory }}
🔄 To restart the application, run:
cd {{ app_directory }}
docker compose down && docker compose up -d --build
or use: make restart (if Makefile is available)
- name: Check if containers are running (optional restart)
shell: "cd {{ app_directory }} && docker compose ps --format json"
register: container_status
ignore_errors: yes
changed_when: false
- name: Ask about restarting containers
debug:
msg: |
Current container status:
{{ container_status.stdout if container_status.stdout else 'No containers found or docker compose not available' }}
💡 Tip: If you want to restart the application with the new files, run:
ansible-playbook restart-app.yml
or manually:
ssh {{ ansible_host }} "cd {{ app_directory }} && docker compose restart"