Files
2026-05-26 16:30:54 +02:00

786 lines
38 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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
# ─── 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'
OK="${GREEN}${RESET}"
WARN="${YELLOW}${RESET}"
ERR="${RED}${RESET}"
INFO="${CYAN}${RESET}"
ARROW="${BLUE}${RESET}"
# ─── 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=""
# ─── Hilfsfunktionen ─────────────────────────────────────────────────────────
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true
}
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 ""
}
print_section() {
echo ""
echo -e "${BOLD}${CYAN}── $1 ${DIM}─────────────────────────────────────────────────────${RESET}"
}
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; }
# Fortschrittsanzeige
step() {
echo -e "\n ${ARROW} ${BOLD}$*${RESET}"
log "STEP: $*"
}
# Ja/Nein-Abfrage mit konfigurierbarem Standard
ask_yes_no() {
local prompt="$1"
local default="${2:-yes}"
local answer
if [[ "$default" == "yes" ]]; then
local hint="[J/n]"
else
local hint="[j/N]"
fi
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
}
# ─── Root-Prüfung ────────────────────────────────────────────────────────────
check_root() {
if [[ $EUID -ne 0 ]]; then
die "Dieses Skript muss als root ausgeführt werden. Bitte 'sudo bash $0' verwenden."
fi
}
# ─── Hilfe ───────────────────────────────────────────────────────────────────
print_help() {
cat <<EOF
${BOLD}Verwendung:${RESET}
sudo bash $(basename "$0") [OPTION]
${BOLD}Optionen:${RESET}
(keine) Interaktive Installation von Docker, Docker Compose und optional Portainer
--remove Vollständige Deinstallation aller installierten Komponenten
--help Diese Hilfe anzeigen
${BOLD}Beispiele:${RESET}
sudo bash $(basename "$0")
sudo bash $(basename "$0") --remove
EOF
}
# ─── Betriebssystem erkennen ─────────────────────────────────────────────────
detect_os() {
if [[ ! -f /etc/os-release ]]; then
die "Kann Betriebssystem nicht erkennen /etc/os-release fehlt."
fi
# shellcheck source=/dev/null
source /etc/os-release
DETECTED_OS="${ID:-unknown}"
DETECTED_VERSION="${VERSION_CODENAME:-${VERSION_ID:-unknown}}"
local os_pretty="${PRETTY_NAME:-$ID $VERSION_ID}"
# Prüfen ob Debian-basiert
local supported=false
local base_id="${ID_LIKE:-$ID}"
for id in $ID $base_id; do
case "$id" in
debian|ubuntu|raspbian|linuxmint|pop|elementary|kali|parrot|mx|zorin|deepin|neon)
supported=true
break
;;
esac
done
if ! $supported; then
die "Nicht unterstütztes Betriebssystem: ${os_pretty}. Dieses Skript unterstützt nur Debian-basierte Systeme."
fi
# Für Ubuntu-Derivate den Ubuntu-Codename ermitteln
if [[ "$ID" != "debian" && "$ID" != "ubuntu" ]]; then
if grep -qi ubuntu /etc/os-release 2>/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 "$@"