diff --git a/docker-install.sh b/docker-install.sh index 5941b87..eff88a5 100644 --- a/docker-install.sh +++ b/docker-install.sh @@ -1,84 +1,786 @@ #!/usr/bin/env bash +# ============================================================================= +# docker-setup.sh +# Docker + Docker Compose (+ optional Portainer) Installer / Uninstaller +# Unterstützte Systeme: Debian, Ubuntu (und deren Derivate) +# Zeitlos durch Verwendung der offiziellen Docker-Installationsmethode +# ============================================================================= +# Verwendung: +# sudo bash docker-setup.sh → Interaktive Installation +# sudo bash docker-setup.sh --remove → Vollständige Deinstallation +# sudo bash docker-setup.sh --help → Hilfe anzeigen +# ============================================================================= + set -euo pipefail -# ------------------------------------------------------------ -# Docker + Docker Compose Plugin Installer for Debian/Ubuntu -# ------------------------------------------------------------ +# ─── Farben & Symbole ──────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +DIM='\033[2m' +RESET='\033[0m' -if [[ "${EUID}" -ne 0 ]]; then - echo "Bitte als root oder mit sudo ausführen." - exit 1 -fi +OK="${GREEN}✔${RESET}" +WARN="${YELLOW}⚠${RESET}" +ERR="${RED}✖${RESET}" +INFO="${CYAN}ℹ${RESET}" +ARROW="${BLUE}➜${RESET}" -echo "==> Prüfe Betriebssystem ..." -if [[ ! -f /etc/os-release ]]; then - echo "Fehler: /etc/os-release nicht gefunden." - exit 1 -fi +# ─── Globale Variablen ─────────────────────────────────────────────────────── +SCRIPT_VERSION="2.0.0" +LOG_FILE="/var/log/docker-setup.log" +DOCKER_GPG_KEY="/etc/apt/keyrings/docker.asc" +DOCKER_REPO_FILE="/etc/apt/sources.list.d/docker.list" +PORTAINER_VOLUME="portainer_data" +PORTAINER_CONTAINER="portainer" +INSTALL_PORTAINER="yes" +ACTION="install" +DETECTED_ENV="" +DETECTED_OS="" +DETECTED_VERSION="" -# shellcheck disable=SC1091 -source /etc/os-release +# ─── Hilfsfunktionen ───────────────────────────────────────────────────────── -if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then - echo "Dieses Skript unterstützt nur Debian und Ubuntu." - echo "Erkanntes System: ${ID}" - exit 1 -fi +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true +} -ARCH="$(dpkg --print-architecture)" -KEYRING_DIR="/etc/apt/keyrings" -DOCKER_GPG="${KEYRING_DIR}/docker.asc" -REPO_FILE="/etc/apt/sources.list.d/docker.list" +print_banner() { + echo "" + echo -e "${BOLD}${BLUE}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${BLUE}║ 🐳 Docker Setup Script v${SCRIPT_VERSION} ║${RESET}" + echo -e "${BOLD}${BLUE}║ Docker · Docker Compose · Portainer für Debian/Ubuntu ║${RESET}" + echo -e "${BOLD}${BLUE}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" +} -echo "==> Installiere Basis-Pakete ..." -apt-get update -apt-get install -y ca-certificates curl gnupg lsb-release +print_section() { + echo "" + echo -e "${BOLD}${CYAN}── $1 ${DIM}─────────────────────────────────────────────────────${RESET}" +} -echo "==> Erstelle Keyring-Verzeichnis ..." -install -m 0755 -d "${KEYRING_DIR}" +info() { echo -e " ${INFO} $*"; log "INFO: $*"; } +success() { echo -e " ${OK} $*"; log "OK: $*"; } +warn() { echo -e " ${WARN} ${YELLOW}$*${RESET}"; log "WARN: $*"; } +error() { echo -e " ${ERR} ${RED}$*${RESET}"; log "ERROR: $*"; } +die() { error "$*"; exit 1; } -echo "==> Lade Docker GPG-Key ..." -curl -fsSL "https://download.docker.com/linux/${ID}/gpg" -o "${DOCKER_GPG}" -chmod a+r "${DOCKER_GPG}" +# Fortschrittsanzeige +step() { + echo -e "\n ${ARROW} ${BOLD}$*${RESET}" + log "STEP: $*" +} -echo "==> Konfiguriere Docker APT-Repository ..." -echo \ - "deb [arch=${ARCH} signed-by=${DOCKER_GPG}] https://download.docker.com/linux/${ID} \ - ${VERSION_CODENAME} stable" | tee "${REPO_FILE}" > /dev/null +# Ja/Nein-Abfrage mit konfigurierbarem Standard +ask_yes_no() { + local prompt="$1" + local default="${2:-yes}" + local answer -echo "==> Aktualisiere Paketquellen ..." -apt-get update + if [[ "$default" == "yes" ]]; then + local hint="[J/n]" + else + local hint="[j/N]" + fi -echo "==> Installiere Docker Engine, CLI, Containerd und Compose Plugin ..." -apt-get install -y \ - docker-ce \ - docker-ce-cli \ - containerd.io \ - docker-buildx-plugin \ - docker-compose-plugin + while true; do + echo -en " ${ARROW} ${BOLD}${prompt}${RESET} ${DIM}${hint}${RESET} " + read -r answer + answer="${answer:-$default}" + case "${answer,,}" in + j|ja|y|yes) return 0 ;; + n|nein|no) return 1 ;; + *) warn "Bitte mit j (ja) oder n (nein) antworten." ;; + esac + done +} -echo "==> Aktiviere und starte Docker-Dienst ..." -systemctl enable docker -systemctl restart docker +# ─── Root-Prüfung ──────────────────────────────────────────────────────────── -# Optional: Benutzer zur docker-Gruppe hinzufügen, falls per sudo gestartet -if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then - echo "==> Füge Benutzer '${SUDO_USER}' zur docker-Gruppe hinzu ..." - usermod -aG docker "${SUDO_USER}" || true - echo "HINWEIS: Damit Gruppenrechte greifen, bitte einmal ab- und wieder anmelden." -fi +check_root() { + if [[ $EUID -ne 0 ]]; then + die "Dieses Skript muss als root ausgeführt werden. Bitte 'sudo bash $0' verwenden." + fi +} -echo "==> Installation abgeschlossen." -echo -echo "Installierte Versionen:" -docker --version || true -docker compose version || true +# ─── Hilfe ─────────────────────────────────────────────────────────────────── -echo -echo "Teste Docker ..." -docker run --rm hello-world || true +print_help() { + cat </dev/null; then + DETECTED_OS="ubuntu" + else + DETECTED_OS="debian" + fi + fi + + info "Betriebssystem erkannt: ${BOLD}${os_pretty}${RESET}" + log "OS: $os_pretty | ID: $DETECTED_OS | Codename: $DETECTED_VERSION" +} + +# ─── Umgebung erkennen (Proxmox, Virtualmin, etc.) ─────────────────────────── + +detect_environment() { + DETECTED_ENV="standalone" + + # Proxmox erkennen + if [[ -f /etc/pve/pve-root-ca.pem ]] || command -v pvesh &>/dev/null 2>&1 || \ + [[ -d /etc/pve ]] || grep -qi "proxmox" /etc/os-release 2>/dev/null; then + DETECTED_ENV="proxmox" + # Virtuozzo / OpenVZ LXC + elif [[ -f /proc/vz/veinfo ]] || [[ -d /proc/vz ]]; then + DETECTED_ENV="openvz" + # Virtualmin erkennen + elif [[ -f /etc/webmin/virtualmin-nginx/config ]] || \ + [[ -f /usr/share/webmin/virtual-server/config ]] || \ + command -v virtualmin &>/dev/null 2>&1; then + DETECTED_ENV="virtualmin" + # Plesk erkennen + elif command -v plesk &>/dev/null 2>&1 || [[ -d /opt/psa ]]; then + DETECTED_ENV="plesk" + # cPanel erkennen + elif command -v whmapi1 &>/dev/null 2>&1 || [[ -d /usr/local/cpanel ]]; then + DETECTED_ENV="cpanel" + # Systemd-nspawn Container + elif systemd-detect-virt --container 2>/dev/null | grep -q "systemd-nspawn"; then + DETECTED_ENV="nspawn" + # Docker-in-Docker + elif [[ -f /.dockerenv ]]; then + DETECTED_ENV="docker" + fi +} + +# ─── Umgebungs-Infoboxen ───────────────────────────────────────────────────── + +show_environment_info() { + case "$DETECTED_ENV" in + proxmox) + echo "" + echo -e "${BOLD}${YELLOW}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${YELLOW}║ ⚠ PROXMOX UMGEBUNG ERKANNT ⚠ ║${RESET}" + echo -e "${BOLD}${YELLOW}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${YELLOW}║ ║${RESET}" + echo -e "${YELLOW}║ Docker wird auf einem Proxmox-Host oder in einer Proxmox- ║${RESET}" + echo -e "${YELLOW}║ VM/LXC installiert. Bitte beachte folgendes: ║${RESET}" + echo -e "${YELLOW}║ ║${RESET}" + echo -e "${YELLOW}║ ${BOLD}Empfehlung: Installation in einer VM (KVM), nicht im Host${RESET}${YELLOW} ║${RESET}" + echo -e "${YELLOW}║ ║${RESET}" + echo -e "${YELLOW}║ Bei LXC-Containern: ║${RESET}" + echo -e "${YELLOW}║ • Container muss 'privilegiert' sein ODER ║${RESET}" + echo -e "${YELLOW}║ 'nesting=1' und 'keyctl=1' in den Features haben ║${RESET}" + echo -e "${YELLOW}║ • Kernel-Module (overlay, ip_tables) müssen verfügbar sein ║${RESET}" + echo -e "${YELLOW}║ • Im Proxmox-GUI: Datacenter → Node → CT → Options ║${RESET}" + echo -e "${YELLOW}║ → Features: Nesting + keyctl aktivieren ║${RESET}" + echo -e "${YELLOW}║ ║${RESET}" + echo -e "${YELLOW}║ Proxmox-Host selbst: ║${RESET}" + echo -e "${YELLOW}║ • Möglich, aber nicht empfohlen (Ressourcenkonflikte) ║${RESET}" + echo -e "${YELLOW}║ • cgroup v2 kann Probleme verursachen ║${RESET}" + echo -e "${YELLOW}║ ║${RESET}" + echo -e "${BOLD}${YELLOW}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + ask_yes_no "Trotzdem fortfahren?" "yes" || { info "Abgebrochen."; exit 0; } + ;; + virtualmin) + echo "" + echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${CYAN}║ ℹ VIRTUALMIN UMGEBUNG ERKANNT ℹ ║${RESET}" + echo -e "${BOLD}${CYAN}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${CYAN}║ ║${RESET}" + echo -e "${CYAN}║ Virtualmin (Webmin) wurde erkannt. Hinweise: ║${RESET}" + echo -e "${CYAN}║ ║${RESET}" + echo -e "${CYAN}║ • Docker läuft problemlos neben Virtualmin ║${RESET}" + echo -e "${CYAN}║ • Port-Konflikte prüfen: 80, 443, 8080 könnten belegt sein ║${RESET}" + echo -e "${CYAN}║ • Firewall-Regeln (ufw/iptables) können Docker-Netz ║${RESET}" + echo -e "${CYAN}║ beeinflussen → ggf. 'DOCKER-USER' Chain beachten ║${RESET}" + echo -e "${CYAN}║ • Portainer auf Port 9443 (HTTPS) oder 9000 (HTTP) ║${RESET}" + echo -e "${CYAN}║ ║${RESET}" + echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + ;; + plesk) + echo "" + echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${CYAN}║ ℹ PLESK UMGEBUNG ERKANNT ℹ ║${RESET}" + echo -e "${BOLD}${CYAN}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${CYAN}║ ║${RESET}" + echo -e "${CYAN}║ Plesk wurde erkannt. Hinweise: ║${RESET}" + echo -e "${CYAN}║ • Plesk hat eine eigene Docker-Erweiterung ║${RESET}" + echo -e "${CYAN}║ • Manuelle Installation möglich, aber Konflikte prüfen ║${RESET}" + echo -e "${CYAN}║ • Port-Konflikte möglich: 80, 443, 8443, 8880 ║${RESET}" + echo -e "${CYAN}║ ║${RESET}" + echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + ;; + openvz) + echo "" + echo -e "${BOLD}${RED}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${RED}║ ✖ OPENVZ/VIRTUOZZO ERKANNT ✖ ║${RESET}" + echo -e "${BOLD}${RED}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${RED}║ OpenVZ/Virtuozzo-Container werden erkannt. ║${RESET}" + echo -e "${RED}║ Docker ist in OpenVZ-Containern NICHT unterstützt, ║${RESET}" + echo -e "${RED}║ da der Host-Kernel geteilt wird und die nötigen ║${RESET}" + echo -e "${RED}║ Kernel-Features (namespaces, cgroups) fehlen. ║${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${RED}║ Empfehlung: KVM/QEMU-VM oder Bare-Metal verwenden. ║${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${BOLD}${RED}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + ask_yes_no "Trotzdem versuchen? (wird wahrscheinlich fehlschlagen)" "no" || { info "Abgebrochen."; exit 0; } + ;; + docker) + warn "Docker-in-Docker erkannt. Funktioniert, aber nicht empfohlen für Produktion." + ;; + esac +} + +# ─── Systemvoraussetzungen prüfen ──────────────────────────────────────────── + +check_prerequisites() { + print_section "Systemprüfung" + + # Architektur prüfen + local arch + arch=$(uname -m) + case "$arch" in + x86_64|aarch64|armv7l|s390x) success "Architektur unterstützt: ${arch}" ;; + *) die "Nicht unterstützte Architektur: ${arch}" ;; + esac + + # Kernel-Version prüfen (mindestens 3.10 für Docker) + local kernel + kernel=$(uname -r) + local kernel_major + kernel_major=$(echo "$kernel" | cut -d. -f1) + local kernel_minor + kernel_minor=$(echo "$kernel" | cut -d. -f2) + + if (( kernel_major > 4 )) || (( kernel_major == 4 && kernel_minor >= 0 )) || \ + (( kernel_major == 3 && kernel_minor >= 10 )); then + success "Kernel-Version ausreichend: ${kernel}" + else + warn "Kernel ${kernel} ist sehr alt. Docker benötigt mindestens 3.10." + fi + + # Internetverbindung testen + if curl -fsS --max-time 10 https://download.docker.com/linux/ > /dev/null 2>&1; then + success "Internetverbindung zu download.docker.com vorhanden" + else + die "Keine Verbindung zu download.docker.com. Bitte Netzwerk und Firewall prüfen." + fi + + # Systemd prüfen + if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then + success "Systemd verfügbar" + else + warn "Systemd nicht verfügbar oder nicht aktiv. Docker-Autostart ggf. manuell konfigurieren." + fi + + # Freier Speicherplatz (mindestens 2 GB) + local free_gb + free_gb=$(df -BG / | awk 'NR==2 {gsub("G",""); print $4}') + if (( free_gb >= 2 )); then + success "Freier Speicherplatz: ${free_gb}GB" + else + warn "Weniger als 2GB freier Speicherplatz (${free_gb}GB). Installation möglicherweise fehlerhaft." + fi +} + +# ─── Abhängigkeiten installieren ───────────────────────────────────────────── + +install_dependencies() { + print_section "Abhängigkeiten installieren" + + step "Paketliste aktualisieren" + apt-get update -qq + success "Paketliste aktualisiert" + + step "Benötigte Pakete installieren" + apt-get install -y -qq \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + apt-transport-https \ + software-properties-common \ + >/dev/null 2>&1 + success "Abhängigkeiten installiert" +} + +# ─── Docker Repository einrichten ──────────────────────────────────────────── + +setup_docker_repo() { + print_section "Docker Repository einrichten" + + step "Docker GPG-Schlüssel herunterladen" + install -m 0755 -d /etc/apt/keyrings + curl -fsSL "https://download.docker.com/linux/${DETECTED_OS}/gpg" \ + -o "$DOCKER_GPG_KEY" + chmod a+r "$DOCKER_GPG_KEY" + success "GPG-Schlüssel installiert: ${DOCKER_GPG_KEY}" + + step "Docker Repository hinzufügen" + # Zeitlos: Immer den aktuellen Codename verwenden + local codename + codename=$(. /etc/os-release && echo "${VERSION_CODENAME}") + + # Fallback falls kein Codename verfügbar (z.B. Rolling-Release-Derivate) + if [[ -z "$codename" ]]; then + codename=$(lsb_release -cs 2>/dev/null || echo "stable") + warn "Kein Versionsname gefunden, verwende: ${codename}" + fi + + local arch + arch=$(dpkg --print-architecture) + + echo "deb [arch=${arch} signed-by=${DOCKER_GPG_KEY}] \ +https://download.docker.com/linux/${DETECTED_OS} ${codename} stable" \ + > "$DOCKER_REPO_FILE" + + success "Docker Repository konfiguriert (${DETECTED_OS}/${codename}/${arch})" + log "Docker Repo: ${DETECTED_OS} ${codename} ${arch}" +} + +# ─── Docker installieren ───────────────────────────────────────────────────── + +install_docker() { + print_section "Docker installieren" + + step "Paketliste aktualisieren" + apt-get update -qq + + step "Docker Engine, CLI, Containerd und Compose-Plugin installieren" + apt-get install -y -qq \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin \ + docker-compose-plugin \ + >/dev/null 2>&1 + success "Docker Engine installiert" + + step "Docker-Dienst aktivieren und starten" + systemctl enable docker >/dev/null 2>&1 + systemctl start docker + + # Kurz warten bis Docker bereit ist + local retries=10 + while ! docker info &>/dev/null 2>&1 && (( retries-- > 0 )); do + sleep 1 + done + + if docker info &>/dev/null 2>&1; then + success "Docker-Daemon läuft" + else + die "Docker-Daemon konnte nicht gestartet werden. Bitte 'journalctl -xe' prüfen." + fi + + # Versionen ausgeben + local docker_version + docker_version=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',') + local compose_version + compose_version=$(docker compose version 2>/dev/null | awk '{print $NF}') + + success "Docker Version: ${docker_version}" + success "Docker Compose Version: ${compose_version}" + log "Installed: Docker ${docker_version}, Compose ${compose_version}" + + # Aktuellen Benutzer zur docker-Gruppe hinzufügen + if [[ -n "${SUDO_USER:-}" ]] && id "$SUDO_USER" &>/dev/null; then + usermod -aG docker "$SUDO_USER" + success "Benutzer '${SUDO_USER}' zur Gruppe 'docker' hinzugefügt" + warn "Bitte ab- und wieder anmelden oder 'newgrp docker' ausführen, damit die Gruppe aktiv wird." + fi +} + +# ─── Docker funktionstest ──────────────────────────────────────────────────── + +test_docker() { + print_section "Docker testen" + step "Hello-World Container starten" + if docker run --rm hello-world >/dev/null 2>&1; then + success "Docker-Test erfolgreich! hello-world Container läuft." + else + warn "hello-world Test fehlgeschlagen. Docker ist möglicherweise noch nicht vollständig bereit." + fi + # Temporäres Image aufräumen + docker rmi hello-world >/dev/null 2>&1 || true +} + +# ─── Portainer installieren ────────────────────────────────────────────────── + +install_portainer() { + print_section "Portainer installieren" + + step "Portainer-Volume erstellen" + docker volume create "${PORTAINER_VOLUME}" >/dev/null 2>&1 + success "Volume '${PORTAINER_VOLUME}' erstellt" + + step "Portainer CE Container starten" + docker run -d \ + --name "${PORTAINER_CONTAINER}" \ + --restart=always \ + -p 8000:8000 \ + -p 9443:9443 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "${PORTAINER_VOLUME}:/data" \ + portainer/portainer-ce:latest \ + >/dev/null 2>&1 + + # Warten bis Portainer erreichbar + local retries=20 + local started=false + while (( retries-- > 0 )); do + if curl -fsk https://localhost:9443 >/dev/null 2>&1; then + started=true + break + fi + sleep 2 + done + + if $started; then + success "Portainer läuft!" + else + warn "Portainer wurde gestartet, ist aber noch nicht über HTTPS erreichbar." + warn "Bitte einige Sekunden warten und dann https://SERVER-IP:9443 aufrufen." + fi + + local server_ip + server_ip=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "SERVER-IP") + + echo "" + echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${GREEN}║ 🎉 Portainer erfolgreich installiert! ║${RESET}" + echo -e "${BOLD}${GREEN}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${GREEN}║ Portainer Web-UI (HTTPS): ║${RESET}" + echo -e "${GREEN}║ ${BOLD}https://${server_ip}:9443${RESET}${GREEN} ║${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${GREEN}║ Beim ersten Aufruf muss ein Admin-Passwort gesetzt werden. ║${RESET}" + echo -e "${GREEN}║ Bitte innerhalb von 5 Minuten nach Start aufrufen! ║${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + log "Portainer installed: https://${server_ip}:9443" +} + +# ─── Installation abschließen ──────────────────────────────────────────────── + +finish_install() { + echo "" + echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${GREEN}║ 🐳 Installation abgeschlossen! ║${RESET}" + echo -e "${BOLD}${GREEN}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${GREEN}║ ║${RESET}" + + local docker_ver compose_ver + docker_ver=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',') + compose_ver=$(docker compose version 2>/dev/null | awk '{print $NF}') + + echo -e "${GREEN}║ ${OK} Docker Engine: ${BOLD}${docker_ver}${RESET}${GREEN} ║${RESET}" + echo -e "${GREEN}║ ${OK} Docker Compose: ${BOLD}${compose_ver}${RESET}${GREEN} ║${RESET}" + + if [[ "$INSTALL_PORTAINER" == "yes" ]] && docker ps --filter "name=${PORTAINER_CONTAINER}" --format "{{.Names}}" | grep -q "^${PORTAINER_CONTAINER}$" 2>/dev/null; then + echo -e "${GREEN}║ ${OK} Portainer CE: ${BOLD}läuft${RESET}${GREEN} ║${RESET}" + fi + + echo -e "${GREEN}║ ║${RESET}" + echo -e "${GREEN}║ Log-Datei: /var/log/docker-setup.log ║${RESET}" + echo -e "${GREEN}║ Deinstallieren: sudo bash $(basename "$0") --remove ║${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" +} + +# ─── DEINSTALLATION ────────────────────────────────────────────────────────── + +do_remove() { + print_section "Docker vollständig entfernen" + + echo "" + echo -e "${BOLD}${RED}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${RED}║ ⚠ ACHTUNG: DEINSTALLATION ⚠ ║${RESET}" + echo -e "${BOLD}${RED}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${RED}║ Folgendes wird UNWIDERRUFLICH gelöscht: ║${RESET}" + echo -e "${RED}║ • Docker Engine, CLI, Containerd, Compose ║${RESET}" + echo -e "${RED}║ • Alle Docker-Images, Container, Volumes, Netzwerke ║${RESET}" + echo -e "${RED}║ • Portainer (inkl. Konfiguration und Volumes) ║${RESET}" + echo -e "${RED}║ • Docker Repository und GPG-Schlüssel ║${RESET}" + echo -e "${RED}║ • /var/lib/docker (ALLE Docker-Daten!) ║${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${RED}║ Nicht gelöscht: ║${RESET}" + echo -e "${RED}║ • Daten außerhalb von Docker (Host-Bind-Mounts) ║${RESET}" + echo -e "${RED}║ ║${RESET}" + echo -e "${BOLD}${RED}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + + if ! ask_yes_no "Bist du SICHER, dass alles gelöscht werden soll?" "no"; then + info "Deinstallation abgebrochen." + exit 0 + fi + + # Nochmals bestätigen + echo -en " ${ARROW} ${BOLD}${RED}Zur Bestätigung 'LÖSCHEN' eingeben:${RESET} " + local confirm + read -r confirm + if [[ "$confirm" != "LÖSCHEN" ]]; then + info "Bestätigung fehlgeschlagen. Abgebrochen." + exit 0 + fi + + print_section "Portainer entfernen" + if docker ps -a --format "{{.Names}}" 2>/dev/null | grep -q "^${PORTAINER_CONTAINER}$"; then + step "Portainer Container stoppen und entfernen" + docker stop "${PORTAINER_CONTAINER}" >/dev/null 2>&1 || true + docker rm "${PORTAINER_CONTAINER}" >/dev/null 2>&1 || true + success "Portainer Container entfernt" + fi + + if docker volume ls --format "{{.Name}}" 2>/dev/null | grep -q "^${PORTAINER_VOLUME}$"; then + step "Portainer Volume löschen" + docker volume rm "${PORTAINER_VOLUME}" >/dev/null 2>&1 || true + success "Portainer Volume gelöscht" + fi + + print_section "Alle Docker-Ressourcen bereinigen" + if command -v docker &>/dev/null && docker info &>/dev/null 2>&1; then + step "Alle Container stoppen" + local containers + containers=$(docker ps -aq 2>/dev/null) + if [[ -n "$containers" ]]; then + echo "$containers" | xargs docker stop 2>/dev/null || true + echo "$containers" | xargs docker rm -f 2>/dev/null || true + success "Container entfernt" + else + info "Keine Container vorhanden" + fi + + step "Alle Images löschen" + local images + images=$(docker images -aq 2>/dev/null) + if [[ -n "$images" ]]; then + echo "$images" | xargs docker rmi -f 2>/dev/null || true + success "Images gelöscht" + else + info "Keine Images vorhanden" + fi + + step "Alle Volumes löschen" + local volumes + volumes=$(docker volume ls -q 2>/dev/null) + if [[ -n "$volumes" ]]; then + echo "$volumes" | xargs docker volume rm -f 2>/dev/null || true + success "Volumes gelöscht" + else + info "Keine Volumes vorhanden" + fi + + step "Alle Netzwerke löschen (außer Standard)" + docker network prune -f >/dev/null 2>&1 || true + success "Netzwerke bereinigt" + fi + + print_section "Docker-Dienst stoppen" + step "Docker-Dienste deaktivieren" + systemctl stop docker docker.socket containerd 2>/dev/null || true + systemctl disable docker docker.socket containerd 2>/dev/null || true + success "Docker-Dienste gestoppt und deaktiviert" + + print_section "Docker-Pakete entfernen" + step "Docker-Pakete deinstallieren" + apt-get purge -y -qq \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin \ + docker-compose-plugin \ + docker-compose \ + docker.io \ + docker-doc \ + podman-docker \ + 2>/dev/null || true + apt-get autoremove -y -qq 2>/dev/null || true + success "Docker-Pakete entfernt" + + print_section "Docker-Daten und Konfiguration löschen" + + step "Docker-Datenverzeichnis löschen" + rm -rf /var/lib/docker 2>/dev/null || true + rm -rf /var/lib/containerd 2>/dev/null || true + success "/var/lib/docker und /var/lib/containerd gelöscht" + + step "Docker-Konfiguration löschen" + rm -rf /etc/docker 2>/dev/null || true + success "/etc/docker gelöscht" + + step "Repository und GPG-Schlüssel entfernen" + rm -f "$DOCKER_REPO_FILE" 2>/dev/null || true + rm -f "$DOCKER_GPG_KEY" 2>/dev/null || true + + # Alte Repo-Formate aufräumen + rm -f /etc/apt/sources.list.d/docker*.list 2>/dev/null || true + rm -f /etc/apt/trusted.gpg.d/docker*.gpg 2>/dev/null || true + rm -f /usr/share/keyrings/docker*.gpg 2>/dev/null || true + success "Repository-Dateien entfernt" + + step "Paketliste aktualisieren" + apt-get update -qq 2>/dev/null || true + success "Paketliste aktualisiert" + + step "Benutzergruppe bereinigen" + if getent group docker &>/dev/null; then + groupdel docker 2>/dev/null || true + success "docker-Gruppe entfernt" + fi + + # Systemd-Unit-Dateien bereinigen + rm -f /etc/systemd/system/docker*.service 2>/dev/null || true + systemctl daemon-reload 2>/dev/null || true + + echo "" + echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════════════╗${RESET}" + echo -e "${BOLD}${GREEN}║ 🗑️ Deinstallation abgeschlossen! ║${RESET}" + echo -e "${BOLD}${GREEN}╠══════════════════════════════════════════════════════════════╣${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${GREEN}║ Docker, Compose und Portainer wurden vollständig entfernt. ║${RESET}" + echo -e "${GREEN}║ Das System ist wieder im ursprünglichen Zustand. ║${RESET}" + echo -e "${GREEN}║ ║${RESET}" + echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════════════╝${RESET}" + echo "" + log "Uninstallation complete." +} + +# ─── Hauptprogramm ─────────────────────────────────────────────────────────── + +main() { + # Argumente verarbeiten + case "${1:-}" in + --remove|-r|remove) ACTION="remove" ;; + --help|-h|help) print_banner; print_help; exit 0 ;; + "") ACTION="install" ;; + *) die "Unbekannte Option: $1. Verwende --help für Hilfe." ;; + esac + + print_banner + check_root + + # Log-Datei initialisieren + touch "$LOG_FILE" 2>/dev/null || true + log "=== docker-setup.sh v${SCRIPT_VERSION} START === ACTION: ${ACTION}" + + # Betriebssystem erkennen + detect_os + + if [[ "$ACTION" == "remove" ]]; then + do_remove + log "=== END ===" + exit 0 + fi + + # Umgebung erkennen und Hinweise anzeigen + detect_environment + show_environment_info + + # ── Interaktive Konfiguration ── + print_section "Installationskonfiguration" + + echo "" + info "Folgende Komponenten werden installiert:" + echo -e " ${BOLD}• Docker Engine${RESET} (neueste stabile Version)" + echo -e " ${BOLD}• Docker Compose Plugin${RESET} (als 'docker compose' Befehl)" + echo -e " ${BOLD}• Docker Buildx Plugin${RESET}" + echo "" + + if ask_yes_no "Soll Portainer CE (Web-UI für Docker) ebenfalls installiert werden?" "yes"; then + INSTALL_PORTAINER="yes" + info "Portainer CE wird installiert (Port 9443 HTTPS, Port 8000 Edge Agent)" + else + INSTALL_PORTAINER="no" + info "Portainer wird übersprungen" + fi + + # ── Zusammenfassung ── + print_section "Installationsübersicht" + echo "" + echo -e " ${BOLD}System:${RESET} ${DETECTED_OS} (${DETECTED_VERSION})" + echo -e " ${BOLD}Umgebung:${RESET} ${DETECTED_ENV}" + echo -e " ${BOLD}Docker:${RESET} wird installiert (neueste Version)" + echo -e " ${BOLD}Compose:${RESET} wird installiert (Plugin)" + echo -e " ${BOLD}Portainer:${RESET} $([ "$INSTALL_PORTAINER" == "yes" ] && echo "wird installiert" || echo "übersprungen")" + echo -e " ${BOLD}Log:${RESET} ${LOG_FILE}" + echo "" + + if ! ask_yes_no "Installation jetzt starten?" "yes"; then + info "Abgebrochen." + exit 0 + fi + + # ── Installation durchführen ── + install_dependencies + setup_docker_repo + install_docker + test_docker + + if [[ "$INSTALL_PORTAINER" == "yes" ]]; then + install_portainer + fi + + finish_install + log "=== docker-setup.sh END === SUCCESS" +} + +# ─── Skript starten ────────────────────────────────────────────────────────── +main "$@" \ No newline at end of file