1107 lines
34 KiB
Markdown
1107 lines
34 KiB
Markdown
# Install.sh - Umfassende Dokumentation
|
|
|
|
**Datei:** `install.sh` (695 Zeilen)
|
|
**Zweck:** Vollautomatische Installation und Konfiguration des Container Spawner Systems
|
|
**Kompatibilität:** Bash, BusyBox (Synology NAS), Docker ≥ 20.10, Docker Compose ≥ 2.0
|
|
|
|
---
|
|
|
|
## Übersicht der Phasen
|
|
|
|
Das Script läuft **11 sequenzielle Phasen** durch:
|
|
|
|
1. **Startup & Konfiguration** - Logging, Farben, Mindestversionen definieren
|
|
2. **.env Prüfung** - Konfigurationsdatei vorhanden?
|
|
3. **Voraussetzungen prüfen** - Docker, Compose, Git Versionen validieren
|
|
4. **Update vs. Neuinstallation** - Git Repository klonen oder aktualisieren
|
|
5. **Verzeichnisse & Rechte** - data/, logs/ erstellen, Berechtigungen setzen
|
|
6. **Docker-Netzwerk** - Traefik-Netzwerk erstellen/prüfen
|
|
7. **Traefik Prüfung** - Ist Traefik-Container laufend?
|
|
8. **Docker Images bauen** - User-Templates und Spawner-Images kompilieren
|
|
9. **Container starten** - Docker Compose up, Health-Checks
|
|
10. **Fertig-Nachricht** - URLs anzeigen, nützliche Befehle listen
|
|
|
|
---
|
|
|
|
## Phase 1: Startup & Konfiguration (Zeilen 1-25)
|
|
|
|
### Zweck
|
|
Initialisierung des Scripts, Definition globaler Variablen und Logging-Setup.
|
|
|
|
### Zeilen 1-2: Shebang & Error Handling
|
|
```bash
|
|
#!/bin/bash
|
|
set -e
|
|
```
|
|
- `#!/bin/bash` - Bash-Interpreter aufrufen (nicht sh/dash/ash)
|
|
- `set -e` - **KRITISCH**: Beende Script sofort bei erstem Fehler (exit code ≠ 0)
|
|
- Verhindert, dass fehlerhafte Befehle ignoriert werden und weitere Schritte ausgeführt werden
|
|
|
|
### Zeilen 4-13: Repositoriums- & Installationsvariablen
|
|
```bash
|
|
REPO_URL="https://gitea.iotxs.de/RainerWieland/spawner.git"
|
|
RAW_URL="https://gitea.iotxs.de/RainerWieland/spawner/raw/branch/main"
|
|
INSTALL_DIR="${PWD}"
|
|
VERSION="0.1.0"
|
|
LOG_FILE="${INSTALL_DIR}/spawner-install.log"
|
|
```
|
|
- `REPO_URL` - Git Repository für klonen/pull (https, kein SSH)
|
|
- `RAW_URL` - Rohe Datei-Downloads (für .env.example)
|
|
- `INSTALL_DIR="${PWD}"` - **WICHTIG**: Installiert im aktuellen Verzeichnis (not hardcoded)
|
|
- `VERSION` - Script-Version (für Logs)
|
|
- `LOG_FILE` - Alle Build-Logs werden hier gesammelt
|
|
|
|
**Voraussetzungen:**
|
|
- Bash >= 4.0
|
|
- PWD muss beschreibbar sein (chmod 755 mindestens)
|
|
|
|
### Zeilen 15-20: Farben für Terminal-Output
|
|
```bash
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
```
|
|
- ANSI Escape Codes für farbige Konsolen-Ausgabe
|
|
- Hilft bei Fehler/Success-Anzeige
|
|
- Kompatibel mit BusyBox (einfache Codes)
|
|
|
|
### Zeilen 22-24: Mindestversionen
|
|
```bash
|
|
MIN_DOCKER_VERSION="20.10"
|
|
MIN_COMPOSE_VERSION="2.0"
|
|
```
|
|
- Definieren Minimum-Versionen für Voraussetzungs-Check
|
|
- Docker < 20.10: Kein Docker Compose v2 Support
|
|
- Compose < 2.0: Alte `docker-compose` CLI (veraltet)
|
|
|
|
**Voraussetzungen:**
|
|
- Docker 20.10+ installiert
|
|
- Docker Compose 2.0+ installiert (integriert in Docker Desktop oder separat)
|
|
|
|
---
|
|
|
|
## Phase 2: Version-Vergleich Hilfsfunktion (Zeilen 30-72)
|
|
|
|
### Zweck
|
|
BusyBox-kompatible Versionierung (Synology NAS nutzt BusyBox, kein `sort -V` vorhanden).
|
|
|
|
### Zeilen 30-35: Funktions-Definition
|
|
```bash
|
|
version_gte() {
|
|
local ver1="$1"
|
|
local ver2="$2"
|
|
```
|
|
- `version_gte "20.10.21" "20.10"` → return 0 (true, >= erfüllt)
|
|
- `version_gte "20.0" "20.10"` → return 1 (false, < Minimum)
|
|
- **Warum**: `sort -V` existiert in BusyBox nicht
|
|
|
|
### Zeilen 40-50: Major/Minor/Patch Parsing
|
|
```bash
|
|
v1_major=$(echo "$ver1" | cut -d. -f1)
|
|
v1_minor=$(echo "$ver1" | cut -d. -f2)
|
|
v1_patch=$(echo "$ver1" | cut -d. -f3)
|
|
```
|
|
- Teile Version "20.10.21" auf:
|
|
- v1_major=20, v1_minor=10, v1_patch=21
|
|
- Verwende 0 als Default wenn Feld fehlt (`${v1_minor:-0}`)
|
|
|
|
### Zeilen 52-71: Vergleich Logic
|
|
1. **Major vergleichen** (Zeile 53-57)
|
|
- Wenn v1_major > v2_major → return 0 (erfüllt)
|
|
- Wenn v1_major < v2_major → return 1 (nicht erfüllt)
|
|
- Wenn gleich → weiter zu Minor
|
|
|
|
2. **Minor vergleichen** (Zeile 60-63)
|
|
- Analog Major-Vergleich
|
|
|
|
3. **Patch vergleichen** (Zeile 67-70)
|
|
- `version_patch >= version_patch` → return 0
|
|
|
|
**Beispiele:**
|
|
- `version_gte "20.10.21" "20.10"` → 20==20, 10==10, 21>=0 → **TRUE**
|
|
- `version_gte "20.9.0" "20.10"` → 20==20, 9<10 → **FALSE**
|
|
- `version_gte "21.0" "20.10"` → 21>20 → **TRUE**
|
|
|
|
**Voraussetzungen:**
|
|
- cut, echo Commands vorhanden (in BusyBox)
|
|
|
|
---
|
|
|
|
## Phase 3: .env Datei Prüfung (Zeilen 74-115)
|
|
|
|
### Zweck
|
|
Prüfe ob .env existiert. Wenn nicht → Download .env.example und beende Script.
|
|
|
|
### Zeilen 74-82: Willkommens-Banner & Log-Init
|
|
```bash
|
|
echo "============================================================"
|
|
echo " Container Spawner Installation v${VERSION}"
|
|
echo "============================================================"
|
|
```
|
|
- Zeige Script-Version an
|
|
- Initialisiere Log-Datei mit Timestamp
|
|
|
|
### Zeilen 87-100: .env Existenz-Check
|
|
```bash
|
|
if [ ! -f "${INSTALL_DIR}/.env" ]; then
|
|
echo "Lade .env.example herunter..."
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -sSL "${RAW_URL}/.env.example" -o "${INSTALL_DIR}/.env.example"
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget -q "${RAW_URL}/.env.example" -O "${INSTALL_DIR}/.env.example"
|
|
```
|
|
|
|
**Logik:**
|
|
- Existiert `.env` NICHT?
|
|
1. Versuche `.env.example` herunterzuladen (über curl oder wget)
|
|
2. Speichere in `${INSTALL_DIR}/.env.example`
|
|
3. **Stoppe Script mit exit 0** → Zeige Anleitung zur manuellen Konfiguration
|
|
|
|
**Warum .env nicht automatisch kopieren?**
|
|
- `.env` enthält Secrets (SECRET_KEY, SMTP_PASSWORD etc.)
|
|
- Admin muss Werte bewusst setzen (BASE_DOMAIN, TRAEFIK_NETWORK etc.)
|
|
- Verhindert Sicherheitslücken durch Auto-Configuration
|
|
|
|
### Zeilen 104-111: Konfigurationsanleitung
|
|
```bash
|
|
echo "Naechste Schritte:"
|
|
echo " 1. Kopiere die Vorlage: cp .env.example .env"
|
|
echo " 2. Passe die Werte an: nano .env"
|
|
echo " 3. Fuehre erneut aus: bash install.sh"
|
|
```
|
|
|
|
**Voraussetzungen:**
|
|
- curl ODER wget installiert
|
|
- Internet-Zugriff zu Gitea-Server
|
|
- `.env.example` im Repository vorhanden
|
|
|
|
---
|
|
|
|
## Phase 4: Voraussetzungen Prüfung (Zeilen 122-185)
|
|
|
|
### Zweck
|
|
Validiere dass alle erforderlichen Tools verfügbar und aktuelle Versionen sind.
|
|
|
|
### Zeilen 128-149: Docker Version Check
|
|
```bash
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo -e "${RED}Fehler: Docker nicht gefunden!${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' 2>/dev/null || \
|
|
docker version 2>/dev/null | grep -i "version" | head -1 | sed 's/.*version[: ]*\([0-9.]*\).*/\1/')
|
|
```
|
|
|
|
**Logic:**
|
|
1. Prüfe ob `docker` command existiert
|
|
- `command -v` ist POSIX-kompatibel (funktioniert auch in BusyBox/ash)
|
|
2. Extrahiere Docker Server-Version:
|
|
- Versuche `docker version --format` (moderne Syntax)
|
|
- Fallback zu regex parsing (für ältere Docker Versionen)
|
|
3. Vergleiche gegen MIN_DOCKER_VERSION="20.10" mit `version_gte()` Funktion
|
|
|
|
**Fehlerbehandlung:**
|
|
```bash
|
|
if [ -z "$DOCKER_VERSION" ]; then
|
|
echo "Version unbekannt (OK, mit Warnung fortfahren)"
|
|
elif version_gte "$DOCKER_VERSION" "$MIN_DOCKER_VERSION"; then
|
|
echo "OK (v${DOCKER_VERSION})"
|
|
else
|
|
echo "FEHLER: Version ${DOCKER_VERSION} zu alt"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
**Voraussetzungen:**
|
|
- `docker` executable vorhanden (PATH)
|
|
- Docker Server läuft (für version --format)
|
|
|
|
### Zeilen 151-178: Docker Compose Check
|
|
```bash
|
|
if docker compose version >/dev/null 2>&1; then
|
|
COMPOSE_CMD="docker compose" # Neue Syntax (v2)
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
COMPOSE_CMD="docker-compose" # Alte Syntax (v1)
|
|
else
|
|
echo -e "${RED}Fehler: Docker Compose nicht gefunden!${NC}"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
**Logic:**
|
|
1. Prüfe ob `docker compose` vorhanden (Docker Desktop oder separate Installation)
|
|
2. Fallback zu `docker-compose` (veraltet aber noch unterstützt)
|
|
3. Speichere in `COMPOSE_CMD` Variable für später
|
|
|
|
**Wichtig:** `${COMPOSE_CMD}` wird später überall verwendet (Zeile 371, 626 etc.)
|
|
|
|
**Voraussetzungen:**
|
|
- Docker Compose v2+ oder `docker-compose` command vorhanden
|
|
|
|
### Zeilen 180-185: Git Check
|
|
```bash
|
|
if ! command -v git >/dev/null 2>&1; then
|
|
echo -e "${RED}Fehler: Git nicht gefunden!${NC}"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
**Benötigt für:**
|
|
- Clone Repository bei Neuinstallation (Zeile 220)
|
|
- Pull Updates bei bestehender Installation (Zeile 209)
|
|
- Versionskontrolle
|
|
|
|
**Voraussetzungen:**
|
|
- `git` executable vorhanden (mindestens Git 2.0)
|
|
|
|
---
|
|
|
|
## Phase 5: Update vs. Neuinstallation (Zeilen 188-241)
|
|
|
|
### Zweck
|
|
Prüfe ob bereits Installation existiert. Wenn ja → update via git pull. Wenn nein → clone repository.
|
|
|
|
### Zeilen 192-193: Git Safe Directory (Synology-Workaround)
|
|
```bash
|
|
git config --global --add safe.directory "${INSTALL_DIR}" 2>/dev/null || true
|
|
```
|
|
|
|
**Warum?**
|
|
- Synology NAS: Docker Container läuft oft als root, Host-Verzeichnis ist root-owned
|
|
- Git misstraut Verzeichnissen mit unterschiedlichem Owner
|
|
- Fehler: `fatal: detected dubious ownership in repository at ...`
|
|
- Lösung: `safe.directory` erlaubt Repository trotzdem
|
|
|
|
**Syntax:**
|
|
- `2>/dev/null || true` - Ignoriere Fehler (akzeptabel wenn git zu alt)
|
|
|
|
### Zeilen 195-213: Update-Branch (Existierende Installation)
|
|
```bash
|
|
if [ -d "${INSTALL_DIR}/.git" ]; then
|
|
echo "Update erkannt - hole neueste Aenderungen..."
|
|
cd "${INSTALL_DIR}"
|
|
|
|
# Sichere lokale Aenderungen
|
|
if git diff --quiet 2>/dev/null; then
|
|
: # Keine Aenderungen
|
|
else
|
|
git stash 2>/dev/null || true
|
|
fi
|
|
|
|
# Update durchfuehren
|
|
if git fetch origin && git pull origin main 2>/dev/null; then
|
|
echo "Repository aktualisiert"
|
|
else
|
|
echo "Git-Update fehlgeschlagen, fahre mit lokalen Dateien fort..."
|
|
fi
|
|
```
|
|
|
|
**Logik:**
|
|
1. Existiert `.git` Verzeichnis? → Installation vorhanden
|
|
2. Prüfe auf lokale Änderungen mit `git diff --quiet`
|
|
3. Falls Änderungen → `git stash` (temporär speichern)
|
|
4. `git fetch` + `git pull origin main` (Update durchführen)
|
|
5. Falls fehlgeschlagen → Warnung aber nicht abbrechen (fahre mit lokalen Dateien fort)
|
|
|
|
**Warum stash?**
|
|
- User hat möglicherweise Dateien lokal angepasst (z.B. api.py Bugfixes)
|
|
- `git pull` würde MERGE CONFLICT verursachen
|
|
- `git stash` speichert lokale Änderungen sicher → Merge ist sauber
|
|
|
|
**Voraussetzungen:**
|
|
- `.git` Verzeichnis existiert (= Installation bereits vorhanden)
|
|
- `origin` Remote existiert (Standard bei clone)
|
|
|
|
### Zeilen 214-240: Clone-Branch (Neuinstallation)
|
|
```bash
|
|
else
|
|
echo "Neuinstallation - klone Repository..."
|
|
|
|
TEMP_DIR=$(mktemp -d)
|
|
|
|
if git clone "${REPO_URL}" "${TEMP_DIR}"; then
|
|
shopt -s dotglob 2>/dev/null || true
|
|
for item in "${TEMP_DIR}"/*; do
|
|
basename_item=$(basename "$item")
|
|
if [ "$basename_item" != ".env" ] && [ "$basename_item" != ".git" ]; then
|
|
cp -r "$item" "${INSTALL_DIR}/"
|
|
fi
|
|
done
|
|
|
|
cp -r "${TEMP_DIR}/.git" "${INSTALL_DIR}/"
|
|
rm -rf "${TEMP_DIR}"
|
|
```
|
|
|
|
**Logik:**
|
|
1. Keine `.git` Verzeichnis → Neuinstallation
|
|
2. Clone in **temporäres Verzeichnis** (nicht direkt nach INSTALL_DIR)
|
|
3. Kopiere Dateien (außer `.env` und `.git`)
|
|
4. Kopiere `.git` Verzeichnis nachträglich (für zukünftige Updates)
|
|
5. Räume Temp-Verzeichnis auf
|
|
|
|
**Warum nicht direkt klonen?**
|
|
- `git clone` würde `.env` überschreiben (wenn Admin bereits angepasst hat)
|
|
- Temporärer Clone als Workaround
|
|
|
|
**Voraussetzungen:**
|
|
- `git clone` funktioniert (Internet, SSH-Key oder HTTPS)
|
|
- mktemp Command vorhanden (POSIX-kompatibel)
|
|
|
|
---
|
|
|
|
## Phase 6: Verzeichnisse & Berechtigungen (Zeilen 244-285)
|
|
|
|
### Zweck
|
|
Erstelle erforderliche Verzeichnisse und setze Berechtigungen für Docker-Zugriff.
|
|
|
|
### Zeilen 249-255: Verzeichnisse erstellen
|
|
```bash
|
|
mkdir -p "${INSTALL_DIR}/data"
|
|
mkdir -p "${INSTALL_DIR}/logs"
|
|
|
|
chmod 755 "${INSTALL_DIR}/data"
|
|
chmod 755 "${INSTALL_DIR}/logs"
|
|
```
|
|
|
|
**Warum diese Verzeichnisse?**
|
|
- `data/` - Persistente Datenbank + Konfiguration
|
|
- `logs/` - Installations- und Anwendungs-Logs
|
|
|
|
**Berechtigungen (755 = rwxr-xr-x):**
|
|
- Owner: read + write + execute
|
|
- Group: read + execute (nur)
|
|
- Others: read + execute (nur)
|
|
|
|
**Kompatibilität:**
|
|
- Docker Container als root: 755 ausreichend
|
|
- Docker Container als non-root: Könnten 777 brauchen (kommentiert Zeile 271)
|
|
|
|
### Zeilen 262-274: Root-Check
|
|
```bash
|
|
if [ "$(id -u)" = "0" ]; then
|
|
# Als root: 755 reicht
|
|
echo "755 (root)"
|
|
else
|
|
# Als normaler User: 755 auch OK
|
|
echo "755"
|
|
fi
|
|
```
|
|
|
|
**Warum Root-Check?**
|
|
- Synology NAS: Installation oft als root
|
|
- Docker-Container: Läuft auch als root
|
|
- User-Scripts (z.B. cron): Könnten als normaler User laufen
|
|
|
|
**Regel:**
|
|
- Root + Docker-Root: 755 OK
|
|
- Normaler User + Docker-Root: 755 auch OK
|
|
- Normaler User + Docker-NonRoot: 777 nötig (siehe Kommentar)
|
|
|
|
### Zeilen 277-279: .env schützen
|
|
```bash
|
|
if [ -f "${INSTALL_DIR}/.env" ]; then
|
|
chmod 600 "${INSTALL_DIR}/.env"
|
|
echo ".env: OK (600, nur Owner)"
|
|
```
|
|
|
|
**Berechtigungen (600 = rw-------):**
|
|
- Nur Owner: read + write
|
|
- Group/Others: kein Zugriff
|
|
|
|
**WICHTIG:** Secrets in .env:
|
|
- `SECRET_KEY` - Flask Session Secret
|
|
- `SMTP_PASSWORD` - Email-Authentifizierung
|
|
- Darf nicht für alle lesbar sein!
|
|
|
|
### Zeilen 282-284: install.sh auführbar machen
|
|
```bash
|
|
if [ -f "${INSTALL_DIR}/install.sh" ]; then
|
|
chmod +x "${INSTALL_DIR}/install.sh"
|
|
```
|
|
|
|
**Warum?**
|
|
- Nach git pull/clone könnte execute-bit verloren gehen
|
|
- Stelle sicher dass `bash install.sh` immer funktioniert
|
|
|
|
**Voraussetzungen:**
|
|
- chmod Command vorhanden (POSIX)
|
|
- Berechtigungen änderbar (nicht in read-only Filesystem)
|
|
|
|
---
|
|
|
|
## Phase 7: Docker-Netzwerk Prüfung (Zeilen 288-309)
|
|
|
|
### Zweck
|
|
Prüfe dass Traefik-Netzwerk existiert. Wenn nicht → Erstelle automatisch.
|
|
|
|
### Zeilen 291-292: Netzwerk aus .env lesen
|
|
```bash
|
|
NETWORK="${TRAEFIK_NETWORK:-web}"
|
|
echo "Pruefe Docker-Netzwerk: ${NETWORK}"
|
|
```
|
|
|
|
- Lese `TRAEFIK_NETWORK` Variable aus .env
|
|
- Fallback zu "web" wenn nicht definiert
|
|
|
|
### Zeilen 294-309: Netzwerk-Logik
|
|
```bash
|
|
if docker network inspect "${NETWORK}" >/dev/null 2>&1; then
|
|
echo "Netzwerk '${NETWORK}': existiert"
|
|
else
|
|
echo "Fehler: Docker-Netzwerk '${NETWORK}' existiert nicht!"
|
|
|
|
# Automatische Erstellung (vorher: interaktive Frage)
|
|
docker network create "${NETWORK}" 2>/dev/null || true
|
|
echo "Netzwerk '${NETWORK}': erstellt/vorhanden"
|
|
fi
|
|
```
|
|
|
|
**Logik:**
|
|
1. Prüfe ob Netzwerk existiert mit `docker network inspect`
|
|
- Erfolgreich → Netzwerk vorhanden, weitermachen
|
|
- Fehler → Netzwerk nicht vorhanden
|
|
|
|
2. **Automatische Erstellung** (Zeile 307)
|
|
- Erstelle Netzwerk: `docker network create "${NETWORK}"`
|
|
- `2>/dev/null || true` - Ignoriere Fehler (z.B. bereits vorhanden)
|
|
- Dadurch war das Script nicht mehr blockiert (früher: `read -p "...?"`)
|
|
|
|
**Warum dieses Netzwerk?**
|
|
- Traefik + Spawner müssen im **gleichen Netzwerk** sein
|
|
- Nur so können Traefik die User-Container entdecken und routen
|
|
- Name definiert in `.env` → `TRAEFIK_NETWORK=web` (oder custom)
|
|
|
|
**Voraussetzungen:**
|
|
- Docker Daemon läuft
|
|
- Benutzer hat docker Zugriff (sudo oder docker-Gruppe)
|
|
- Netzwerk nicht durch andere Tools belegt
|
|
|
|
---
|
|
|
|
## Phase 8: Traefik Prüfung (Zeilen 312-361)
|
|
|
|
### Zweck
|
|
Prüfe ob Traefik-Container läuft. Falls ja → Validiere dass es im richtigen Netzwerk ist.
|
|
|
|
### Zeilen 318-333: Traefik-Container Suche (3 Versuche)
|
|
|
|
**Versuch 1 (Zeile 318): Nach Name suchen**
|
|
```bash
|
|
TRAEFIK_CONTAINER=$(docker ps --filter "name=traefik" --filter "status=running" \
|
|
--format "{{.Names}}" 2>/dev/null | head -1)
|
|
```
|
|
- Suche nach Container mit "traefik" im Namen
|
|
- `--filter "status=running"` - Nur laufende Container
|
|
- `--format "{{.Names}}"` - Nur Container-Name ausgeben
|
|
- `head -1` - Nimm ersten (falls mehrere)
|
|
|
|
**Versuch 2 (Zeile 322): Nach Image suchen**
|
|
```bash
|
|
if [ -z "$TRAEFIK_CONTAINER" ]; then
|
|
TRAEFIK_CONTAINER=$(docker ps --filter "ancestor=traefik" \
|
|
--filter "status=running" --format "{{.Names}}" 2>/dev/null | head -1)
|
|
fi
|
|
```
|
|
- Falls Name-Suche leer → Suche nach `ancestor=traefik` (Image-Name)
|
|
- Findungschance: Container könnte anders benannt sein, aber vom traefik Image stammen
|
|
|
|
**Versuch 3 (Zeile 327-332): Nach Label suchen**
|
|
```bash
|
|
if [ -z "$TRAEFIK_CONTAINER" ]; then
|
|
TRAEFIK_CONTAINER=$(docker ps ... | while read name; do
|
|
if docker inspect "$name" 2>/dev/null | grep -q '"traefik.http.routers\|com.docker.compose.service": "traefik"'; then
|
|
echo "$name"
|
|
break
|
|
fi
|
|
done)
|
|
fi
|
|
```
|
|
- Falls noch immer nicht gefunden → Prüfe Labels
|
|
- Traefik setzt typischerweise: `com.docker.compose.service=traefik`
|
|
|
|
### Zeilen 335-361: Traefik-Status Ausgabe
|
|
|
|
**Falls gefunden (Zeile 335-350):**
|
|
```bash
|
|
if [ -n "$TRAEFIK_CONTAINER" ]; then
|
|
TRAEFIK_VERSION=$(docker exec "$TRAEFIK_CONTAINER" traefik version 2>/dev/null | ...)
|
|
echo "Traefik: OK (Container: ${TRAEFIK_CONTAINER}, v${TRAEFIK_VERSION})"
|
|
|
|
# Prüfe ob im richtigen Netzwerk
|
|
TRAEFIK_NETWORKS=$(docker inspect "$TRAEFIK_CONTAINER" \
|
|
--format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}')
|
|
if echo "$TRAEFIK_NETWORKS" | grep -q "$NETWORK"; then
|
|
echo "Traefik-Netzwerk: OK (verbunden mit '${NETWORK}')"
|
|
else
|
|
echo "WARNUNG: Traefik nicht mit Netzwerk '${NETWORK}' verbunden"
|
|
echo "Traefik-Netzwerke: ${TRAEFIK_NETWORKS}"
|
|
fi
|
|
fi
|
|
```
|
|
|
|
**Checks:**
|
|
1. `traefik version` - Zeige Traefik-Version an
|
|
2. `docker inspect` - Prüfe Netzwerk-Verbindungen
|
|
3. Regex-Match gegen `${NETWORK}` - Sind Traefik und Spawner im gleichen Netzwerk?
|
|
|
|
**Falls nicht gefunden (Zeile 352-361):**
|
|
```bash
|
|
else
|
|
echo "WARNUNG: Kein laufender Traefik-Container gefunden!"
|
|
echo "Traefik wird fuer das Routing benoetigt, aber Script faehrt fort..."
|
|
```
|
|
|
|
- **WARNUNG nicht FEHLER** → Script bricht nicht ab
|
|
- Spawner funktioniert auch ohne Traefik:
|
|
- Nur lokal erreichbar (localhost:5000)
|
|
- Keine automatischen Subdomains
|
|
- Keine User-Container Routing
|
|
|
|
**Voraussetzungen:**
|
|
- Docker Daemon läuft und antwortet
|
|
- Traefik-Container läuft (optional, mit Warnung weitergemacht)
|
|
|
|
---
|
|
|
|
## Phase 9: Docker Images Bauen (Zeilen 364-563)
|
|
|
|
### Zweck
|
|
Baue alle erforderlichen Docker Images:
|
|
1. **User-Templates** (template-01, template-02, template-next etc.)
|
|
2. **Spawner API** (Flask Backend)
|
|
3. **Spawner Frontend** (Next.js)
|
|
|
|
### Zeilen 370-378: USER_TEMPLATE_IMAGES aus .env auslesen
|
|
```bash
|
|
USER_TEMPLATE_IMAGES=""
|
|
if [ -f "${INSTALL_DIR}/.env" ]; then
|
|
USER_TEMPLATE_IMAGES=$(grep "^USER_TEMPLATE_IMAGES=" "${INSTALL_DIR}/.env" | \
|
|
cut -d'=' -f2- | tr -d '"' | tr -d "'")
|
|
fi
|
|
```
|
|
|
|
**Parse-Logic:**
|
|
1. `grep "^USER_TEMPLATE_IMAGES="` - Finde genau diese Zeile (nicht commented)
|
|
2. `cut -d'=' -f2-` - Alles nach dem `=` Zeichen
|
|
3. `tr -d '"' | tr -d "'"` - Entferne Quotes (USER_TEMPLATE_IMAGES="...;...;...")
|
|
|
|
**Beispiel:**
|
|
```bash
|
|
# In .env
|
|
USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest"
|
|
|
|
# Nach Parsing
|
|
USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest"
|
|
```
|
|
|
|
### Zeilen 380-385: Fallback auf .env.example
|
|
```bash
|
|
if [ -z "$USER_TEMPLATE_IMAGES" ] && [ -f "${INSTALL_DIR}/.env.example" ]; then
|
|
echo "⚠️ USER_TEMPLATE_IMAGES nicht definiert"
|
|
echo " Nutze .env.example als Fallback..."
|
|
USER_TEMPLATE_IMAGES=$(grep "^USER_TEMPLATE_IMAGES=" "${INSTALL_DIR}/.env.example" | ...)
|
|
fi
|
|
```
|
|
|
|
- Wenn `.env` USER_TEMPLATE_IMAGES nicht hat → Versuche `.env.example`
|
|
- Erlaubt Test-Installation ohne .env Anpassung
|
|
|
|
### Zeilen 387-437: Fallback-Modus (Keine .env Konfiguration)
|
|
```bash
|
|
if [ -z "$USER_TEMPLATE_IMAGES" ]; then
|
|
echo "⚠️ USER_TEMPLATE_IMAGES nicht konfiguriert"
|
|
echo " Fallback: Baue alle user-template-* Verzeichnisse..."
|
|
|
|
for template_dir in "${INSTALL_DIR}"/user-template*; do
|
|
[ -d "$template_dir" ] || continue
|
|
template_name=$(basename "$template_dir")
|
|
image_name="${template_name}:latest"
|
|
|
|
docker build --no-cache -t "${image_name}" "${template_dir}/" >> "${BUILD_LOG}" 2>&1
|
|
```
|
|
|
|
**Logik (Rückwärtskompatibilität):**
|
|
- Wenn USER_TEMPLATE_IMAGES leer → Auto-Detection
|
|
- Suche alle `user-template-*` Verzeichnisse
|
|
- Baue jedes als Docker Image
|
|
|
|
**Beispiel:**
|
|
```
|
|
user-template-01/
|
|
user-template-02/
|
|
user-template-next/
|
|
|
|
→ Baue: user-template-01:latest, user-template-02:latest, user-template-next:latest
|
|
```
|
|
|
|
**Warum Fallback?**
|
|
- Alte Installationen ohne .env Konfiguration müssen auch funktionieren
|
|
- Neue Installationen verwenden .env-basiertes System
|
|
|
|
### Zeilen 440-562: .env-basiertes Building (NEUE LOGIK)
|
|
```bash
|
|
else
|
|
echo " Baue Templates aus .env Konfiguration..."
|
|
|
|
# Split by Semicolon
|
|
IFS=';' read -ra TEMPLATE_IMAGES <<< "$USER_TEMPLATE_IMAGES"
|
|
|
|
TOTAL_TEMPLATES=${#TEMPLATE_IMAGES[@]}
|
|
|
|
for image_with_tag in "${TEMPLATE_IMAGES[@]}"; do
|
|
image_with_tag=$(echo "$image_with_tag" | xargs) # Trim whitespace
|
|
[ -z "$image_with_tag" ] && continue
|
|
|
|
# Extract directory name (vor dem :)
|
|
template_dir_name="${image_with_tag%%:*}"
|
|
|
|
# Extract tag (nach dem :)
|
|
template_tag="${image_with_tag##*:}"
|
|
[ -z "$template_tag" ] && template_tag="latest"
|
|
|
|
template_dir="${INSTALL_DIR}/${template_dir_name}"
|
|
```
|
|
|
|
**Parse-Logik für "user-template-01:latest":**
|
|
1. Split Array by `;`
|
|
- `"user-template-01:latest;user-template-02:latest"`
|
|
- → `["user-template-01:latest", "user-template-02:latest"]`
|
|
|
|
2. Trim Whitespace mit `xargs`
|
|
- `" user-template-01:latest "` → `"user-template-01:latest"`
|
|
|
|
3. Extrahiere Image-Name und Tag
|
|
- `${image%%:*}` - Alles VOR dem ersten `:` (user-template-01)
|
|
- `${image##*:}` - Alles NACH dem letzten `:` (latest)
|
|
|
|
**Validation (Zeilen 475-493):**
|
|
```bash
|
|
# Prüfe ob Verzeichnis existiert
|
|
if [ ! -d "$template_dir" ]; then
|
|
echo "❌ Fehler: Template-Verzeichnis nicht gefunden"
|
|
echo " Definiert in .env: USER_TEMPLATE_IMAGES"
|
|
echo " Erwartetes Verzeichnis: ${template_dir}"
|
|
continue # Überspringe dieses Template
|
|
fi
|
|
|
|
# Dockerfile vorhanden?
|
|
if [ ! -f "${template_dir}/Dockerfile" ]; then
|
|
echo "❌ Fehler: Kein Dockerfile gefunden"
|
|
continue
|
|
fi
|
|
```
|
|
|
|
- Nicht-existente Verzeichnisse → WARNUNG + continue (nicht FEHLER)
|
|
- So können einzelne Templates fehlschlagen, andere funktionieren weiterhin
|
|
|
|
**Build-Logik (Zeilen 507-527):**
|
|
```bash
|
|
docker build --no-cache -t "${template_dir_name}:${template_tag}" "${template_dir}/" >> "${BUILD_LOG}" 2>&1
|
|
BUILD_EXIT=$?
|
|
|
|
# Zeige gefilterten Output
|
|
grep -E "(Step |#[0-9]+ |Successfully|ERROR|error:|COPY|RUN|FROM)" "${BUILD_LOG}" | sed 's/^/ /'
|
|
|
|
# Prüfe ob erfolgreich
|
|
if [ $BUILD_EXIT -eq 0 ] && docker image inspect "${template_dir_name}:${template_tag}" >/dev/null 2>&1; then
|
|
echo "✅ ${template_name}: OK"
|
|
BUILT_TEMPLATES=$((BUILT_TEMPLATES + 1))
|
|
else
|
|
echo "❌ ${template_name}: FEHLER"
|
|
tail -50 "${BUILD_LOG}"
|
|
exit 1 # Breche ab
|
|
fi
|
|
```
|
|
|
|
**Build-Logik:**
|
|
1. Führe `docker build` aus, leite Output zu Log-Datei
|
|
2. Zeige gefilterte Build-Schritte (Step, FROM, RUN, COPY, ERROR)
|
|
3. Prüfe ob erfolgreich:
|
|
- Exit-Code = 0?
|
|
- `docker image inspect` findet das Image?
|
|
4. Beide Bedingungen erfüllt → OK
|
|
5. Sonst → FEHLER, zeige last 50 Zeilen, beende Script
|
|
|
|
**Spezieller Handling für Next.js (Zeile 497-498):**
|
|
```bash
|
|
if [[ "$template_dir_name" == *"next"* ]]; then
|
|
echo "${BLUE}Dies kann 2-5 Minuten dauern (npm install + build)...${NC}"
|
|
fi
|
|
```
|
|
|
|
- Next.js Templates brauchen länger (npm install + npm run build)
|
|
- Warnung damit Admin nicht denkt Installation ist hänggeblieben
|
|
|
|
**Ungekonfigurierte Templates Warnung (Zeilen 539-561):**
|
|
```bash
|
|
echo " Prüfe auf ungekonfigurierte Template-Verzeichnisse..."
|
|
|
|
for template_dir in "${INSTALL_DIR}"/user-template*; do
|
|
template_name=$(basename "$template_dir")
|
|
|
|
# Ist dieses Template in USER_TEMPLATE_IMAGES definiert?
|
|
if [[ ! "$USER_TEMPLATE_IMAGES" =~ "$template_name" ]]; then
|
|
echo " ⚠️ ${template_name} (nicht in USER_TEMPLATE_IMAGES definiert)"
|
|
fi
|
|
done
|
|
```
|
|
|
|
- Hilft Admin zu sehen, welche Templates **nicht** gebaut wurden
|
|
- Z.B. Template existiert lokal, aber nicht in .env → wird ignoriert
|
|
|
|
### Zeilen 565-616: Spawner API & Frontend bauen
|
|
|
|
**Spawner API (Flask Backend) - Zeilen 565-587:**
|
|
```bash
|
|
echo " Baue Spawner API (Flask Backend)..."
|
|
|
|
docker build --no-cache -t spawner:latest "${INSTALL_DIR}/" >> "${BUILD_LOG}" 2>&1
|
|
BUILD_EXIT=$?
|
|
|
|
if [ $BUILD_EXIT -eq 0 ] && docker image inspect spawner:latest >/dev/null 2>&1; then
|
|
echo " spawner-api: ${GREEN}OK${NC}"
|
|
else
|
|
echo " spawner-api: ${RED}FEHLER${NC}"
|
|
tail -50 "${BUILD_LOG}"
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
- Baut Image aus Dockerfile im **root directory** (nicht frontend/)
|
|
- Image-Name: `spawner:latest`
|
|
- **Muss erfolgreich sein** (exit 1 bei Fehler)
|
|
|
|
**Spawner Frontend (Next.js) - Zeilen 590-616:**
|
|
```bash
|
|
if [ -d "${INSTALL_DIR}/frontend" ]; then
|
|
echo " Baue Frontend (Next.js)..."
|
|
echo "${BLUE}Dies kann 2-5 Minuten dauern (npm install + build)...${NC}"
|
|
|
|
docker build --no-cache -t spawner-frontend:latest "${INSTALL_DIR}/frontend/" >> "${BUILD_LOG}" 2>&1
|
|
...
|
|
fi
|
|
```
|
|
|
|
- **Optional Check**: Falls `frontend/` Verzeichnis nicht vorhanden → überspringe
|
|
- Image-Name: `spawner-frontend:latest`
|
|
- Warnung dass npm build lange dauert
|
|
|
|
**Voraussetzungen:**
|
|
- `docker build` Command funktioniert
|
|
- Ausreichend Disk-Space für Images
|
|
- Internetzugriff (für npm install, pip install etc.)
|
|
- Dockerfile(s) syntaktisch korrekt
|
|
|
|
---
|
|
|
|
## Phase 10: Container Starten (Zeilen 621-646)
|
|
|
|
### Zweck
|
|
Starte Docker Compose Services (Spawner API, Frontend, optional Traefik) und prüfe Health.
|
|
|
|
### Zeilen 625-626: Compose Up
|
|
```bash
|
|
echo ""
|
|
echo "Starte Container..."
|
|
${COMPOSE_CMD} up -d
|
|
```
|
|
|
|
- `${COMPOSE_CMD}` ist entweder `docker compose` oder `docker-compose` (aus Phase 4)
|
|
- `-d` Flag: Detached Mode (laufen im Hintergrund)
|
|
|
|
**Was wird gestartet?** (Definiert in docker-compose.yml)
|
|
- spawner (API, Port 5000)
|
|
- spawner-frontend (Next.js, Port 3000)
|
|
- Optional: traefik, database etc. (je nach Konfiguration)
|
|
|
|
### Zeilen 631-646: Health-Checks
|
|
```bash
|
|
echo "Warte auf Spawner-Start..."
|
|
sleep 5
|
|
|
|
SPAWNER_URL="http://localhost:${SPAWNER_PORT:-5000}/health"
|
|
if curl -sf "${SPAWNER_URL}" >/dev/null 2>&1; then
|
|
echo " API Health-Check: ${GREEN}OK${NC}"
|
|
else
|
|
echo " API Health-Check: ${YELLOW}Noch nicht bereit (normal beim ersten Start)${NC}"
|
|
fi
|
|
|
|
if curl -sf "http://localhost:3000/" >/dev/null 2>&1; then
|
|
echo " Frontend Health-Check: ${GREEN}OK${NC}"
|
|
else
|
|
echo " Frontend Health-Check: ${YELLOW}Noch nicht bereit${NC}"
|
|
fi
|
|
```
|
|
|
|
**Logik:**
|
|
1. Warte 5 Sekunden (Container brauchen Zeit zu starten)
|
|
2. Prüfe API: `curl -sf http://localhost:5000/health`
|
|
- `-s` Silent mode
|
|
- `-f` Fail silently auf HTTP-Error
|
|
3. Prüfe Frontend: `curl -sf http://localhost:3000/`
|
|
|
|
**Status:**
|
|
- Beide OK → ✅ Alles funktioniert
|
|
- Eine/beide nicht bereit → ⚠️ Normal beim ersten Start (DB wird initialisiert etc.)
|
|
|
|
**Voraussetzungen:**
|
|
- curl Command verfügbar
|
|
- Container haben Zeit zu starten
|
|
- Ports 5000 und 3000 lokal verfügbar
|
|
|
|
---
|
|
|
|
## Phase 11: Fertig-Nachricht (Zeilen 648-695)
|
|
|
|
### Zweck
|
|
Zeige Erfolgs-Nachricht und nützliche Informationen für Admin.
|
|
|
|
### Zeilen 652-656: Success Banner
|
|
```bash
|
|
echo "============================================================"
|
|
echo -e " ${GREEN}Installation abgeschlossen!${NC}"
|
|
echo "============================================================"
|
|
echo ""
|
|
echo "Installations-Log: ${LOG_FILE}"
|
|
```
|
|
|
|
- Zeige Erfolgs-Banner
|
|
- Link zu Log-Datei (für Debugging)
|
|
|
|
### Zeilen 659-668: URLs anzeigen
|
|
```bash
|
|
SCHEME="https"
|
|
if [ "${BASE_DOMAIN:-localhost}" = "localhost" ]; then
|
|
SCHEME="http"
|
|
fi
|
|
FULL_URL="${SCHEME}://${SPAWNER_SUBDOMAIN:-coder}.${BASE_DOMAIN:-localhost}"
|
|
|
|
echo "Zugriff:"
|
|
echo " Frontend: ${FULL_URL}"
|
|
echo " API: ${FULL_URL}/api"
|
|
echo " Health: ${FULL_URL}/health"
|
|
```
|
|
|
|
**Logic:**
|
|
- Lese `BASE_DOMAIN` und `SPAWNER_SUBDOMAIN` aus .env
|
|
- Falls localhost → nutze http:// (kein HTTPS)
|
|
- Sonst → nutze https:// (erwartet Let's Encrypt)
|
|
|
|
**Beispiel .env:**
|
|
```
|
|
BASE_DOMAIN=wieland.org
|
|
SPAWNER_SUBDOMAIN=coder
|
|
```
|
|
|
|
→ URLs:
|
|
- Frontend: https://coder.wieland.org
|
|
- API: https://coder.wieland.org/api
|
|
- Health: https://coder.wieland.org/health
|
|
|
|
### Zeilen 670-678: Lokale URLs & Befehle
|
|
```bash
|
|
echo ""
|
|
echo "Lokaler Zugriff (ohne Traefik):"
|
|
echo " API: http://localhost:${SPAWNER_PORT:-5000}"
|
|
echo " Frontend: http://localhost:3000"
|
|
echo ""
|
|
echo "Nützliche Befehle:"
|
|
echo " Status: ${COMPOSE_CMD} ps"
|
|
echo " Logs API: ${COMPOSE_CMD} logs -f spawner"
|
|
echo " Logs FE: ${COMPOSE_CMD} logs -f frontend"
|
|
```
|
|
|
|
- Zeige wie man Services ohne Traefik zugreift
|
|
- Zeige häufige Wartungs-Befehle
|
|
|
|
### Zeilen 681-695: Wichtige Informationen
|
|
```bash
|
|
echo "WICHTIG - Multi-Container MVP:"
|
|
echo " - Jeder User erhält 2 Container: Development und Production"
|
|
echo " - Dev Container: https://${SPAWNER_SUBDOMAIN}.${BASE_DOMAIN}/{slug}-dev"
|
|
echo " - Prod Container: https://${SPAWNER_SUBDOMAIN}.${BASE_DOMAIN}/{slug}-prod"
|
|
echo ""
|
|
echo "WICHTIG - Passwordless Auth:"
|
|
echo " Das System nutzt Magic Links (Email-basiert)!"
|
|
echo " - SMTP konfigurieren: .env Datei anpassen"
|
|
echo " - Datenbank wird automatisch mit allen Tabellen erstellt"
|
|
```
|
|
|
|
- Erkläre Multi-Container Feature
|
|
- Erkläre Passwordless Authentication
|
|
- Hinweis auf SMTP Konfiguration
|
|
|
|
---
|
|
|
|
## Zusammenfassung der Abhängigkeiten
|
|
|
|
### Externe Tools (müssen vorhanden sein)
|
|
| Tool | Min-Version | Zweck | Fallback |
|
|
|------|-------------|-------|---------|
|
|
| docker | 20.10 | Container management | Keine |
|
|
| docker compose | 2.0 | Service orchestration | docker-compose (v1) |
|
|
| git | 2.0+ | Repository management | Keine |
|
|
| curl/wget | - | Download .env.example | curl ODER wget |
|
|
| bash | 4.0+ | Script interpreter | Nur bash (nicht sh) |
|
|
| grep, cut, tr, sed | - | Text parsing | BusyBox kompatibel |
|
|
|
|
### Umgebungsvariablen (.env)
|
|
| Variable | Beispiel | Zweck | Required |
|
|
|----------|---------|-------|----------|
|
|
| `SECRET_KEY` | `abc123...` | Flask session secret | Ja |
|
|
| `BASE_DOMAIN` | `wieland.org` | Hauptdomain | Ja |
|
|
| `SPAWNER_SUBDOMAIN` | `coder` | Subdomain prefix | Ja |
|
|
| `TRAEFIK_NETWORK` | `web` | Docker network für Traefik | Ja |
|
|
| `USER_TEMPLATE_IMAGES` | `user-template-01:latest;...` | Templates zum bauen | Optional (Fallback) |
|
|
| `SPAWNER_PORT` | `5000` | Backend Port | Optional |
|
|
|
|
### Dateisystem-Anforderungen
|
|
- **Disk-Space**: 3-5 GB (für Docker Images)
|
|
- **Verzeichnisse**:
|
|
- `.env` - Konfiguration (chmod 600)
|
|
- `data/` - Datenbank (chmod 755)
|
|
- `logs/` - Installation/App Logs (chmod 755)
|
|
- `frontend/` - Frontend Code (optional)
|
|
- `user-template-*/` - Template Dockerfiles
|
|
|
|
### Netzwerk-Anforderungen
|
|
- **Internet**: Zum downloaden .env.example, docker images, npm/pip packages
|
|
- **Docker Daemon**: Lokal erreichbar (Unix Socket oder TCP)
|
|
- **Docker Network**: `${TRAEFIK_NETWORK}` darf nicht existieren (oder wird geteilt mit Traefik)
|
|
|
|
### Sicherheits-Anforderungen
|
|
- `.env` muss **before** Installation vorhanden sein (mit SECRET_KEY gesetzt)
|
|
- Docker Socket nur für vertrauenswürdige Benutzer zugänglich
|
|
- SMTP-Credentials (wenn konfiguriert) müssen sicher sein
|
|
|
|
---
|
|
|
|
## Fehlerbehandlung & Logging
|
|
|
|
### Log-Datei: `spawner-install.log`
|
|
|
|
Alle Build-Outputs werden geloggt:
|
|
```
|
|
=== Spawner Installation 2026-02-03 10:00:00 ===
|
|
|
|
=== Build: user-template-01 ===
|
|
Step 1/5 : FROM nginx:latest
|
|
---> ...
|
|
Step 2/5 : COPY index.html /usr/share/nginx/html/
|
|
...
|
|
Successfully built abc123
|
|
|
|
=== Build: spawner-api ===
|
|
...
|
|
```
|
|
|
|
### Fehler-Handling mit `set -e`
|
|
|
|
**Reihenfolge:**
|
|
1. Script stoppt sofort bei erstem Fehler (`set -e`)
|
|
2. Zuletzt erfolgreich durchgeführte Zeile wird gezeigt
|
|
3. Admin kann Log-Datei prüfen: `tail spawner-install.log`
|
|
|
|
**Beispiel:**
|
|
```bash
|
|
# Fehler: Docker nicht installiert
|
|
$ bash install.sh
|
|
Fehler: Docker nicht gefunden!
|
|
→ exit 1 (Script stoppt)
|
|
→ install.sh log nicht weitergehe
|
|
```
|
|
|
|
### Recovery-Strategien
|
|
|
|
**Fehler: Zu alte Docker Version**
|
|
```bash
|
|
→ Installiere Docker ≥ 20.10
|
|
→ Starte install.sh erneut
|
|
```
|
|
|
|
**Fehler: .env nicht gefunden**
|
|
```bash
|
|
→ cp .env.example .env
|
|
→ nano .env (setze SECRET_KEY, BASE_DOMAIN etc.)
|
|
→ bash install.sh
|
|
```
|
|
|
|
**Fehler: Template-Image Build fehlt**
|
|
```bash
|
|
→ Prüfe docker build Fehler in Log
|
|
→ Ursache meist: Dockerfile Syntax, Base Image nicht vorhanden
|
|
→ Korrigiere Dockerfile
|
|
→ Starte install.sh erneut (re-baut Imagen)
|
|
```
|
|
|
|
**Fehler: Git Clone fehlgeschlagen**
|
|
```bash
|
|
→ Prüfe Internet-Zugriff: curl https://gitea.iotxs.de
|
|
→ Prüfe Git SSH/HTTPS Keys (falls SSH)
|
|
→ Starte install.sh erneut
|
|
```
|
|
|
|
---
|
|
|
|
## Tipps für Synology NAS
|
|
|
|
### BusyBox-Kompatibilität
|
|
|
|
Install.sh läuft auf Synology mit BusyBox-Shell. Wichtige Unterschiede:
|
|
|
|
| Bash | BusyBox | Workaround in install.sh |
|
|
|------|---------|-------------------------|
|
|
| `sort -V` | Nicht vorhanden | Eigene `version_gte()` Funktion |
|
|
| `grep -P` (Perl) | Nicht vorhanden | Nutze Basic `grep` |
|
|
| `shopt` | Teilweise | `2>/dev/null \|\| true` (ignore error) |
|
|
|
|
### Docker auf Synology
|
|
|
|
```bash
|
|
# Docker läuft meist als root
|
|
id -u # → 0 (root)
|
|
|
|
# Berechtigungen daher 755 OK (nicht 777 nötig)
|
|
|
|
# Git safe.directory Workaround
|
|
git config --global --add safe.directory /volume1/docker/spawner
|
|
```
|
|
|
|
### Disk-Space prüfen
|
|
|
|
```bash
|
|
# Vor Installation
|
|
df -h /volume1/docker/
|
|
|
|
# Braucht ~5 GB für alle Images
|
|
# Bei < 10 GB verfügbar → Warnung geben
|
|
```
|
|
|
|
---
|
|
|
|
## Häufige Fragen
|
|
|
|
**F: Kann ich install.sh mehrmals hintereinander ausführen?**
|
|
A: Ja. Script erkennt bestehende Installation (`.git` Verzeichnis) und führt `git pull` aus statt zu klonen.
|
|
|
|
**F: Was passiert bei Netzwerk-Fehler während Download?**
|
|
A: Script stoppt mit Fehler (set -e). Nach Netzwerk-Reparatur kann man `bash install.sh` erneut ausführen.
|
|
|
|
**F: Kann ich nur bestimmte Templates bauen?**
|
|
A: Ja. Ändere `.env`: `USER_TEMPLATE_IMAGES="user-template-01:latest"` (nur template-01)
|
|
|
|
**F: Wie debugge ich Build-Fehler?**
|
|
A: Prüfe spawner-install.log: `cat spawner-install.log | grep -A 20 "ERROR\|FEHLER"`
|
|
|
|
**F: Können User-Container nach Installation noch hinzugefügt werden?**
|
|
A: Ja. Neue `user-template-*` Ordner + .env update + `docker build` manuell oder nächster install.sh run.
|
|
|
|
---
|
|
|
|
**Stand:** 2026-02-03
|
|
**Version:** install.sh v0.1.0
|
|
**Kompatibilität:** Bash 4.0+, BusyBox, Docker 20.10+, Docker Compose 2.0+
|