chore: complete update
This commit is contained in:
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"
|
||||
Reference in New Issue
Block a user