chore: complete update
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,7 +25,7 @@ ssl/*.pem
|
|||||||
node_modules
|
node_modules
|
||||||
storage/uploads/*
|
storage/uploads/*
|
||||||
storage/logs/.gitkeep
|
storage/logs/.gitkeep
|
||||||
dist/
|
!/dist/
|
||||||
|
|
||||||
.archive/
|
.archive/
|
||||||
|
|
||||||
|
|||||||
6
ansible/netcup-simple-deploy/.gitignore
vendored
Normal file
6
ansible/netcup-simple-deploy/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# .gitignore für Netcup Deployment
|
||||||
|
*.retry
|
||||||
|
.ansible/
|
||||||
|
*.log
|
||||||
|
.env.local
|
||||||
|
secrets.yml
|
||||||
136
ansible/netcup-simple-deploy/Makefile
Normal file
136
ansible/netcup-simple-deploy/Makefile
Normal 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"
|
||||||
128
ansible/netcup-simple-deploy/QUICK-SETUP.md
Normal file
128
ansible/netcup-simple-deploy/QUICK-SETUP.md
Normal 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! 🎉
|
||||||
40
ansible/netcup-simple-deploy/README.md
Normal file
40
ansible/netcup-simple-deploy/README.md
Normal 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!
|
||||||
161
ansible/netcup-simple-deploy/SETUP-COMPOSE.md
Normal file
161
ansible/netcup-simple-deploy/SETUP-COMPOSE.md
Normal 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! 🎉
|
||||||
172
ansible/netcup-simple-deploy/SETUP-OHNE-GIT.md
Normal file
172
ansible/netcup-simple-deploy/SETUP-OHNE-GIT.md
Normal 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! 🎉
|
||||||
165
ansible/netcup-simple-deploy/SETUP-PHP.md
Normal file
165
ansible/netcup-simple-deploy/SETUP-PHP.md
Normal 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! 🎉
|
||||||
8
ansible/netcup-simple-deploy/ansible.cfg
Normal file
8
ansible/netcup-simple-deploy/ansible.cfg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = inventory/hosts.yml
|
||||||
|
host_key_checking = False
|
||||||
|
timeout = 30
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
become = True
|
||||||
|
become_method = sudo
|
||||||
105
ansible/netcup-simple-deploy/deploy-debian-fallback.yml
Normal file
105
ansible/netcup-simple-deploy/deploy-debian-fallback.yml
Normal 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
|
||||||
119
ansible/netcup-simple-deploy/deploy.sh
Executable file
119
ansible/netcup-simple-deploy/deploy.sh
Executable 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"
|
||||||
163
ansible/netcup-simple-deploy/deploy.yml
Normal file
163
ansible/netcup-simple-deploy/deploy.yml
Normal 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
|
||||||
19
ansible/netcup-simple-deploy/inventory/group_vars.yml
Normal file
19
ansible/netcup-simple-deploy/inventory/group_vars.yml
Normal 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
|
||||||
26
ansible/netcup-simple-deploy/inventory/hosts.yml
Normal file
26
ansible/netcup-simple-deploy/inventory/hosts.yml
Normal 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
|
||||||
91
ansible/netcup-simple-deploy/restart-app.yml
Normal file
91
ansible/netcup-simple-deploy/restart-app.yml
Normal 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 }}
|
||||||
24
ansible/netcup-simple-deploy/roles/webapp/defaults/main.yml
Normal file
24
ansible/netcup-simple-deploy/roles/webapp/defaults/main.yml
Normal 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"
|
||||||
272
ansible/netcup-simple-deploy/roles/webapp/tasks/main.yml
Normal file
272
ansible/netcup-simple-deploy/roles/webapp/tasks/main.yml
Normal 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 %}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
128
ansible/netcup-simple-deploy/upload-only.yml
Normal file
128
ansible/netcup-simple-deploy/upload-only.yml
Normal 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"
|
||||||
31
ansible/nginx-cdn-germany/.gitignore
vendored
Normal file
31
ansible/nginx-cdn-germany/.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Cache
|
||||||
|
*.cache
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Ansible
|
||||||
|
*.retry
|
||||||
|
.ansible/
|
||||||
|
|
||||||
|
# System
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
*.backup
|
||||||
|
*.bak
|
||||||
|
|
||||||
|
# SSL Keys (niemals committen!)
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
*.crt
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
vault.yml
|
||||||
|
secrets.yml
|
||||||
|
|
||||||
|
# Temporäre Dateien
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
64
ansible/nginx-cdn-germany/Makefile
Normal file
64
ansible/nginx-cdn-germany/Makefile
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Einfache CDN-Verwaltung mit Make
|
||||||
|
|
||||||
|
.PHONY: deploy check health purge-cache warm-cache status reload
|
||||||
|
|
||||||
|
# Standard deployment
|
||||||
|
deploy:
|
||||||
|
@echo "🚀 Deploying Simple CDN..."
|
||||||
|
chmod +x scripts/deploy.sh
|
||||||
|
./scripts/deploy.sh
|
||||||
|
|
||||||
|
# Deployment mit Check-Modus (Dry-Run)
|
||||||
|
check:
|
||||||
|
@echo "🔍 Checking deployment (dry-run)..."
|
||||||
|
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml --check --diff
|
||||||
|
|
||||||
|
# Health check aller Nodes
|
||||||
|
health:
|
||||||
|
@echo "🏥 Checking CDN health..."
|
||||||
|
ansible cdn_nodes -i inventories/production/hosts.yml -m uri -a "url=https://{{ inventory_hostname }}/health method=GET"
|
||||||
|
|
||||||
|
# Cache leeren
|
||||||
|
purge-cache:
|
||||||
|
@echo "🧹 Purging cache on all nodes..."
|
||||||
|
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "find /var/cache/nginx/ -type f -delete"
|
||||||
|
@echo "✅ Cache purged on all nodes"
|
||||||
|
|
||||||
|
# Cache warming
|
||||||
|
warm-cache:
|
||||||
|
@echo "🔥 Warming cache..."
|
||||||
|
chmod +x scripts/warm-cache.sh
|
||||||
|
./scripts/warm-cache.sh
|
||||||
|
|
||||||
|
# Status-Report
|
||||||
|
status:
|
||||||
|
@echo "📊 CDN Status Report..."
|
||||||
|
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "echo '=== {{ inventory_hostname }} ===' && /usr/local/bin/cdn-monitor && echo ''"
|
||||||
|
|
||||||
|
# Nginx neuladen
|
||||||
|
reload:
|
||||||
|
@echo "⚙️ Reloading nginx configuration..."
|
||||||
|
ansible cdn_nodes -i inventories/production/hosts.yml -m systemd -a "name=nginx state=reloaded"
|
||||||
|
|
||||||
|
# SSL-Zertifikate erneuern
|
||||||
|
renew-ssl:
|
||||||
|
@echo "🔐 Renewing SSL certificates..."
|
||||||
|
ansible cdn_nodes -i inventories/production/hosts.yml -m shell -a "certbot renew --quiet"
|
||||||
|
|
||||||
|
# Interaktive Verwaltung
|
||||||
|
manage:
|
||||||
|
@echo "🔧 Starting interactive management..."
|
||||||
|
ansible-playbook -i inventories/production/hosts.yml playbooks/manage-cdn.yml
|
||||||
|
|
||||||
|
# Hilfe
|
||||||
|
help:
|
||||||
|
@echo "📖 Available commands:"
|
||||||
|
@echo " make deploy - Deploy CDN"
|
||||||
|
@echo " make check - Test deployment (dry-run)"
|
||||||
|
@echo " make health - Check all nodes health"
|
||||||
|
@echo " make purge-cache - Clear all cache"
|
||||||
|
@echo " make warm-cache - Warm cache with popular URLs"
|
||||||
|
@echo " make status - Show detailed status"
|
||||||
|
@echo " make reload - Reload nginx config"
|
||||||
|
@echo " make renew-ssl - Renew SSL certificates"
|
||||||
|
@echo " make manage - Interactive management"
|
||||||
48
ansible/nginx-cdn-germany/README.md
Normal file
48
ansible/nginx-cdn-germany/README.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Simple Nginx CDN für Deutschland
|
||||||
|
|
||||||
|
Dieses Ansible-Projekt erstellt ein einfaches, aber effektives CDN nur mit Nginx für deutsche Server.
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
1. **Konfiguration anpassen:**
|
||||||
|
```bash
|
||||||
|
# Server-IPs eintragen
|
||||||
|
vim inventories/production/hosts.yml
|
||||||
|
|
||||||
|
# Domains anpassen
|
||||||
|
vim inventories/production/group_vars/all/main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Deployment:**
|
||||||
|
```bash
|
||||||
|
# Testen
|
||||||
|
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml --check
|
||||||
|
|
||||||
|
# Deployen
|
||||||
|
ansible-playbook -i inventories/production/hosts.yml playbooks/deploy-simple-cdn.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Verwalten:**
|
||||||
|
```bash
|
||||||
|
# Cache leeren
|
||||||
|
make purge-cache
|
||||||
|
|
||||||
|
# Status prüfen
|
||||||
|
make health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Struktur
|
||||||
|
|
||||||
|
- `inventories/` - Server-Konfiguration
|
||||||
|
- `roles/` - Ansible-Rollen
|
||||||
|
- `playbooks/` - Deployment-Skripte
|
||||||
|
- `scripts/` - Hilfsskripte
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
✅ Nginx-basiertes CDN
|
||||||
|
✅ SSL mit Let's Encrypt
|
||||||
|
✅ DSGVO-konforme Logs
|
||||||
|
✅ Einfaches Monitoring
|
||||||
|
✅ Cache-Management
|
||||||
|
✅ Rate Limiting
|
||||||
115
ansible/nginx-cdn-germany/SETUP.md
Normal file
115
ansible/nginx-cdn-germany/SETUP.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# SETUP.md - Einrichtungsanleitung
|
||||||
|
|
||||||
|
## 1. Vorbereitung
|
||||||
|
|
||||||
|
### Server vorbereiten
|
||||||
|
```bash
|
||||||
|
# Für jeden CDN-Server (als root):
|
||||||
|
apt update && apt upgrade -y
|
||||||
|
apt install -y python3 python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSH-Keys einrichten
|
||||||
|
```bash
|
||||||
|
# Auf deinem lokalen Rechner:
|
||||||
|
ssh-keygen -t rsa -b 4096 -C "cdn-deployment"
|
||||||
|
ssh-copy-id root@cdn-fra1.example.de
|
||||||
|
ssh-copy-id root@cdn-ham1.example.de
|
||||||
|
ssh-copy-id root@cdn-muc1.example.de
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Konfiguration anpassen
|
||||||
|
|
||||||
|
### Domains und IPs ändern
|
||||||
|
```bash
|
||||||
|
# 1. Server-IPs eintragen
|
||||||
|
vim inventories/production/hosts.yml
|
||||||
|
|
||||||
|
# 2. Domain-Namen anpassen
|
||||||
|
vim inventories/production/group_vars/all/main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Ändere diese Werte:
|
||||||
|
- `cdn_domain: "cdn.example.de"` → deine CDN-Domain
|
||||||
|
- `ssl_email: "admin@example.de"` → deine E-Mail
|
||||||
|
- `origin_domain: "www.example.de"` → deine Website
|
||||||
|
- Alle IP-Adressen in `hosts.yml`
|
||||||
|
|
||||||
|
## 3. DNS konfigurieren
|
||||||
|
|
||||||
|
Bevor du deployest, stelle sicher dass deine CDN-Domain zu den Servern zeigt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# A-Records für deine CDN-Domain:
|
||||||
|
cdn.example.de. IN A 10.0.1.10 # Frankfurt
|
||||||
|
cdn.example.de. IN A 10.0.2.10 # Hamburg
|
||||||
|
cdn.example.de. IN A 10.0.3.10 # München
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Testen
|
||||||
|
make check
|
||||||
|
|
||||||
|
# Deployen
|
||||||
|
make deploy
|
||||||
|
|
||||||
|
# Health-Check
|
||||||
|
make health
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CDN testen
|
||||||
|
curl -I https://cdn.example.de/health
|
||||||
|
|
||||||
|
# Cache-Header prüfen
|
||||||
|
curl -I https://cdn.example.de/some-static-file.css
|
||||||
|
|
||||||
|
# Performance testen
|
||||||
|
time curl -o /dev/null -s https://cdn.example.de/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Wartung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Cache leeren
|
||||||
|
make purge-cache
|
||||||
|
|
||||||
|
# Status prüfen
|
||||||
|
make status
|
||||||
|
|
||||||
|
# SSL erneuern
|
||||||
|
make renew-ssl
|
||||||
|
|
||||||
|
# Interaktive Verwaltung
|
||||||
|
make manage
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Ansible-Verbindung testen
|
||||||
|
```bash
|
||||||
|
ansible all -m ping
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nginx-Konfiguration prüfen
|
||||||
|
```bash
|
||||||
|
ansible cdn_nodes -m shell -a "nginx -t"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs anschauen
|
||||||
|
```bash
|
||||||
|
ansible cdn_nodes -m shell -a "tail -f /var/log/nginx/error.log"
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL-Probleme
|
||||||
|
```bash
|
||||||
|
# SSL-Status prüfen
|
||||||
|
ansible cdn_nodes -m shell -a "certbot certificates"
|
||||||
|
|
||||||
|
# Manuell erneuern
|
||||||
|
ansible cdn_nodes -m shell -a "certbot renew --force-renewal"
|
||||||
|
```
|
||||||
15
ansible/nginx-cdn-germany/ansible.cfg
Normal file
15
ansible/nginx-cdn-germany/ansible.cfg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = inventories/production/hosts.yml
|
||||||
|
host_key_checking = False
|
||||||
|
timeout = 30
|
||||||
|
forks = 5
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
become = True
|
||||||
|
become_method = sudo
|
||||||
|
become_user = root
|
||||||
|
become_ask_pass = False
|
||||||
|
|
||||||
|
[ssh_connection]
|
||||||
|
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
|
||||||
|
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
|
||||||
19
ansible/nginx-cdn-germany/init.sh
Normal file
19
ansible/nginx-cdn-germany/init.sh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Scripts ausführbar machen
|
||||||
|
chmod +x scripts/deploy.sh
|
||||||
|
chmod +x scripts/warm-cache.sh
|
||||||
|
|
||||||
|
echo "✅ CDN-Projekt wurde erfolgreich erstellt!"
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo "1. Konfiguration anpassen:"
|
||||||
|
echo " - vim inventories/production/hosts.yml"
|
||||||
|
echo " - vim inventories/production/group_vars/all/main.yml"
|
||||||
|
echo ""
|
||||||
|
echo "2. SETUP.md lesen für detaillierte Anleitung"
|
||||||
|
echo ""
|
||||||
|
echo "3. Deployment testen:"
|
||||||
|
echo " make check"
|
||||||
|
echo ""
|
||||||
|
echo "4. CDN deployen:"
|
||||||
|
echo " make deploy"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# Globale Variablen für das CDN
|
||||||
|
|
||||||
|
# Domain-Konfiguration (ÄNDERE DIESE!)
|
||||||
|
cdn_domain: "cdn.example.de" # Deine CDN-Domain
|
||||||
|
ssl_email: "admin@example.de" # E-Mail für SSL-Zertifikate
|
||||||
|
origin_domain: "www.example.de" # Deine Haupt-Website
|
||||||
|
|
||||||
|
# Cache-Einstellungen
|
||||||
|
cache_settings:
|
||||||
|
static_files_ttl: "1y" # CSS, JS, Fonts
|
||||||
|
images_ttl: "30d" # Bilder
|
||||||
|
html_ttl: "5m" # HTML-Seiten
|
||||||
|
api_ttl: "0" # APIs (kein Caching)
|
||||||
|
|
||||||
|
# DSGVO-Einstellungen
|
||||||
|
gdpr_settings:
|
||||||
|
log_retention_days: 30
|
||||||
|
anonymize_ips: true
|
||||||
|
cookie_consent_required: true
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
rate_limits:
|
||||||
|
api: "10r/s"
|
||||||
|
static: "100r/s"
|
||||||
|
images: "50r/s"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# CDN-Node spezifische Konfiguration
|
||||||
|
|
||||||
|
# Nginx Performance
|
||||||
|
nginx_worker_processes: "auto"
|
||||||
|
nginx_worker_connections: 2048
|
||||||
|
nginx_keepalive_timeout: 65
|
||||||
|
|
||||||
|
# Performance-Tuning
|
||||||
|
tcp_optimizations:
|
||||||
|
tcp_nodelay: "on"
|
||||||
|
tcp_nopush: "on"
|
||||||
|
sendfile: "on"
|
||||||
|
|
||||||
|
# Proxy-Einstellungen
|
||||||
|
proxy_settings:
|
||||||
|
connect_timeout: "5s"
|
||||||
|
send_timeout: "10s"
|
||||||
|
read_timeout: "10s"
|
||||||
|
buffering: "on"
|
||||||
|
buffer_size: "64k"
|
||||||
|
buffers: "8 64k"
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
# Inventar mit gruppierten SSH-Schlüsseln
|
||||||
|
|
||||||
|
all:
|
||||||
|
children:
|
||||||
|
origin_servers:
|
||||||
|
hosts:
|
||||||
|
origin1.example.de:
|
||||||
|
ansible_host: 192.168.1.10
|
||||||
|
origin2.example.de:
|
||||||
|
ansible_host: 192.168.1.11
|
||||||
|
vars:
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/origin_servers_key
|
||||||
|
|
||||||
|
cdn_nodes:
|
||||||
|
children:
|
||||||
|
primary_nodes:
|
||||||
|
hosts:
|
||||||
|
cdn-fra1.example.de:
|
||||||
|
ansible_host: 10.0.1.10
|
||||||
|
city: "Frankfurt"
|
||||||
|
region: "Hessen"
|
||||||
|
tier: "primary"
|
||||||
|
cache_size: "50g"
|
||||||
|
vars:
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/cdn_primary_key
|
||||||
|
|
||||||
|
secondary_nodes:
|
||||||
|
hosts:
|
||||||
|
cdn-ham1.example.de:
|
||||||
|
ansible_host: 10.0.2.10
|
||||||
|
city: "Hamburg"
|
||||||
|
region: "Hamburg"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
cdn-muc1.example.de:
|
||||||
|
ansible_host: 10.0.3.10
|
||||||
|
city: "München"
|
||||||
|
region: "Bayern"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
vars:
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/cdn_secondary_key
|
||||||
|
|
||||||
|
vars:
|
||||||
|
ansible_user: root
|
||||||
|
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
# Inventar mit individuellen SSH-Schlüsseln
|
||||||
|
|
||||||
|
all:
|
||||||
|
children:
|
||||||
|
origin_servers:
|
||||||
|
hosts:
|
||||||
|
origin1.example.de:
|
||||||
|
ansible_host: 192.168.1.10
|
||||||
|
datacenter: "Frankfurt"
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/origin1_key
|
||||||
|
origin2.example.de:
|
||||||
|
ansible_host: 192.168.1.11
|
||||||
|
datacenter: "Frankfurt"
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/origin2_key
|
||||||
|
|
||||||
|
cdn_nodes:
|
||||||
|
hosts:
|
||||||
|
# Frankfurt - Primary
|
||||||
|
cdn-fra1.example.de:
|
||||||
|
ansible_host: 10.0.1.10
|
||||||
|
city: "Frankfurt"
|
||||||
|
region: "Hessen"
|
||||||
|
tier: "primary"
|
||||||
|
cache_size: "50g"
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/cdn_fra1_key
|
||||||
|
|
||||||
|
# Hamburg - Secondary
|
||||||
|
cdn-ham1.example.de:
|
||||||
|
ansible_host: 10.0.2.10
|
||||||
|
city: "Hamburg"
|
||||||
|
region: "Hamburg"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/cdn_ham1_key
|
||||||
|
|
||||||
|
# München - Secondary
|
||||||
|
cdn-muc1.example.de:
|
||||||
|
ansible_host: 10.0.3.10
|
||||||
|
city: "München"
|
||||||
|
region: "Bayern"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/cdn_muc1_key
|
||||||
|
|
||||||
|
vars:
|
||||||
|
ansible_user: root
|
||||||
|
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
|
||||||
45
ansible/nginx-cdn-germany/inventories/production/hosts.yml
Normal file
45
ansible/nginx-cdn-germany/inventories/production/hosts.yml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
# Inventar für deutsches CDN
|
||||||
|
|
||||||
|
all:
|
||||||
|
children:
|
||||||
|
origin_servers:
|
||||||
|
hosts:
|
||||||
|
origin1.example.de:
|
||||||
|
ansible_host: 192.168.1.10 # Ändere diese IP
|
||||||
|
datacenter: "Frankfurt"
|
||||||
|
origin2.example.de:
|
||||||
|
ansible_host: 192.168.1.11 # Ändere diese IP
|
||||||
|
datacenter: "Frankfurt"
|
||||||
|
|
||||||
|
cdn_nodes:
|
||||||
|
hosts:
|
||||||
|
# Frankfurt - Primary
|
||||||
|
cdn-fra1.example.de:
|
||||||
|
ansible_host: 10.0.1.10 # Ändere diese IP
|
||||||
|
city: "Frankfurt"
|
||||||
|
region: "Hessen"
|
||||||
|
tier: "primary"
|
||||||
|
cache_size: "50g"
|
||||||
|
|
||||||
|
# Hamburg - Secondary
|
||||||
|
cdn-ham1.example.de:
|
||||||
|
ansible_host: 10.0.2.10 # Ändere diese IP
|
||||||
|
city: "Hamburg"
|
||||||
|
region: "Hamburg"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
|
||||||
|
# München - Secondary
|
||||||
|
cdn-muc1.example.de:
|
||||||
|
ansible_host: 10.0.3.10 # Ändere diese IP
|
||||||
|
city: "München"
|
||||||
|
region: "Bayern"
|
||||||
|
tier: "secondary"
|
||||||
|
cache_size: "20g"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
# SSH-Konfiguration
|
||||||
|
ansible_user: root
|
||||||
|
ansible_ssh_private_key_file: ~/.ssh/id_rsa
|
||||||
|
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
|
||||||
43
ansible/nginx-cdn-germany/playbooks/deploy-simple-cdn.yml
Normal file
43
ansible/nginx-cdn-germany/playbooks/deploy-simple-cdn.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
# Simple CDN Deployment
|
||||||
|
|
||||||
|
- name: Deploy Simple CDN for Germany
|
||||||
|
hosts: cdn_nodes
|
||||||
|
become: yes
|
||||||
|
serial: 1 # Ein Node nach dem anderen
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Update system packages
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
upgrade: dist
|
||||||
|
|
||||||
|
- name: Install required packages
|
||||||
|
apt:
|
||||||
|
name:
|
||||||
|
- nginx
|
||||||
|
- certbot
|
||||||
|
- python3-certbot-nginx
|
||||||
|
- curl
|
||||||
|
- htop
|
||||||
|
- bc
|
||||||
|
state: present
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- nginx-cdn-config
|
||||||
|
- ssl-certificates
|
||||||
|
- simple-monitoring
|
||||||
|
|
||||||
|
post_tasks:
|
||||||
|
- name: Verify CDN health
|
||||||
|
uri:
|
||||||
|
url: "https://{{ inventory_hostname }}/health"
|
||||||
|
method: GET
|
||||||
|
status_code: 200
|
||||||
|
validate_certs: yes
|
||||||
|
retries: 5
|
||||||
|
delay: 10
|
||||||
|
|
||||||
|
- name: Display deployment success
|
||||||
|
debug:
|
||||||
|
msg: "✅ CDN Node {{ inventory_hostname }} ({{ city }}) successfully deployed!"
|
||||||
68
ansible/nginx-cdn-germany/playbooks/manage-cdn.yml
Normal file
68
ansible/nginx-cdn-germany/playbooks/manage-cdn.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
# CDN Management Tasks
|
||||||
|
- name: CDN Management and Maintenance
|
||||||
|
hosts: cdn_nodes
|
||||||
|
become: yes
|
||||||
|
vars_prompt:
|
||||||
|
- name: action
|
||||||
|
prompt: "What action? (purge-cache/reload-config/check-health/view-stats/warm-cache)"
|
||||||
|
private: no
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Purge all cache
|
||||||
|
shell: find /var/cache/nginx/ -type f -delete
|
||||||
|
when: action == "purge-cache"
|
||||||
|
|
||||||
|
- name: Display cache purge result
|
||||||
|
debug:
|
||||||
|
msg: "✅ Cache purged on {{ inventory_hostname }}"
|
||||||
|
when: action == "purge-cache"
|
||||||
|
|
||||||
|
- name: Reload nginx configuration
|
||||||
|
systemd:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
||||||
|
when: action == "reload-config"
|
||||||
|
|
||||||
|
- name: Check CDN health
|
||||||
|
uri:
|
||||||
|
url: "https://{{ inventory_hostname }}/health"
|
||||||
|
method: GET
|
||||||
|
status_code: 200
|
||||||
|
register: health_result
|
||||||
|
when: action == "check-health"
|
||||||
|
|
||||||
|
- name: Display health result
|
||||||
|
debug:
|
||||||
|
msg: "{{ health_result.content }}"
|
||||||
|
when: action == "check-health"
|
||||||
|
|
||||||
|
- name: Show cache and system statistics
|
||||||
|
shell: |
|
||||||
|
echo "=== Cache Size ==="
|
||||||
|
du -sh /var/cache/nginx/
|
||||||
|
echo "=== Cache Files ==="
|
||||||
|
find /var/cache/nginx/ -type f | wc -l
|
||||||
|
echo "=== System Load ==="
|
||||||
|
uptime
|
||||||
|
echo "=== Memory Usage ==="
|
||||||
|
free -h
|
||||||
|
echo "=== Disk Usage ==="
|
||||||
|
df -h /
|
||||||
|
register: stats_result
|
||||||
|
when: action == "view-stats"
|
||||||
|
|
||||||
|
- name: Display statistics
|
||||||
|
debug:
|
||||||
|
msg: "{{ stats_result.stdout_lines }}"
|
||||||
|
when: action == "view-stats"
|
||||||
|
|
||||||
|
- name: Warm cache with popular URLs
|
||||||
|
uri:
|
||||||
|
url: "https://{{ inventory_hostname }}{{ item }}"
|
||||||
|
method: GET
|
||||||
|
loop:
|
||||||
|
- "/"
|
||||||
|
- "/health"
|
||||||
|
ignore_errors: yes
|
||||||
|
when: action == "warm-cache"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
- name: reload nginx
|
||||||
|
systemd:
|
||||||
|
name: nginx
|
||||||
|
state: reloaded
|
||||||
|
|
||||||
|
- name: restart nginx
|
||||||
|
systemd:
|
||||||
|
name: nginx
|
||||||
|
state: restarted
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
# Nginx CDN Konfiguration
|
||||||
|
|
||||||
|
- name: Remove default nginx config
|
||||||
|
file:
|
||||||
|
path: /etc/nginx/sites-enabled/default
|
||||||
|
state: absent
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Create nginx directories
|
||||||
|
file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
owner: www-data
|
||||||
|
group: www-data
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- /var/cache/nginx/static
|
||||||
|
- /var/cache/nginx/images
|
||||||
|
- /var/cache/nginx/html
|
||||||
|
- /var/log/nginx/cdn
|
||||||
|
- /etc/nginx/includes
|
||||||
|
|
||||||
|
- name: Configure nginx main config
|
||||||
|
template:
|
||||||
|
src: nginx.conf.j2
|
||||||
|
dest: /etc/nginx/nginx.conf
|
||||||
|
backup: yes
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Create nginx includes
|
||||||
|
template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: "/etc/nginx/includes/{{ item }}"
|
||||||
|
loop:
|
||||||
|
- security-headers.conf
|
||||||
|
- rate-limiting.conf
|
||||||
|
- gzip-settings.conf
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Configure CDN site
|
||||||
|
template:
|
||||||
|
src: cdn-site.conf.j2
|
||||||
|
dest: /etc/nginx/sites-available/cdn
|
||||||
|
backup: yes
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Enable CDN site
|
||||||
|
file:
|
||||||
|
src: /etc/nginx/sites-available/cdn
|
||||||
|
dest: /etc/nginx/sites-enabled/cdn
|
||||||
|
state: link
|
||||||
|
notify: reload nginx
|
||||||
|
|
||||||
|
- name: Test nginx configuration
|
||||||
|
command: nginx -t
|
||||||
|
register: nginx_test
|
||||||
|
failed_when: nginx_test.rc != 0
|
||||||
|
|
||||||
|
- name: Start and enable nginx
|
||||||
|
systemd:
|
||||||
|
name: nginx
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name {{ cdn_domain }};
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name {{ cdn_domain }};
|
||||||
|
|
||||||
|
# SSL Configuration (wird von Certbot automatisch gefüllt)
|
||||||
|
ssl_certificate /etc/letsencrypt/live/{{ cdn_domain }}/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/{{ cdn_domain }}/privkey.pem;
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||||
|
|
||||||
|
# CDN Headers
|
||||||
|
add_header X-CDN-Node "{{ city }}, Deutschland";
|
||||||
|
add_header X-Cache-Status $upstream_cache_status always;
|
||||||
|
add_header X-Served-By "nginx-{{ inventory_hostname }}";
|
||||||
|
|
||||||
|
# Security Headers
|
||||||
|
include /etc/nginx/includes/security-headers.conf;
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
access_log /var/log/nginx/cdn/{{ cdn_domain }}.access.log cdn_format;
|
||||||
|
error_log /var/log/nginx/cdn/{{ cdn_domain }}.error.log warn;
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
include /etc/nginx/includes/rate-limiting.conf;
|
||||||
|
|
||||||
|
# Gzip Compression
|
||||||
|
include /etc/nginx/includes/gzip-settings.conf;
|
||||||
|
|
||||||
|
# Nginx Status für Monitoring
|
||||||
|
location /nginx_status {
|
||||||
|
stub_status on;
|
||||||
|
access_log off;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Static Files (CSS, JS, Fonts) - Lange Cache-Zeit
|
||||||
|
##
|
||||||
|
location ~* \.(css|js|woff|woff2|ttf|eot|ico)$ {
|
||||||
|
proxy_pass https://origin_servers;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
# Cache Settings
|
||||||
|
proxy_cache static_cache;
|
||||||
|
proxy_cache_valid 200 302 {{ cache_settings.static_files_ttl }};
|
||||||
|
proxy_cache_valid 404 1m;
|
||||||
|
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||||
|
proxy_cache_lock on;
|
||||||
|
proxy_cache_revalidate on;
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
expires {{ cache_settings.static_files_ttl }};
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header Vary "Accept-Encoding";
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
limit_req zone=static_files burst=50 nodelay;
|
||||||
|
|
||||||
|
# Proxy Headers
|
||||||
|
proxy_set_header Host {{ origin_domain }};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Images - Mittlere Cache-Zeit
|
||||||
|
##
|
||||||
|
location ~* \.(jpg|jpeg|png|gif|webp|svg|avif|bmp|tiff)$ {
|
||||||
|
proxy_pass https://origin_servers;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
# Cache Settings
|
||||||
|
proxy_cache images_cache;
|
||||||
|
proxy_cache_valid 200 302 {{ cache_settings.images_ttl }};
|
||||||
|
proxy_cache_valid 404 5m;
|
||||||
|
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
expires {{ cache_settings.images_ttl }};
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
add_header Vary "Accept";
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
limit_req zone=images burst=30 nodelay;
|
||||||
|
|
||||||
|
# Proxy Headers
|
||||||
|
proxy_set_header Host {{ origin_domain }};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# API Endpoints - Kein Caching
|
||||||
|
##
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass https://origin_servers;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
# Kein Caching
|
||||||
|
proxy_no_cache 1;
|
||||||
|
proxy_cache_bypass 1;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
add_header Expires "0";
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
limit_req zone=api burst=10 nodelay;
|
||||||
|
|
||||||
|
# CORS Headers
|
||||||
|
add_header Access-Control-Allow-Origin "https://{{ origin_domain }}";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With";
|
||||||
|
add_header Access-Control-Allow-Credentials true;
|
||||||
|
|
||||||
|
# Proxy Headers
|
||||||
|
proxy_set_header Host {{ origin_domain }};
|
||||||
|
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;
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
proxy_connect_timeout {{ proxy_settings.connect_timeout }};
|
||||||
|
proxy_send_timeout {{ proxy_settings.send_timeout }};
|
||||||
|
proxy_read_timeout {{ proxy_settings.read_timeout }};
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Cache Purge (nur interne IPs)
|
||||||
|
##
|
||||||
|
location ~ /purge(/.*) {
|
||||||
|
allow 127.0.0.1;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
allow 192.168.0.0/16;
|
||||||
|
deny all;
|
||||||
|
|
||||||
|
proxy_cache_purge static_cache $scheme$proxy_host$1;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Health Check
|
||||||
|
##
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "OK - CDN Node {{ city }}\nRegion: {{ region }}\nTier: {{ tier }}\nTimestamp: $time_iso8601\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Default Location - HTML Caching
|
||||||
|
##
|
||||||
|
location / {
|
||||||
|
proxy_pass https://origin_servers;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
# Cache Settings für HTML
|
||||||
|
proxy_cache html_cache;
|
||||||
|
proxy_cache_valid 200 {{ cache_settings.html_ttl }};
|
||||||
|
proxy_cache_valid 404 1m;
|
||||||
|
proxy_cache_bypass $arg_nocache $cookie_sessionid $http_authorization;
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
add_header Cache-Control "public, max-age=300";
|
||||||
|
|
||||||
|
# Proxy Headers
|
||||||
|
proxy_set_header Host {{ origin_domain }};
|
||||||
|
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_set_header X-CDN-Node "{{ city }}";
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
proxy_connect_timeout {{ proxy_settings.connect_timeout }};
|
||||||
|
proxy_send_timeout {{ proxy_settings.send_timeout }};
|
||||||
|
proxy_read_timeout {{ proxy_settings.read_timeout }};
|
||||||
|
|
||||||
|
# Buffering
|
||||||
|
proxy_buffering {{ proxy_settings.buffering }};
|
||||||
|
proxy_buffer_size {{ proxy_settings.buffer_size }};
|
||||||
|
proxy_buffers {{ proxy_settings.buffers }};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Gzip Compression Settings
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_disable "msie6";
|
||||||
|
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/atom+xml
|
||||||
|
image/svg+xml
|
||||||
|
application/json
|
||||||
|
application/ld+json;
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
user www-data;
|
||||||
|
worker_processes {{ nginx_worker_processes }};
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections {{ nginx_worker_connections }};
|
||||||
|
use epoll;
|
||||||
|
multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
##
|
||||||
|
# Basic Settings
|
||||||
|
##
|
||||||
|
sendfile {{ tcp_optimizations.sendfile }};
|
||||||
|
tcp_nopush {{ tcp_optimizations.tcp_nopush }};
|
||||||
|
tcp_nodelay {{ tcp_optimizations.tcp_nodelay }};
|
||||||
|
keepalive_timeout {{ nginx_keepalive_timeout }};
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
server_names_hash_bucket_size 64;
|
||||||
|
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
##
|
||||||
|
# DSGVO-konforme Logging
|
||||||
|
##
|
||||||
|
map $remote_addr $anonymized_ip {
|
||||||
|
~(?P<ip>\d+\.\d+\.\d+)\.\d+ $ip.0;
|
||||||
|
~(?P<ipv6>[^:]+:[^:]+:[^:]+:[^:]+):.* $ipv6::;
|
||||||
|
default 0.0.0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_format cdn_format '$anonymized_ip - $remote_user [$time_local] '
|
||||||
|
'"$request" $status $body_bytes_sent '
|
||||||
|
'"$http_referer" "$http_user_agent" '
|
||||||
|
'rt=$request_time '
|
||||||
|
'cache="$upstream_cache_status" '
|
||||||
|
'cdn_node="{{ inventory_hostname }}"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log cdn_format;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Cache Paths
|
||||||
|
##
|
||||||
|
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:100m
|
||||||
|
max_size={{ cache_size }} inactive=7d use_temp_path=off;
|
||||||
|
proxy_cache_path /var/cache/nginx/images levels=1:2 keys_zone=images_cache:100m
|
||||||
|
max_size={{ cache_size }} inactive=30d use_temp_path=off;
|
||||||
|
proxy_cache_path /var/cache/nginx/html levels=1:2 keys_zone=html_cache:50m
|
||||||
|
max_size=5g inactive=1h use_temp_path=off;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Upstream zu Origin-Servern
|
||||||
|
##
|
||||||
|
upstream origin_servers {
|
||||||
|
{% for host in groups['origin_servers'] %}
|
||||||
|
server {{ hostvars[host]['ansible_default_ipv4']['address'] }}:443
|
||||||
|
weight=1 max_fails=3 fail_timeout=30s;
|
||||||
|
{% endfor %}
|
||||||
|
keepalive 32;
|
||||||
|
keepalive_requests 1000;
|
||||||
|
keepalive_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
# Include configurations
|
||||||
|
##
|
||||||
|
include /etc/nginx/includes/*.conf;
|
||||||
|
include /etc/nginx/sites-enabled/*;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Rate Limiting Zones
|
||||||
|
|
||||||
|
limit_req_zone $binary_remote_addr zone=api:10m rate={{ rate_limits.api }};
|
||||||
|
limit_req_zone $binary_remote_addr zone=static_files:10m rate={{ rate_limits.static }};
|
||||||
|
limit_req_zone $binary_remote_addr zone=images:10m rate={{ rate_limits.images }};
|
||||||
|
|
||||||
|
# Connection Limiting
|
||||||
|
limit_conn_zone $binary_remote_addr zone=perip:10m;
|
||||||
|
limit_conn perip 10;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Security Headers für CDN
|
||||||
|
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
|
||||||
|
# DSGVO Header
|
||||||
|
add_header X-Data-Processing "Art. 6 Abs. 1 lit. f DSGVO" always;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
# Einfaches Monitoring ohne Prometheus
|
||||||
|
|
||||||
|
- name: Create simple monitoring script
|
||||||
|
template:
|
||||||
|
src: simple-monitor.sh.j2
|
||||||
|
dest: /usr/local/bin/cdn-monitor
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Setup monitoring cron job
|
||||||
|
cron:
|
||||||
|
name: "CDN Health Monitor"
|
||||||
|
minute: "*/5"
|
||||||
|
job: "/usr/local/bin/cdn-monitor"
|
||||||
|
user: root
|
||||||
|
|
||||||
|
- name: Create log rotation for monitoring logs
|
||||||
|
copy:
|
||||||
|
content: |
|
||||||
|
/var/log/nginx/cdn-monitor.log {
|
||||||
|
weekly
|
||||||
|
missingok
|
||||||
|
rotate 4
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
}
|
||||||
|
dest: /etc/logrotate.d/cdn-monitor
|
||||||
|
mode: '0644'
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Einfaches CDN Monitoring für {{ inventory_hostname }}
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/nginx/cdn-monitor.log"
|
||||||
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
CDN_DOMAIN="{{ cdn_domain }}"
|
||||||
|
|
||||||
|
# Health Check
|
||||||
|
health_check() {
|
||||||
|
local response=$(curl -s -o /dev/null -w "%{http_code}" "https://$CDN_DOMAIN/health")
|
||||||
|
|
||||||
|
if [ "$response" = "200" ]; then
|
||||||
|
echo "[$TIMESTAMP] ✅ Health check OK" >> $LOG_FILE
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "[$TIMESTAMP] ❌ Health check FAILED (HTTP $response)" >> $LOG_FILE
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nginx-Statistiken
|
||||||
|
nginx_stats() {
|
||||||
|
local stats=$(curl -s http://127.0.0.1/nginx_status 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
local active_conn=$(echo "$stats" | grep "Active connections" | awk '{print $3}')
|
||||||
|
local total_requests=$(echo "$stats" | grep "server accepts" | awk '{print $3}')
|
||||||
|
echo "[$TIMESTAMP] 📊 Active: $active_conn, Total: $total_requests" >> $LOG_FILE
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache-Größe prüfen
|
||||||
|
cache_check() {
|
||||||
|
local cache_size=$(du -sh /var/cache/nginx/ 2>/dev/null | cut -f1)
|
||||||
|
local cache_files=$(find /var/cache/nginx/ -type f 2>/dev/null | wc -l)
|
||||||
|
echo "[$TIMESTAMP] 💾 Cache: $cache_size ($cache_files files)" >> $LOG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
# System-Ressourcen
|
||||||
|
system_check() {
|
||||||
|
local load=$(uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ')
|
||||||
|
local memory=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}')
|
||||||
|
local disk=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] 🖥️ Load: $load, Memory: ${memory}%, Disk: ${disk}%" >> $LOG_FILE
|
||||||
|
|
||||||
|
# Warnungen bei hoher Auslastung
|
||||||
|
if (( $(echo "$load > 5.0" | bc -l 2>/dev/null || echo 0) )); then
|
||||||
|
echo "[$TIMESTAMP] ⚠️ HIGH LOAD WARNING: $load" >> $LOG_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( $(echo "$memory > 90.0" | bc -l 2>/dev/null || echo 0) )); then
|
||||||
|
echo "[$TIMESTAMP] ⚠️ HIGH MEMORY WARNING: ${memory}%" >> $LOG_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$disk" -gt 85 ]; then
|
||||||
|
echo "[$TIMESTAMP] ⚠️ HIGH DISK USAGE WARNING: ${disk}%" >> $LOG_FILE
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Hauptausführung
|
||||||
|
main() {
|
||||||
|
health_check
|
||||||
|
nginx_stats
|
||||||
|
cache_check
|
||||||
|
system_check
|
||||||
|
|
||||||
|
# Log-Datei begrenzen (nur letzte 1000 Zeilen behalten)
|
||||||
|
tail -n 1000 $LOG_FILE > ${LOG_FILE}.tmp && mv ${LOG_FILE}.tmp $LOG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
# SSL-Zertifikate mit Let's Encrypt
|
||||||
|
|
||||||
|
- name: Check if certificate exists
|
||||||
|
stat:
|
||||||
|
path: "/etc/letsencrypt/live/{{ cdn_domain }}/fullchain.pem"
|
||||||
|
register: cert_exists
|
||||||
|
|
||||||
|
- name: Generate SSL certificate with certbot
|
||||||
|
command: >
|
||||||
|
certbot certonly --nginx
|
||||||
|
-d {{ cdn_domain }}
|
||||||
|
--non-interactive
|
||||||
|
--agree-tos
|
||||||
|
--email {{ ssl_email }}
|
||||||
|
when: not cert_exists.stat.exists
|
||||||
|
|
||||||
|
- name: Setup SSL certificate renewal
|
||||||
|
cron:
|
||||||
|
name: "Renew SSL certificates"
|
||||||
|
minute: "0"
|
||||||
|
hour: "3"
|
||||||
|
job: "certbot renew --quiet --deploy-hook 'systemctl reload nginx'"
|
||||||
|
user: root
|
||||||
|
|
||||||
|
- name: Test SSL certificate renewal (dry-run)
|
||||||
|
command: certbot renew --dry-run
|
||||||
|
register: renewal_test
|
||||||
|
failed_when: renewal_test.rc != 0
|
||||||
|
changed_when: false
|
||||||
44
ansible/nginx-cdn-germany/scripts/deploy.sh
Normal file
44
ansible/nginx-cdn-germany/scripts/deploy.sh
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Simple CDN Deployment Script
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
INVENTORY_FILE="inventories/production/hosts.yml"
|
||||||
|
PLAYBOOK="playbooks/deploy-simple-cdn.yml"
|
||||||
|
|
||||||
|
echo "🚀 Starting Simple CDN Deployment for Germany..."
|
||||||
|
|
||||||
|
# Pre-deployment checks
|
||||||
|
echo "🔍 Running pre-deployment checks..."
|
||||||
|
if ! ansible all -i $INVENTORY_FILE -m ping; then
|
||||||
|
echo "❌ Some hosts are not reachable. Please check your inventory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📋 Testing ansible configuration..."
|
||||||
|
if ! ansible-playbook $PLAYBOOK -i $INVENTORY_FILE --check --diff; then
|
||||||
|
echo "❌ Configuration test failed. Please fix errors first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Continue with deployment? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Deployment cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
echo "🔧 Deploying CDN nodes..."
|
||||||
|
ansible-playbook $PLAYBOOK -i $INVENTORY_FILE
|
||||||
|
|
||||||
|
# Post-deployment verification
|
||||||
|
echo "✅ Verifying deployment..."
|
||||||
|
ansible cdn_nodes -i $INVENTORY_FILE -m uri -a "url=https://{{ inventory_hostname }}/health method=GET status_code=200"
|
||||||
|
|
||||||
|
echo "🎉 CDN Deployment completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Update your DNS to point to the CDN nodes"
|
||||||
|
echo "2. Test your CDN: curl -I https://your-cdn-domain.de/health"
|
||||||
|
echo "3. Monitor with: ansible-playbook -i $INVENTORY_FILE playbooks/manage-cdn.yml"
|
||||||
125
ansible/nginx-cdn-germany/scripts/ssh-keys.sh
Normal file
125
ansible/nginx-cdn-germany/scripts/ssh-keys.sh
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SSH-Schlüssel Management für CDN
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
echo "CDN SSH Key Management"
|
||||||
|
echo ""
|
||||||
|
echo "Usage: $0 [OPTION]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " single - Ein Schlüssel für alle Nodes (Standard)"
|
||||||
|
echo " individual - Separater Schlüssel pro Node"
|
||||||
|
echo " grouped - Gruppierte Schlüssel (Primary/Secondary)"
|
||||||
|
echo " generate - SSH-Schlüssel generieren"
|
||||||
|
echo " deploy - Öffentliche Schlüssel zu Servern kopieren"
|
||||||
|
echo " help - Diese Hilfe anzeigen"
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_single_key() {
|
||||||
|
echo "🔑 Generiere einen SSH-Schlüssel für alle CDN-Nodes..."
|
||||||
|
|
||||||
|
if [ ! -f ~/.ssh/cdn_key ]; then
|
||||||
|
ssh-keygen -t ed25519 -C "cdn-deployment" -f ~/.ssh/cdn_key -N ""
|
||||||
|
echo "✅ Schlüssel generiert: ~/.ssh/cdn_key"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Schlüssel existiert bereits: ~/.ssh/cdn_key"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Inventar anpassen
|
||||||
|
sed -i 's|ansible_ssh_private_key_file: .*|ansible_ssh_private_key_file: ~/.ssh/cdn_key|' \
|
||||||
|
"$SCRIPT_DIR/../inventories/production/hosts.yml"
|
||||||
|
|
||||||
|
echo "✅ Inventar aktualisiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_individual_keys() {
|
||||||
|
echo "🔑 Generiere individuelle SSH-Schlüssel..."
|
||||||
|
|
||||||
|
NODES=("cdn_fra1" "cdn_ham1" "cdn_muc1" "origin1" "origin2")
|
||||||
|
|
||||||
|
for node in "${NODES[@]}"; do
|
||||||
|
if [ ! -f ~/.ssh/${node}_key ]; then
|
||||||
|
ssh-keygen -t ed25519 -C "cdn-${node}" -f ~/.ssh/${node}_key -N ""
|
||||||
|
echo "✅ Schlüssel generiert: ~/.ssh/${node}_key"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Schlüssel existiert bereits: ~/.ssh/${node}_key"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Alle individuellen Schlüssel generiert"
|
||||||
|
echo "💡 Verwende: cp inventories/production/hosts-individual-keys.yml.example inventories/production/hosts.yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_grouped_keys() {
|
||||||
|
echo "🔑 Generiere gruppierte SSH-Schlüssel..."
|
||||||
|
|
||||||
|
GROUPS=("origin_servers" "cdn_primary" "cdn_secondary")
|
||||||
|
|
||||||
|
for group in "${GROUPS[@]}"; do
|
||||||
|
if [ ! -f ~/.ssh/${group}_key ]; then
|
||||||
|
ssh-keygen -t ed25519 -C "cdn-${group}" -f ~/.ssh/${group}_key -N ""
|
||||||
|
echo "✅ Schlüssel generiert: ~/.ssh/${group}_key"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Schlüssel existiert bereits: ~/.ssh/${group}_key"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Alle gruppierten Schlüssel generiert"
|
||||||
|
echo "💡 Verwende: cp inventories/production/hosts-grouped-keys.yml.example inventories/production/hosts.yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy_keys() {
|
||||||
|
echo "🚀 Deploye öffentliche Schlüssel zu den Servern..."
|
||||||
|
|
||||||
|
# Lese IPs aus dem Inventar
|
||||||
|
IPS=$(grep "ansible_host:" "$SCRIPT_DIR/../inventories/production/hosts.yml" | awk '{print $2}' | sort | uniq)
|
||||||
|
|
||||||
|
for ip in $IPS; do
|
||||||
|
echo "Deploying to $ip..."
|
||||||
|
|
||||||
|
# Versuche verschiedene Schlüssel
|
||||||
|
for key in ~/.ssh/*_key ~/.ssh/cdn_key ~/.ssh/id_rsa; do
|
||||||
|
if [ -f "$key" ]; then
|
||||||
|
echo " Versuche Schlüssel: $key"
|
||||||
|
if ssh-copy-id -i "${key}.pub" "root@$ip" 2>/dev/null; then
|
||||||
|
echo " ✅ Erfolgreich: $key -> $ip"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
"single")
|
||||||
|
generate_single_key
|
||||||
|
;;
|
||||||
|
"individual")
|
||||||
|
generate_individual_keys
|
||||||
|
;;
|
||||||
|
"grouped")
|
||||||
|
generate_grouped_keys
|
||||||
|
;;
|
||||||
|
"generate")
|
||||||
|
echo "Welche Art von Schlüsseln?"
|
||||||
|
echo "1) Ein Schlüssel für alle (empfohlen für Start)"
|
||||||
|
echo "2) Individuelle Schlüssel pro Node (sicherste)"
|
||||||
|
echo "3) Gruppierte Schlüssel (Kompromiss)"
|
||||||
|
read -p "Wähle (1-3): " choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) generate_single_key ;;
|
||||||
|
2) generate_individual_keys ;;
|
||||||
|
3) generate_grouped_keys ;;
|
||||||
|
*) echo "Ungültige Auswahl" ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"deploy")
|
||||||
|
deploy_keys
|
||||||
|
;;
|
||||||
|
"help"|*)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
37
ansible/nginx-cdn-germany/scripts/warm-cache.sh
Normal file
37
ansible/nginx-cdn-germany/scripts/warm-cache.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Cache Warming Script
|
||||||
|
|
||||||
|
INVENTORY_FILE="inventories/production/hosts.yml"
|
||||||
|
|
||||||
|
# URLs zum Cache-Warming
|
||||||
|
URLS=(
|
||||||
|
"/"
|
||||||
|
"/health"
|
||||||
|
# Füge hier deine wichtigsten URLs hinzu:
|
||||||
|
# "/css/main.css"
|
||||||
|
# "/js/app.js"
|
||||||
|
# "/images/logo.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "🔥 Starting cache warming for all CDN nodes..."
|
||||||
|
|
||||||
|
# Hole alle CDN Node Hostnamen
|
||||||
|
CDN_NODES=$(ansible-inventory -i $INVENTORY_FILE --list | jq -r '.cdn_nodes.hosts[]' 2>/dev/null || ansible cdn_nodes -i $INVENTORY_FILE --list-hosts | grep -v hosts)
|
||||||
|
|
||||||
|
for node in $CDN_NODES; do
|
||||||
|
echo "Warming cache for: $node"
|
||||||
|
|
||||||
|
for url in "${URLS[@]}"; do
|
||||||
|
echo " Warming: $url"
|
||||||
|
response=$(curl -s -o /dev/null -w "%{http_code}" "https://${node}${url}" || echo "000")
|
||||||
|
if [ "$response" = "200" ]; then
|
||||||
|
echo " ✅ OK"
|
||||||
|
else
|
||||||
|
echo " ❌ Failed (HTTP $response)"
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "🎉 Cache warming completed!"
|
||||||
Reference in New Issue
Block a user